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.
Correlation ID
Section titled “Correlation ID”using Cratis.Execution;
public class CorrelationIdentityCausationCorrelation(ICorrelationIdAccessor accessor, ICorrelationIdModifier modifier){ public CorrelationId GetCurrent() => accessor.Current;
public void SetForRequest() => modifier.Modify(CorrelationId.New());}import io.cratis.chronicle.correlation.correlationIdManagerimport java.util.UUID
class CorrelationIdentityCausationCorrelation { fun getCurrent(): UUID = correlationIdManager.current
fun setForRequest() = correlationIdManager.set(UUID.randomUUID())}import io.cratis.chronicle.correlation.CorrelationIdManagerKt;import java.util.UUID;
class CorrelationIdentityCausationCorrelation { UUID getCurrent() { return CorrelationIdManagerKt.getCorrelationIdManager().getCurrent(); }
void setForRequest() { CorrelationIdManagerKt.getCorrelationIdManager().set(UUID.randomUUID()); }}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()) endendimport { correlationIdManager, CorrelationId } from '@cratis/chronicle';
class CorrelationIdentityCausationCorrelation { getCurrent(): CorrelationId { return correlationIdManager.current; }
setForRequest(): void { correlationIdManager.setCurrent(CorrelationId.create()); }}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
Section titled “Identity”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();}import io.cratis.chronicle.identity.Identityimport io.cratis.chronicle.identity.identityProvider
class CorrelationIdentityCausationIdentity { fun setForRequest(subject: String, name: String, userName: String) { identityProvider.setCurrentIdentity(Identity(subject, name, userName)) }
fun getCurrent(): Identity = identityProvider.currentIdentity}import io.cratis.chronicle.identity.Identity;import io.cratis.chronicle.identity.IdentityProviderKt;
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(); }}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() endendimport { 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(); }}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.
Causation
Section titled “Causation”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 });}import io.cratis.chronicle.auditing.CausationTypeimport io.cratis.chronicle.auditing.causationManager
class CorrelationIdentityCausationCausation { fun recordPlaceOrder(orderId: String) { causationManager.add(CausationType("MyApp.Commands.PlaceOrder"), mapOf("orderId" to orderId)) }}Java does not support this workflow yet.defmodule MyApp.CorrelationIdentityCausationCausation do alias Chronicle.Auditing.CausationManager
def record_place_order(order_id) do CausationManager.add("MyApp.Commands.PlaceOrder", %{order_id: order_id}) endendimport { causationManager, CausationType } from '@cratis/chronicle';
class CorrelationIdentityCausationCausation { recordPlaceOrder(orderId: string): void { causationManager.add(new CausationType('MyApp.Commands.PlaceOrder'), { 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).
Client-specific reference
Section titled “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 —
Cratis.Execution.ICorrelationIdAccessor/ICorrelationIdModifier,Cratis.Chronicle.Identities.IIdentityProvider,Cratis.Chronicle.Auditing.ICausationManager - Kotlin client —
io.cratis.chronicle.correlation,io.cratis.chronicle.identity,io.cratis.chronicle.auditing - Elixir client —
Chronicle.CorrelationId,Chronicle.Identity,Chronicle.Auditing.CausationManager - TypeScript client —
correlationIdManager,identityProvider,causationManager