Skip to content

Every major version, and what it meant

One row per major boundary, oldest recorded first. Read the rows between where you are and where you are going — that set, in order, is your upgrade.

Each row says what broke and what you do about it. Where a release left no record, this page says so rather than guessing.

Find your current version and your target, and read every row between them. The Touches column is what decides how much work a jump is: compile-time breaks accumulate harmlessly, stored state does not.

Boundary Touches In one line
6 → 7 Stored state Inbox observer keys changed; stored state needs rewriting
7 → 8 Compile-time, transport Namespaces renamed wholesale; client moved off Orleans to REST
8 → 9 Compile-time Startup and configuration only; wire-compatible both directions
9 → 10 Unknown No release notes exist for this major
10 → 11 Stored state Job state enum values changed; in-flight jobs unreliable
11 → 12 Compile-time Package renamed; internal APIs hidden; CDC API removed
12 → 13 Compile-time GetEventStore() became async
13 → 14 Stored state, compile-time Read-model formalization; collections and definitions moved
14 → 15 Compile-time, config Rules retired; Content type changed; TLS required in production
15 → 16 Config, endpoints One port 35000, always TLS; management port removed
16 → 17 Wire Every gRPC service regenerated; method names and envelopes changed
17 → 18 Wire Append Content back to a plain JSON string
18 → 19 Compile-time One .NET parameter bool → bool?; rebuild
19 → 20 Cluster Kernel silos of 19 and 20 cannot share a cluster; upgrade every silo together
  • Compile-time — it breaks the build and you fix it once, against the version you land on.
  • Stored state — it concerns data already written. Ordered: skipping past it does not skip it.
  • Wire — kernel and clients must move together across it.
  • Cluster — kernel silos of the two majors cannot run side by side, so the whole kernel moves at once.
  • Config, endpoints — deployment and configuration change, not your code.

Stored observer state — released 2023-01-10.

The key for Inbox observers changed to include the source microservice id, so existing stored observer state for inboxes is not read back correctly. The release notes carry the remediation: rewrite the _id of the affected document in the observers collection to <event sequence id> : <observer id> : <source microservice id>.

You do: migrate that stored state before starting 7.x, or accept that inbox observers restart from their default position.

Namespaces, transport and several APIs — released 2023-01-23.

The largest boundary in this list. The client stopped using an Orleans client to reach the kernel and moved to a REST-based approach. Namespaces were renamed wholesale (Aksio.Cratis.Events.Store → Aksio.Cratis.Events, …Events.Projections → …Projections, …Events.Observation → …Observation, event sequences to …EventSequences, …Events.Schemas → …Schemas). CommandResult and QueryResult moved to Fundamentals. ITenants began returning a Tenant object rather than an id. IImmediateProjections began returning ImmediateProjectionResult, so reads became .Model. DefineState on RuleFor became abstract and required.

You do: expect compile errors across the codebase and work through them; update cratis.json / cluster.json, where single-cluster mode changed from local to single.

Repository split and startup — released 2023-07-19.

Non-Chronicle code moved out into separate Fundamentals, ApplicationModel and MongoDB repositories. From a usage perspective only startup and configuration changed: .UseCratis() became a fluent configuration surface, and an ASP.NET Core application using a Startup class must call .UseCratis() on IApplicationBuilder.

Notable: this boundary was explicitly wire-compatible both directions — pre-9 clients could talk to a 9 kernel and vice versa.

No recorded change — released 2024-07-30.

This release has no notes, and the tag sits on a dependency-update merge. No breaking change is recorded for it. Treat the major as unexplained rather than assuming it was deliberate, and test your own usage across the boundary.

Stored job state — released 2025-03-12.

JobState and JobStepState enum values changed, and the release explicitly does not guarantee backward compatibility for the persisted values.

You do: expect in-flight job state from 10.x to be unreliable. Drain jobs before upgrading if their completion matters.

Internalized APIs and a renamed package — released 2025-06-04.

Kernel APIs that were reachable through dependencies were internalized. The Cratis.Chronicle.Orleans.InProcess package became Cratis.Chronicle.InProcess. The client-side Change Data Capture API was removed.

You do: rename the package reference; stop using anything from Cratis.Chronicle.Contracts or Cratis.Chronicle.Infrastructure that you were reaching into.

One method became async — released 2025-06-23.

ChronicleClient.GetEventStore() is async, and now calls DiscoverAll() and RegisterAll() for you.

You do: await it. Opt out with AutoDiscoverAndRegister = false on ChronicleOptions or the skipDiscovery argument if you were managing discovery yourself.

