Skip to content

Auditing — causation

See Correlation, identity, and causation for what causation means and why Chronicle tracks it. This page covers the TypeScript-specific API in depth. The causation chain is stored with every event and enables full audit-trail tracing.

TypeRole
CausationTypeA named string wrapper identifying the kind of operation that caused something.
CausationA single node in the chain — a timestamp, a CausationType, and a property bag.
ICausationManagerInterface for reading and mutating the chain in the current call context.
CausationManagerDefault AsyncLocalStorage-backed implementation.
import { CausationType } from '@cratis/chronicle';
// Built-in types
CausationType.root // 'Root' — the process-level root
CausationType.unknown // 'Unknown'
CausationType.appendEvent // 'TypeScriptClient.Append'
CausationType.appendManyEvents // 'TypeScriptClient.AppendMany'
// Custom types
const myType = new CausationType('MyApp.CreateEmployee');
import { Causation, CausationType } from '@cratis/chronicle';
const entry = new Causation(
new Date(), // occurred
new CausationType('MyApp.CreateEmployee'), // type
{ employeeId: '123' } // properties (string→string map)
);
// Factory for a placeholder
const placeholder = Causation.unknown();

The module exports a singleton causationManager that uses Node.js AsyncLocalStorage so the chain is scoped automatically to the active async context.

import { causationManager } from '@cratis/chronicle';
const chain = causationManager.getCurrentChain();
// chain[0] is always the root causation
// chain[n] are additional entries added in this context

Call add() before performing an operation to push a new entry onto the chain:

This excerpt assumes a store, an eventSourceId, and an EmployeeHired event type.

import { causationManager, CausationType } from '@cratis/chronicle';
causationManager.add(
new CausationType('MyApp.CreateEmployee'),
{ correlatedCommand: 'HireEmployee', tenantId: 'acme' }
);
// Now append an event — the event will carry this entry in its Causation chain
await store.eventLog.append(eventSourceId, new EmployeeHired('Jane', 'Doe'));

By default the root causation is a bare CausationType.root entry. Call defineRoot() once at start-up to attach application-specific properties:

import { causationManager } from '@cratis/chronicle';
causationManager.defineRoot({
application: 'hr-service',
version: '2.1.0'
});
import { causationManager, CausationType } from '@cratis/chronicle';
async function handleCommand(commandName: string, handler: () => Promise<void>) {
await causationManager.run(new CausationType(`MyApp.Command.${commandName}`), {}, handler);
}

run(type, properties, callback) restores the parent chain even when the callback awaits or throws. run(callback) isolates the existing chain without adding a link. Prefer these for concurrent or nested operations; add() changes the ambient chain of the current context and remains for legacy callers. Each append adds its own link only to that append’s outgoing chain, so sibling appends do not inherit one another’s links.

When you call eventLog.append() or eventLog.appendMany(), the Chronicle client automatically reads causationManager.getCurrentChain() and sends the full chain as the Causation field of the gRPC request. You do not need to pass it manually.