Audit chain
An append-only, hash-chained record of every tool call and money movement. Exactly-once, tamper-evident, and exportable for compliance.
Why it exists
Agents move money. The audit chain is the CFO-grade record of everything they did: every tool call, every payment, every KYC check, written append-only and hash-chained so it can be proven untampered after the fact.
Every governed action in CodeSpar appends an event to a per-tenant audit chain:
- Append-only. Events are never updated or deleted. Corrections are new events that reference the original.
- Hash-chained. Each event carries an
entry_hashover its contents plus theprev_hashof the event before it, so any retroactive edit breaks the chain and is detectable. - Exactly-once. An action is attested once and only once, whether it was dispatched through
session.execute(), the chat-loopsession.send(), or a rawproxy_execute. Client-SDK tool calls are attested withattested_by: client_sdk; server-side dispatch with the server attestor. - Self-auditing exports. Reading the chain out through the export path is itself an audited event.
What gets recorded
Meta-tool calls, raw provider calls, money movement (charge / pay / payout settlement), KYC dispositions, mandate spends, and policy decisions, each with its tool_call_id, resolved rail, idempotency key, and outcome. Completed tool calls land as tool_call.succeeded or tool_call.failed events with the same names and shape on every dispatch path.
Reading the chain
GET /v1/audit/eventsA browse-only, paginated listing of your org's chain events. Each event carries sequence_number, event_type, happened_at, payload, prev_hash, and entry_hash. Filters:
| Query param | Behavior |
|---|---|
event_type | Exact match, or prefix match when it ends with a dot: tool_call. matches both tool_call.succeeded and tool_call.failed. |
from / to | ISO 8601 window. Defaults to the last 7 days. |
limit | 1 to 200, default 50. |
before_sequence | Cursor for older-than pagination. Pass the next_before_sequence from the previous page to walk back. |
Browsing is read-only and does not append an export event; it exists so you (and the /dashboard/audit page) can inspect recent activity without dumping the full chain.
Anchors
A background job periodically signs the chain head, the tuple of org, head sequence, and head hash, with HMAC-SHA256 and delivers that anchor record to a configured target. The chain proves internal consistency; an anchor held outside the database is what turns tampering from detectable into provable.
Whether the anchor is held elsewhere depends on the target
The delivery target is AUDIT_ANCHOR_TARGET, one of noop, webhook or
file, and it defaults to noop — which is what CodeSpar's hosted
production runs today. Under noop the anchor is signed and persisted, and its
signature is verifiable by anyone holding AUDIT_ANCHOR_SECRET, but it is
witnessed nowhere outside the same database it is anchoring. That is a real
integrity check against accidental corruption and a real audit record; it is not
an external witness, so it does not survive an adversary with write access to
that database.
The job reports the two cases separately for exactly this reason: delivered
counts anchors an external target accepted (a webhook 2xx or a file append),
local_only counts noop. If your compliance story needs the anchor held
elsewhere, set AUDIT_ANCHOR_TARGET=webhook with a webhookUrl you control
(or file onto storage outside the database host) and check that delivered,
not local_only, is what moves.
| Endpoint | Purpose |
|---|---|
GET /v1/audit/anchors | List your org's anchors, newest first, with delivery status. |
POST /v1/audit/anchors/verify | Post an anchor record you hold (for example, fetched from your webhook target's storage). The backend recomputes the HMAC signature and cross-checks the (org, sequence, hash) tuple against the row persisted at emit time. |
The verify response carries a verdict: verified, signature_ok_but_no_local_record, local_record_diverges, or signature_invalid. A valid signature with a diverging or missing local record is provable tampering on one side or the other.
Export
An org-level export endpoint streams a slice of the chain with a signed manifest for compliance and archival; the export itself appends an audit_export event to the chain.
Audit events also drive the incident lifecycle: anomalous or policy-flagged events can be opened, triaged, and resolved as incidents from /dashboard/audit.
See also
- Guardrails: the policy decisions that the chain records
- Wallets: programmable spend, every movement attested
- Test mode: audit events fire on mocked calls too
Recipes that build on this:
- Verify the Audit Chain: anchor, verify and export the trail, and what each verdict means
- Hold a Spend for Approval: the events a held spend writes before and after the decision
- What an Allowance Spent: reading the chain to tell an attempt from money that moved
Guardrails
The policy layer that gates every agent tool call before it dispatches, spend budgets, rate limits, and human approval, enforced server-side.
Agent identity and trust (KYA)
Verifiable agent identity (did:web + Ed25519), signed reputation, and the register / rotate / revoke key lifecycle. Know Your Agent for the money side.