raxit
Capabilities

Audit & the DPR chain

The hash-chained Decision Provenance Record (DPR) audit trail.

Every decision — permit, defer, step_up, modify, deny — leaves a line you can read back:

$ raxit audit tail --n 50 --audit .raxit/var/daemon-audit.jsonl
SEQ   CALL_ID               TIME                  AGENT                 ACTION->RESOURCE        EFFECT  REASON
------------------------------------------------------------------------------------------------------------------------
3     e5c17818a2c942bbb830dce66cbae312  2026-07-11T22:28:06Z  default-agent  tool.call->calc         permit  permit
5     d867c63661454e8497369a08ebaec35d  2026-07-11T22:28:06Z  default-agent  tool.call->post_to_w..  defer   defer.policy

and any single record explains itself, including which Cedar policy produced it:

$ raxit explain d867c63661454e8497369a08ebaec35d --audit .raxit/var/daemon-audit.jsonl
call_id:         d867c63661454e8497369a08ebaec35d
effect:          DEFER
reason_code:     defer.policy
hint:            A review rule matched; a human must approve via: raxit approvals approve <token>.
chain_position:  seq=5  prev_hash=0bc499dda517...  record_hash=fc9d708826e4...

contributing cedar policies:
  [defer-post_to_webhook]
    @id("defer-post_to_webhook")
    @effect("defer")
    permit (
        principal == Agent::"default-agent",
        action == Action::"tool.call",
        resource == Tool::"post_to_webhook"
    );

That contributing cedar policies block is the real, compiled Cedar source — not a reconstruction — showing exactly the @effect("defer") annotation that produced the third decision state on top of Cedar's native binary Allow/Deny.

The mental model: audit-before-execute

The record is written and fsync'd to the write-ahead log (WAL) before the decision is returned to the caller — not logged afterward, best-effort. If that write fails, the decision flips to deny (deny.audit_error) rather than letting an unrecorded action through. Put differently: a call can never execute without a corresponding audit record existing first. This is the same fail-closed posture covered in Fail-closed & degrade-closed, applied specifically to the audit path.

Hash-chained, not just appended

Each record (schema: "dpr/2") carries prev_hash and record_hash, chaining every entry to the one before it — including the accumulated session facts that produced the decision (taint_snapshot, prior_actions, original_request), so tampering any one of those fields breaks the chain exactly like tampering effect or reason_code would.

$ raxit audit verify --audit .raxit/var/daemon-audit.jsonl
audit verify: OK

A genuinely tampered record fails loudly and specifically:

$ raxit audit verify --audit .raxit/var/daemon-audit-tampered.jsonl
audit verify: FAIL: record 5 (seq 5): record_hash mismatch (tampered)

A different failure mode is not tampering. The chain's genesis hash is seeded from the agent id at chain-creation time — verifying against the wrong --agent produces a distinct, carefully worded message rather than a false tamper alarm:

$ raxit audit verify --audit .raxit/var/daemon-audit.jsonl --agent totally-different-agent
audit verify: FAIL: this audit log's genesis was seeded for a different agent than
"totally-different-agent" (not tampering); pass --agent <the id the chain was created with>,
or start a new chain

If you see this, re-run with --agent matching whatever agent: the chain was actually created under — see Common issues for the same case walked through as a stall.

Signing is real, but opt-in and dev-keyed

Hash-chaining is unconditional and always on. Cryptographic signing of individual records is a separate, optional layer on top:

raxit: WARNING: audit log .raxit/var/daemon-audit.jsonl is UNSIGNED (no Ed25519 key found at
--sign-key). Records are still hash-chained but carry no signature. Run `raxit keys init` for
tamper-evident R5 evidence.

raxit keys init generates an Ed25519 keypair and wires --sign-key so subsequent records are individually signed. audit: require_signed: true in security.yaml (or --require-signed on the CLI) can make an unsigned audit log a load-time refusal rather than a warning.

Signing exists and works, but is off by default, and even when enabled the signing key today is a local dev-seed key, not a production KMS or certificate-chain-backed key. Don't describe audit records as "cryptographically signed by default" — the honest framing is: hash-chained always, optionally Ed25519-signed once you run raxit keys init, dev-seed-keyed until a real key management story lands.

On this page