Skip to content

Correlation, identity, and causation

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.

using Cratis.Execution;
public class CorrelationIdentityCausationCorrelation(ICorrelationIdAccessor accessor, ICorrelationIdModifier modifier)
{
public CorrelationId GetCurrent() => accessor.Current;
public void SetForRequest() => modifier.Modify(CorrelationId.New());
}

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.

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();
}

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.

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 });
}

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).

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 clientCratis.Execution.ICorrelationIdAccessor/ICorrelationIdModifier, Cratis.Chronicle.Identities.IIdentityProvider, Cratis.Chronicle.Auditing.ICausationManager
  • Kotlin clientio.cratis.chronicle.correlation, io.cratis.chronicle.identity, io.cratis.chronicle.auditing
  • Elixir clientChronicle.CorrelationId, Chronicle.Identity, Chronicle.Auditing.CausationManager
  • TypeScript clientcorrelationIdManager, identityProvider, causationManager