Read-model formalization and stored layout — released 2025-08-15.

“Model” became ReadModel throughout. Projections and reducers now reference formalized read models rather than carrying their own definitions. The collection holding event types was renamed from schemas to event-types, projection definitions are stored differently, and observer definition and state were split because definitions are shared across namespaces while state is per namespace. Default naming for read models and properties became configurable rather than fixed.

You do: this one touches stored layout as well as APIs. Read the release notes in full, and go to 14.0.1, not 14.0.0.

Rules retired, TLS, content type — released 2026-02-03.

Rules were removed as a building block in favor of FluentValidation and standard ASP.NET Core mechanisms. AppendedEvent.Content in the .NET client became object — the actual deserialized type — instead of ExpandoObject. TLS became required for the kernel in production. [Passive] moved to the ReadModels namespace. The Cratis.Chronicle.XUnit package became Cratis.Chronicle.Testing, with namespaces to match.

You do: replace Rules usage; fix code that treated Content as ExpandoObject; configure TLS for production; rename the testing package reference.

One port, always TLS — released 2026-07-09.

The server consolidated all traffic onto port 35000 and that port always uses TLS, with a self-signed certificate generated automatically in development. The managementPort server option and client ManagementPort, the separate workbench.tls configuration, and the Aspire management endpoint were all removed. The Workbench moved to https://localhost:35000.

You do: remove management-port configuration, point tooling and bookmarks at the single TLS port, and configure a certificate for production.

The generated wire contract — released 2026-08-25.

Every gRPC service became generated from Arc [Command]/[ReadModel] artifacts in Core instead of being hand-written, completing a migration begun in 16.x: method names and payload envelopes changed across the surface. The kernel also gained a single server-side compatibility check that every client language uses.

You do: take a matching client. Go to 17.0.1 — 17.0.0 is the accidental release described above and carries a pre-migration contract.

Append content back to a string — released 2026-09-08.

Content on AppendRequest, EventToAppend, EventForEventSourceId and ReviseRequest changed from map<string, JsonNode> back to a plain JSON string, matching how event content is represented everywhere else on the wire.

You do: nothing, if you use a published client at a matching version. A client built directly against the raw gRPC contracts must send and receive Content as a string.

A .NET signature — released 2026-09-17.

One public .NET method parameter moved from bool to bool?. Nothing changed on the wire. See Upgrading from 18 to 19.

You do: rebuild.

Cratis Choreography — released 2026-11-04.

The kernel now builds on Cratis.Choreography, the renamed Cratis.Orleans. Grain interfaces that take a job or job step identifier carry the new types, so their wire identities changed, and silos of 19 and 20 cannot share a cluster. Stored job and job step state keeps its identifiers, so existing data still resolves. Clients and their wire contract are unaffected.

You do: stop every kernel silo of the cluster and start them on 20 together, rather than rolling pods one at a time across this boundary.

Nothing prevents jumping from, say, 14 to 19 directly. What that costs is the union of every row in between, so read them in order and collect the work before starting.

Two kinds of change behave differently when you skip:

  • Compile-time changes accumulate but do not interact. Renamed namespaces, moved types and changed signatures pile up without interacting; you fix them once against the version you land on.
  • Stored-state changes are ordered. 6 → 7 (observer keys), 10 → 11 (job state) and 13 → 14 (read-model and event-type layout) each concern data already written. Skipping past one does not skip its consequence.

Where you are crossing a stored-state boundary, take a backup you have restored from at least once before, and rehearse the upgrade against a copy.

Chronicle verifies that the current build still serves every released minor of the major it is on published before it. That guarantee stops at a major boundary — which is exactly what a major means. So a client and kernel differing only in minor are expected to interoperate, while a client and kernel on different majors are not, unless a boundary above explicitly says otherwise (8 → 9 did).

Upgrade the kernel and the clients together across a major boundary, rather than assuming a skew will be tolerated.

The TypeScript client, @cratis/chronicle, has its own version line (6.x at the time of writing), so its version does not follow the kernel’s major. What is known about the pairing:

  • 6.0.0 needs kernel 18.4.1 or later. It moved append routing to the kernel, so upgrade every kernel node to 18.4.1 or later before you upgrade the client.
  • 6.x checks compatibility on every connection. The client sends its contract descriptor to the kernel each time it connects. When the kernel reports an incompatibility, or has no such check, the client throws IncompatibleChronicleServer, does not retry, and rejects every later call. Deploy a compatible kernel and create a new client.
  • No compatibility matrix is published. Test the TypeScript client against the kernel version you run before relying on the pairing.