Skip to content

Get started

The fastest way to understand Chronicle is to watch one fact travel through it. Chronicle stores the fact as an event, projections fold it into read models, and reactors respond to it. That loop is the same no matter which client SDK you use.

This page is the shared Chronicle starting point. It explains the common model and shows the same examples in every supported client. Use the client-specific setup guides for package installation, project scaffolding, host integration, and runtime idioms.

Start with the client that matches the application you are building. The setup guide owns the language-specific details; the rest of this page owns the Chronicle concepts.

ClientSetup guide
.NET.NET client
KotlinKotlin client
JavaKotlin/JVM client
ElixirElixir client
TypeScriptTypeScript client

Chronicle application code connects to a running Chronicle kernel. For local development, the fastest path is the development image:

Terminal window
docker run -d -p 27017:27017 -p 35000:35000 cratis/chronicle:latest-development

The kernel listens on chronicle://localhost:35000, the workbench runs on https://localhost:35000 — the same, single TLS-secured port — and the bundled MongoDB stores materialized read models. If you need Docker Compose, Aspire, a separate database, or production-style hosting, use Choose an application host model.

One event-sourced interaction has three parts:

Append a fact

Event log

Projection

Read model

Reactor

Side effect

In event modeling notation, the tiny example below is:

UI/A: DemoC/RM: DemoStream: Demo
TestEvent



message: string
TestProjection
TestReactor

Each client has its own syntax, but the Chronicle shape is the same: create a client, choose an event store, and append a fact to the event log.

Program.cs
using var client = new ChronicleClient();
var eventStore = await client.GetEventStore("ChronicleConsole");
await eventStore.EventLog.Append("some-event-source", new TestEvent("Hello world!"));

Reading top to bottom: the client connects to the kernel, asks for an event store by name, and appends a TestEvent to its event log. That append is the only operation that changes the event store. Everything else reacts to the recorded fact.

The event itself is just a record marked as a fact:

The event - an immutable fact
[EventType]
public record TestEvent(string Message);

A projection folds that event into a read model: state you can query, rebuild, and store in a sink:

The projection - builds queryable state
[FromEvent<TestEvent>]
public record TestProjection(
string Message,
[SetFromContext<TestEvent>(nameof(EventContext.EventSourceId))] string EventSource);

A reactor does something when the event arrives:

The reactor - does something when it happens
public class TestReactor : IReactor
{
public Task React(TestEvent @event)
{
Console.WriteLine($"Received event with message: {@event.Message}");
return Task.CompletedTask;
}
}

Discovery and registration are client-specific. Some hosts discover annotated artifacts; others register modules, classes, or functions explicitly. The invariant is the same: events are facts, projections derive read models, and reactors observe events to produce side effects.

The development image includes the Chronicle workbench — a web UI for inspecting event stores. Open https://localhost:35000 and log in with the development credentials: username Admin, password ChangeMeNow!. In development the port uses a self-signed certificate, so accept your browser’s certificate warning the first time.

After you run one of the client setup guides or the example flow above, choose the event store your client used and open Sequences. The TestEvent is permanent and ordered in the event log. Append more events and watch the sequence grow. That log is the source of truth your projections and reactors read from.