---
title: Chronicle
editUrl: https://github.com/Cratis/Arc.TypeScript/edit/main/Documentation/chronicle/index.md
description: Return Chronicle events from Arc commands, serve projected read models from Arc queries, and let reactors return Arc commands, with the experimental @cratis/arc.chronicle integration.
---


A librarian registers an author. You want that registration kept as a fact, and you want the author list on screen to update from it. Without an integration, every command opens a Chronicle client, picks the event store namespace for the caller's tenant, appends inside `handle()`, turns constraint violations into validation results, and takes care never to append when validation has already failed.

`@cratis/arc.chronicle` removes that plumbing. A command **returns** the event, and Arc appends it after authorization, validation, and `provide()` have passed, in the namespace of the tenant Arc already resolved. Arc does not require event sourcing: a command can do its work through any service. Use this integration when you want commands to record facts in [Chronicle](/chronicle/), the event-sourcing database.

:::caution[Experimental]
`@cratis/arc.chronicle` is experimental, and, like every package in this repository, it is not published to npm. Its APIs can change. The [capability reference](/arc/backend/typescript/reference/capabilities/#persistence-and-chronicle) has its status and the checks behind it.
:::

To try it, [Add event sourcing](/arc/backend/typescript/chronicle/add-event-sourcing/) starts a Chronicle kernel on your machine and adds a first event-sourced slice to the application from [Create an application](/arc/backend/typescript/getting-started/create-an-application/).

## How the pieces fit

Arc and Chronicle meet at one loop. A command returns an event, Chronicle appends it and projects it into a read model, and an Arc query serves that read model back to the client.

```mermaid
flowchart LR
    UI[Client] -->|command| CMD["@command() class · handle()"]
    CMD -->|returns an event| EV[(Chronicle event log)]
    EV -->|projection| RM["@readModel() · @fromEvent"]
    RM -->|query| UI
    EV -->|reactor| RE["@reactor() · returns commands"]
    RE -->|command| CMD
```

The [Library sample](https://github.com/Cratis/Arc.TypeScript/tree/main/Samples/Library) walks that loop. Registration returns the event:

```typescript title="Features/Authors/Registration/Registration.ts (excerpt)"
@eventType()
export class AuthorRegistered {
    @field(AuthorName) name: AuthorName;
    constructor(name: AuthorName = new AuthorName('')) { this.name = name; }
}

@command()
@roles('Librarian')
export class RegisterAuthor {
    @key() @field(AuthorId) id!: AuthorId;
    @field(AuthorName) name!: AuthorName;

    handle(): AuthorRegistered { return new AuthorRegistered(this.name); }
}
```

The listing projects that event into a read model and serves it:

```typescript title="Features/Authors/Listing/Listing.ts (excerpt)"
@readModel()
@fromEvent(AuthorRegistered)
export class Author {
    @field(AuthorId) id!: AuthorId;
    @field(AuthorName) name!: AuthorName;

    @query({ observable: true }, service(ChronicleReadModels))
    static allAuthors(models: ChronicleReadModels): Observable<Author[]> {
        return models.observeAll(Author, author => author.id.toString());
    }
}
```

The `@key()` field names the event source, so the registration lands in that author's stream. `@fromEvent(AuthorRegistered)` asks Chronicle to copy the event's matching properties into `Author`, keyed by the event source. The observable query then pushes a new author list to the browser whenever the projection changes. Appending and projecting are separate steps inside Chronicle, so a successful command can return before the list has caught up.

## What the integration adds

- **Returned events are appended.** `handle()` returns one event, several, or events beside a response. See [Returning events](/arc/backend/typescript/chronicle/commands/).
- **Metadata comes from the command.** The event source, stream, subject, and causation are resolved from the command and the request. See [Event metadata](/arc/backend/typescript/chronicle/commands/event-metadata/).
- **Tenants map to namespaces.** Every append and read uses the tenant of the current execution as the Chronicle namespace.
- **Rejections become validation results.** A constraint or concurrency violation answers 400, and nothing is appended.
- **Nested commands share one batch.** Returned events from nested commands are appended together, or not at all. See [Transactional commands](/arc/backend/typescript/chronicle/commands/transactional-commands/).
- **Current state is a parameter.** A command can take its own read model or a rehydrated aggregate as a `handle()` argument. See [Read models in commands](/arc/backend/typescript/chronicle/read-models/injecting-into-commands/) and [Aggregates](/arc/backend/typescript/chronicle/aggregates/).
- **Reactors can return commands.** A Chronicle reactor returns an Arc command, and Arc runs it through the full command pipeline. See [Reactors](/arc/backend/typescript/chronicle/reactors/).

## Find your way

| Page | Use it when you want to |
| --- | --- |
| [Add event sourcing](/arc/backend/typescript/chronicle/add-event-sourcing/) | Start a local kernel, record a first event from a command, and query its projection |
| [Registration options](/arc/backend/typescript/chronicle/registration-options/) | Look up the `withChronicle` options, configuration keys, and client ownership |
| [The Cratis package](/arc/backend/typescript/chronicle/cratis-package/) | Register Arc and Chronicle with one call from `@cratis/cratis` |
| [Returning events](/arc/backend/typescript/chronicle/commands/) | Return one event, a batch, or events next to a response |
| [Event metadata](/arc/backend/typescript/chronicle/commands/event-metadata/) | See what each appended event carries and where every value comes from |
| [Resolving the event source ID](/arc/backend/typescript/chronicle/resolving-event-source-id/) | Choose which event source an event is appended to, and route it |
| [Subject](/arc/backend/typescript/chronicle/commands/subject/) | Record whose personal data an event carries |
| [Concurrency](/arc/backend/typescript/chronicle/commands/concurrency/) | Reject an append when the stream moved |
| [Causation and auditing](/arc/backend/typescript/chronicle/commands/causation/) | See what the permanent causation chain records, and keep secrets out |
| [Transactional commands](/arc/backend/typescript/chronicle/commands/transactional-commands/) | Understand the batch across nested commands and its failure rules |
| [Read models](/arc/backend/typescript/chronicle/read-models/) | Serve projected state from queries |
| [Read models in commands](/arc/backend/typescript/chronicle/read-models/injecting-into-commands/) | Decide or validate from the state projected for the command's key |
| [When read model resolution fails](/arc/backend/typescript/chronicle/read-models/failures/) | Understand a rejected command that loads a read model |
| [Aggregates](/arc/backend/typescript/chronicle/aggregates/) | Decide from one event source's full history |
| [Reactors](/arc/backend/typescript/chronicle/reactors/) | Run a follow-up command when an event is recorded |
| [Compliance](/arc/backend/typescript/chronicle/compliance/) | Know what the integration does, and does not do, for personal data |
| [Code analysis](/arc/backend/typescript/chronicle/code-analysis/) | See which .NET `ARCCHR` diagnostics apply in TypeScript |
| [Testing Chronicle commands](/arc/backend/typescript/testing/chronicle/) | Assert returned events without a kernel |
| [Testing against a kernel](/arc/backend/typescript/testing/chronicle-kernel/) | Check projections and constraints against a live Chronicle kernel |
| [Read consistency](/arc/backend/typescript/queries/read-consistency/) | Select passive reads or wait for observer completion after a write |

## Where it stops

- A command's events go to one event log in one event store. There is no transaction across other stores or external calls.
- An event appended directly through the SDK inside `handle()` is outside the command's batch.
- Read models you read through Chronicle arrive already decrypted. Arc releases encrypted personal data at its query edge only for a protected Chronicle read model read directly from MongoDB and returned by a query, either as an instance of its exact class or as a raw `MongoReadModels` document typed with `readModel`. Untyped raw documents, derived subtypes, and mapped objects are served as stored unless you call `readModels.release` yourself. See [Compliance](/arc/backend/typescript/chronicle/compliance/).
- SDK 6.9.0 and later replay reactors by default. Mark non-replayable effects with `@onceOnly()`, but keep returned commands safe for failed-partition re-delivery. See [Reactors](/arc/backend/typescript/chronicle/reactors/).
- No `ARCCHR` analyzers exist for TypeScript. See [Code analysis](/arc/backend/typescript/chronicle/code-analysis/).

Start with [Add event sourcing](/arc/backend/typescript/chronicle/add-event-sourcing/), or run the [Library sample](/arc/backend/typescript/getting-started/library-sample/) to see a finished application.

## Related

- [Chronicle TypeScript client](https://github.com/Cratis/Chronicle.TypeScript), where the SDK is developed
- [CQRS without event sourcing](/arc/arc-without-event-sourcing/)
- [Capability reference](/arc/backend/typescript/reference/capabilities/#persistence-and-chronicle)
