---
title: Correlation, identity, and causation
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/concepts/correlation-identity-causation.mdx
description: The three pieces of context Chronicle carries with every event — who caused it, what operation it belongs to, and why it happened.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';


Every event Chronicle appends carries more than its own data. Three pieces of context travel alongside it, scoped to the current call and captured automatically on every append:

- **Correlation ID** — ties together every event, command, and side effect that belong to the same logical operation (a single HTTP request, a message-bus handler invocation).
- **Identity** — records *who* caused the change: a user, a system, or a service.
- **Causation** — records *why*: the chain of operations, from a root action down to the specific command that triggered this event.

All three are set once at the start of an operation (a request, a background job) and flow into every event appended during it, without threading them through every function call.

## Correlation ID

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Execution;

public class CorrelationIdentityCausationCorrelation(ICorrelationIdAccessor accessor, ICorrelationIdModifier modifier)
{
    public CorrelationId GetCurrent() => accessor.Current;

    public void SetForRequest() => modifier.Modify(CorrelationId.New());
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/correlation.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.correlation.correlationIdManager
import java.util.UUID

// Per thread, not per request or coroutine; clear in finally on thread-pooled paths.
class CorrelationIdentityCausationCorrelation {
    fun getCurrent(): UUID = correlationIdManager.current

    fun setForRequest() = correlationIdManager.set(UUID.randomUUID())
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/correlation.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.correlation.CorrelationIdManagerKt;
import java.util.UUID;

// Per thread, not per request or coroutine; clear in finally on thread-pooled paths.
class CorrelationIdentityCausationCorrelation {
    UUID getCurrent() {
        return CorrelationIdManagerKt.getCorrelationIdManager().getCurrent();
    }

