Audit trail
Kaizen keeps two deliberately different records:
- the conversation journal contains safe, member-visible facts needed to rebuild a room — messages, membership changes, artifacts, approval request/decision facts, and terminal outcomes;
- the security audit trail records who attempted or completed a consequential operation, its outcome, and privacy-safe reason codes.
They serve different readers and have different rules. Confusing them is the usual way an audit log becomes a second data breach.
What the audit trail records
Every consequential operation goes through one append-only funnel:
- tool use and actions,
- approvals and denials,
- membership and invitation changes,
- resource links, versions, and tombstones,
- task control and execution,
- configuration changes,
- sandbox dispatch and isolation denials.
Each record identifies the actor, the target (when already authorized), the outcome, and a bounded reason category.
Approval is intentionally present in both records without duplicating raw content. The room journal keeps an immutable request fact and the later member-attributed approved or denied fact so replay is auditable. The security trail keeps the consequential operation and outcome for operators. Both carry bounded identities and a canonical argument digest; neither carries the raw tool arguments.
What it deliberately excludes
This list is the point of the design, not a limitation of it:
- chat text, queries, and answer content,
- raw tool arguments and results,
- credentials, tokens, cookies, and auth headers,
- hidden reasoning and system or developer instructions,
- raw provider errors,
- source snippets and hidden source titles,
- ACL membership lists,
- invitation capabilities,
- environment values,
- presence heartbeats.
An audit trail containing chat content would need the same protection as the chats themselves — while being read by more people, more often, for unrelated reasons.
Denials reveal nothing extra
Authorization and policy failures use bounded reason codes such as not_found,
forbidden, agent_not_shareable, conversation_not_shareable,
participant_limit, and idempotency_conflict.
A record may identify the route family and target id when already authorized, but it never reveals whether an unrelated chat, invitation, member, source, or resource exists.
Model-route denials follow the same rule. A prompt refused for exceeding the
turn's input budget records outcome context_overflow with error class
ContextBudgetExceeded, alongside the existing redaction and budget refusals.
It carries the prompt's hash, the measured token count and the budget — never
the prompt — and is written before any provider call, so a refused turn leaves
the same evidence as one that was sent.
Idempotency
An idempotent retry reuses the original logical outcome. It must not create another capability or a duplicate durable fact.
An external capability invocation records its credential-free replay claim and its terminal receipt in the same transactions that persist those states. The records contain the source, operation, capability class, receipt status, and whether a credential was remembered; they never contain request arguments or credential values. A replayed nonce creates neither a second claim nor a second external request.
A run has one terminal rollup even when retries, cancellation, or salvage race. Normal reconnects create no input or run audit rows at all — durable replay is authoritative, so reconnecting is not an event.
Presence is not audited
Presence is intentionally absent from both the journal and the audit trail. Heartbeats, tab counts, device details, IP addresses, typing text, and last-seen history are not monitoring data Kaizen retains.
Viewing and typing leases are short-lived ephemeral state. If that service is unavailable, the roster shows Presence unavailable and durable messages continue to replay normally.
Run traces are not audit rows
A task's task_run_event timeline is a separate append-only member record.
A current worker lease allocates its gap-free sequence; authenticated member
controls use a separate audited append path.
Trace envelopes may contain state transitions, reason categories, step and
attempt correlation, duration, token and cost counts, exact policy and target
versions, and opaque source references. Source detail is re-authorized at
read time and may come back authorized, redacted, or
payload_unavailable.
The event payload denylist always removes system and developer instructions, hidden reasoning, credentials, auth headers, tokens, cookies, raw prompts and completions, raw provider errors, unrestricted tool arguments and results, and inaccessible member content.
SSE replay of a trace uses the durable sequence and creates no new event or audit row.
How to read a denial
- Find the reason code. It is a bounded category, not a message.
- Check the actor and route family. They tell you what was attempted.
- Do not infer existence.
not_foundcovers "never existed", "you lost access", and "it was deleted" on purpose. - Correlate with the run. Audit rows carry stable input and run correlations; the member-facing trace carries the safe diagnosis.
For an unattended run, the three authority outcomes mean different things:
denied (membership, access, policy, or binding rejected it), failed
(evaluation or exchange validation failed), and unavailable (the
authoritative path is absent). Only the second is a bug hunt. See
Task scheduler operations.
Uniform by construction
These event families flow through Kaizen's existing append-only funnel and its approval boundary. Individual agents do not implement their own audit path — which is why enabling a new agent cannot create a gap in the record.
Related
- Audit event reference — the lookup tables
- Compliance & evidence
- Data handling & retention