Audit trail and events
Every command writes audit events in the same transaction as the change itself: what
happened, who did it (actorId, or null for tick), when, and the details, such as the comment
or which approvers were removed and why. The audit trail is append-only.
const history = await rungs.history(request.id);for (const event of history) console.log(event.seq, event.at, event.actorId, event.type);
// 1 … u_req request.submitted// 2 … u_req step.activated// 3 … u_req task.assigned// 4 … u_mgr task.approved// …Runs in CI:examples/docs-snippets/src/concepts/audit.ts
Events for your application
Section titled “Events for your application”After a command commits, Rungs delivers domain events to onEvent, for example to send an
email when a task is assigned:
const rungs = createRungs({ store: memoryStore(), clock: () => new Date(), ids: () => crypto.randomUUID(), // After each command commits, at least once and in order per request. Make it idempotent: // `requestId` and `seq` identify an event. onEvent: async (event) => { if (event.type === "task.assigned") await email(event); },});Runs in CI:examples/docs-snippets/src/concepts/audit.ts
Events are stored in an outbox in the same transaction, so none is lost when a process stops. They
are delivered at least once, in order per request; a failed delivery is retried with backoff and
holds back only that request’s later events. A process without onEvent, such as a web server,
leaves them for a worker that runs rungs.dispatchPending() (EC-58).
Because an event can arrive twice, make handlers idempotent: requestId with seq identifies an
event.