    void setForRequest() {
        CorrelationIdManagerKt.getCorrelationIdManager().set(UUID.randomUUID());
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/concepts/correlation-identity-causation/correlation.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.CorrelationIdentityCausationCorrelation do
  alias Chronicle.Correlation.CorrelationId

  def get_current do
    Chronicle.current_correlation_id()
  end

  def set_for_request do
    Chronicle.set_correlation_id(CorrelationId.create())
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/correlation.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { correlationIdManager, CorrelationId } from '@cratis/chronicle';

class CorrelationIdentityCausationCorrelation {
    getCurrent(): CorrelationId {
        return correlationIdManager.current;
    }

    setForRequest(): void {
        correlationIdManager.setCurrent(CorrelationId.create());
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/correlation.md)

</TabItem>
</Tabs>

Kotlin's correlation manager scopes the id per-thread (`ThreadLocal`); Elixir and TypeScript scope it per logical call context (Elixir's process dictionary, TypeScript's `AsyncLocalStorage`) — in both cases automatically, without extra code, as long as you don't spawn new threads/processes/async contexts without carrying the value across explicitly.

## Identity

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Identities;

public class CorrelationIdentityCausationIdentity(IIdentityProvider identityProvider)
{
    public void SetForRequest(string subject, string name, string userName) =>
        identityProvider.SetCurrentIdentity(new Identity(subject, name, userName));

    public Identity GetCurrent() => identityProvider.GetCurrent();
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/identity.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.identity.Identity
import io.cratis.chronicle.identity.identityProvider

// Per thread, not per request or coroutine; clear in finally on thread-pooled paths.
class CorrelationIdentityCausationIdentity {
    fun setForRequest(subject: String, name: String, userName: String) {
        identityProvider.setCurrentIdentity(Identity(subject, name, userName))
    }

    fun getCurrent(): Identity = identityProvider.currentIdentity
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/identity.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.identity.Identity;
import io.cratis.chronicle.identity.IdentityProviderKt;

// Per thread, not per request or coroutine; clear in finally on thread-pooled paths.
class CorrelationIdentityCausationIdentity {
    void setForRequest(String subject, String name, String userName) {
        IdentityProviderKt.getIdentityProvider().setCurrentIdentity(new Identity(subject, name, userName, null));
    }

    Identity getCurrent() {
        return IdentityProviderKt.getIdentityProvider().getCurrentIdentity();
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/concepts/correlation-identity-causation/identity.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.CorrelationIdentityCausationIdentity do
  alias Chronicle.Identity

  def set_for_request(subject, name, user_name) do
    Chronicle.set_identity(Identity.new(subject, name, user_name))
  end

  def get_current do
    Chronicle.current_identity()
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/identity.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { identityProvider, Identity } from '@cratis/chronicle';

class CorrelationIdentityCausationIdentity {
    setForRequest(subject: string, name: string, userName: string): void {
        identityProvider.setCurrentIdentity(new Identity(subject, name, userName));
    }

    getCurrent(): Identity {
        return identityProvider.getCurrent();
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/identity.md)

</TabItem>
</Tabs>

Every client provides the same three sentinel identities for well-known cases — `System`, `NotSet`, and `Unknown` — and supports on-behalf-of delegation chains (an identity that acted *for* another identity) via a `withoutDuplicates()`/`without_duplicates/1` helper that collapses repeated subjects in a long chain.

### Renaming an identity

The name recorded alongside an identity is a display name, and people's names change. Renaming updates it in
one place for every event that identity has ever caused — the events themselves are untouched, since the name
is not part of the fact they record.

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle;

public static class RenamingAnIdentity
{
    public static async Task Rename(IEventStore eventStore)
    {
        await eventStore.Identities.Rename("subject-42", "Jane Austen");
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/renaming-an-identity.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.IEventStore

class CorrelationIdentityCausationRenamingAnIdentity {
    suspend fun rename(eventStore: IEventStore) {
        eventStore.identities.rename("subject-42", "Jane Austen")
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/renaming-an-identity.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.IEventStore;

import io.cratis.chronicle.java.IdentityManagerServiceJavaBridge;

class CorrelationIdentityCausationRenamingAnIdentity {
    void rename(IEventStore eventStore) {
        IdentityManagerServiceJavaBridge.rename(eventStore.getIdentities(), "subject-42", "Jane Austen");
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/concepts/correlation-identity-causation/renaming-an-identity.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.CorrelationIdentityCausationRenamingAnIdentity do
  alias Chronicle.Identities

  def rename do
    Identities.rename("subject-42", "Jane Austen")
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/renaming-an-identity.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { IEventStore } from '@cratis/chronicle';

async function renameAnIdentity(eventStore: IEventStore): Promise<void> {
    await eventStore.identities.rename('subject-42', 'Jane Austen');
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/renaming-an-identity.md)

</TabItem>
</Tabs>

The subject is the identity's stable key. It is what events are recorded against, so it does not change.

### Identity resolution on metadata reads

[Metadata reads](/chronicle/events/getting-events/#read-metadata-by-sequence-locator) resolve caused-by
identities directly from storage rather than a silo's identity cache. A rename is visible on
the next metadata read, including for events appended before the rename. Each chain link has
a `Resolution` status:

| Status | Meaning | Name |
| --- | --- | --- |
| `Resolved` | The identity exists and has a display name. | The current name. |
| `NameUnavailable` | The identity exists but its display name is empty. | `null`. |
| `Missing` | The stored identity identifier no longer resolves. | `null`. |
| `NotSet` | No caused-by identity was set. | `null`. |
| `System` | The subject is Chronicle's well-known system identity. | `null`. |

Chronicle never substitutes the subject for an unavailable name. `OnBehalfOf` retains the
chain order, resolving each link separately. The head's `InitiatorType` is `Agent` when a
resolved identity acts on behalf of another identity, `User` for a resolved identity without
that chain, `System` for Chronicle, and `Unknown` for a missing or unset head. An unavailable
display name does not change whether that identity acts as an agent.

Application-defined service identities are not automatically classified as systems. Recognize
them by their subjects in your application, rather than relying on display names or the
`User` classification.

PII erasure removes encryption keys for protected event and read-model data; it does **not**
erase identity display names. To make a name unavailable on metadata reads, rename that
identity to an empty name. Its subject and username remain available. Your application owns
authorization and must enforce it **before** calling the metadata API.

## Causation

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Auditing;

public class CorrelationIdentityCausationCausation(ICausationManager causationManager)
{
    public void RecordPlaceOrder(string orderId) =>
        causationManager.Add(
            "MyApp.Commands.PlaceOrder",
            new Dictionary<string, string> { ["orderId"] = orderId });
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/causation.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.auditing.CausationType
import io.cratis.chronicle.auditing.causationManager

// Per thread, not per request or coroutine; clear in finally on thread-pooled paths.
class CorrelationIdentityCausationCausation {
    fun recordPlaceOrder(orderId: String) {
        causationManager.add(CausationType("MyApp.Commands.PlaceOrder"), mapOf("orderId" to orderId))
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/causation.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.java.CausationManagerJavaBridge;

import java.util.Map;

import static io.cratis.chronicle.auditing.CausationManagerKt.getCausationManager;

// Per thread, not per request or coroutine; clear in finally on thread-pooled paths.
class CorrelationIdentityCausationCausation {
    void recordPlaceOrder(String orderId) {
        CausationManagerJavaBridge.add(
            getCausationManager(),
            "MyApp.Commands.PlaceOrder",
            Map.of("orderId", orderId));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/concepts/correlation-identity-causation/causation.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.CorrelationIdentityCausationCausation do
  alias Chronicle.Auditing.CausationManager

  def record_place_order(order_id) do
    CausationManager.add("MyApp.Commands.PlaceOrder", %{order_id: order_id})
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/causation.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { causationManager, CausationType } from '@cratis/chronicle';

class CorrelationIdentityCausationCausation {
    recordPlaceOrder(orderId: string): void {
        causationManager.add(new CausationType('MyApp.Commands.PlaceOrder'), { orderId });
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/concepts/correlation-identity-causation/causation.md)

</TabItem>
</Tabs>

TypeScript also has a real `causationManager.add(type, properties)` — the client-specific pages linked below cover it, along with more Kotlin/Elixir/TypeScript causation detail than fits here (defining/redefining the root cause, reading the full chain, framework-integration examples for scoping context per request).

## Client-specific reference

Each client's own documentation covers the full API in depth — provider interfaces, framework-integration examples (Express middleware, and similar), and thread/process/async-context caveats specific to that runtime:

- [.NET client](/chronicle/clients/dotnet/) — `Cratis.Execution.ICorrelationIdAccessor`/`ICorrelationIdModifier`, `Cratis.Chronicle.Identities.IIdentityProvider`, `Cratis.Chronicle.Auditing.ICausationManager`
- [Kotlin client](/chronicle/clients/kotlin/) — `io.cratis.chronicle.correlation`, `io.cratis.chronicle.identity`, `io.cratis.chronicle.auditing`
- [Elixir client](/chronicle/clients/elixir/) — `Chronicle.CorrelationId`, `Chronicle.Identity`, `Chronicle.Auditing.CausationManager`
- [TypeScript client](/chronicle/clients/typescript/) — `correlationIdManager`, `identityProvider`, `causationManager`
