Skip to content

Audit log

The optional Orchestra Audit Trail submodule (orchestra_audit_trail) records every process transition into the Audit Trail chain: a durable, tamper-evident log that survives instance deletion. It complements the live process trace; the trace shows the current run, the audit log is the permanent record of who did what, and when.

How it works

The engine and the inbox dispatch a neutral OrchestraAuditableEvent at each meaningful transition, with no knowledge of any backend. This submodule subscribes to that event and writes each one into the Audit Trail chain. The engine itself keeps zero dependency on audit_trail: enable the submodule to turn the events into chain rows, or leave it off and consume the events another way (an ECA model, a custom subscriber).

Enabling the submodule provisions a dedicated orchestra chain in audit_trail (flag mode) that claims the orchestra channels. Flag mode keeps the chain to the structured lifecycle events this module records, leaving out plain engine log lines that happen to share the orchestra channel. Recording then needs audit_trail to have a signing secret configured (see audit_trail's own setup). A write that fails (for example before a secret exists) is logged on the Orchestra channel and never breaks the running workflow, since the audit log is an optional add-on.

Every row carries the instance UUID as its correlation id, so the audit trail groups all the rows of one process run together.

What it records

Channel Actions
orchestra.instance started, completed, canceled, failed, incident_raised, remote_child_orphaned, notice_undeliverable, notification_undeliverable
orchestra.task assigned, claimed, completed, reassigned, released, reoffered, canceled
orchestra.token parked, resumed, canceled, ignored
orchestra.variables set

Every event can be toggled on the settings form. All of them are on by default except variables set, which is off, as variable writes are frequent. One event records one write, however many variables it sets at once (a webform's answers, a completion payload): the variable names and their scope (instance-wide or local to one token branch). The values are dropped unless Also record the values is turned on, since a value can hold arbitrary or personal data.

Permanent and transient context

Audit Trail keeps two context buckets per row: a permanent, HMAC-signed bucket, and a transient bucket that a retention policy can purge. The subscriber routes the structured, non-PII fields (workflow, node, tenant, outcome, reason, instance) to the permanent bucket, and leaves the actor and assignee user IDs in the transient bucket, so they stay GDPR-purgeable. Variable names and their scope are non-PII metadata and go to the permanent bucket; recorded values stay transient, so they are purgeable along with the user IDs.

A task claimed or completed under a delegation carries an extra on_behalf_of context key naming the person whose work it was. It is a user ID, so it belongs with the actor in the transient, purgeable bucket.

Configuration

At Configuration > Workflow > Orchestra > Audit trail, toggle which (channel, action) pairs are recorded. Every pair is on by default except variables set. The setting is read at dispatch time, so a missing entry counts as enabled and only an explicit off drops that event; that lets you silence a noisy transition without touching code.

Alongside the variables Set toggle sits Also record the values, off by default. On, each value is serialized, length-capped and written to the transient bucket, together with what the variables that already existed held before, so Audit Trail shows the write as the same diff as an entity update: which values changed and from what, and which variables are new. That is what makes a forensic record of every data change possible. Off, only the names and scope are kept. It applies only while Set is enabled.

When a write fails

By default a chain-write failure is caught and logged on the orchestra channel, and the workflow proceeds: a transition that happened is worth more than a transition refused for want of a log row. Strict mode re-throws it instead, for deployments where an auditable record is mandatory.

Strict mode does not by itself roll the transition back, and it is worth knowing why before relying on it. Audit Trail stages a write made inside a caller's transaction and chains it after that transaction commits, which is what stops two writers taking the same head hash. So on its default write mode the chain write happens when the transition is already committed, and a failure there is retried by cron from the outbox rather than undoing anything: the durability comes from the outbox, not from strict mode. Setting Audit Trail's in-transaction write mode to inline restores the older behavior, where a failing write aborts the caller's transaction, at the cost the staging exists to avoid. And two of the transitions recorded here, a cancel and the completion check, emit outside any transaction at all, so their fact is committed before the write is attempted whichever mode is set.

Trace versus audit log

The trace and the audit log answer different questions. The trace is live observability: the token trail of a run, rebuilt from its retained tokens, and gone when the instance is deleted. The audit log is the durable record: an append-only, tamper-evident chain that outlives the instance, for compliance and forensics.

Requirements