Verifying evidence

Prove the audit chain end-to-end, from spans collected during a run, to the run-root, to the signed root, to the transparency log proof.

What you'll build: a verification pipeline that walks the chain, signs the run-root, anchors it, and exports an auditor-ready bundle. Prerequisites: MCP server, or any tutorial run that produced an evidenceHashChainHead. Next: run an eval suite to regression-test compliance behavior.

Span lifecycle

code
input arrives
    |
    v
+---------------------+
| policy.evaluate     |   Cerbos: allow / deny / requires-approval
+---------+-----------+
          | allow
          v
+---------------------+
| tool.execute        |   Tool.run(parsedInput, ctx)
+---------+-----------+
          |
          v
+---------------------+
| guardrail.toolResult|   PII / injection / residency scan
+---------+-----------+
          |
          v
+---------------------+
| hash-chain append   |   prevHash + RFC 8785 + sha256
+---------+-----------+
          |
          v
+---------------------+
| run-root sign       |   Ed25519 (KMS or local)
+---------+-----------+
          |
          v
+---------------------+
| transparency anchor |   inclusion proof + checkpoint
+---------------------+

Every step on this timeline emits a span; the chain head after the last step is evidenceHashChainHead.

Collect records

ts
import { runAgent } from '@fuze-ai/agent'

const records: any[] = []

const result = await runAgent(
  { definition: agent, policy, evidenceSink: (r) => records.push(r) },
  ctx,
)

Each record has a sequence, a prevHash, and a payload. The hash of record n is computed from the canonical (RFC 8785) serialization of { sequence, prevHash, payload }.

Verify the chain

ts
import { verifyChain } from '@fuze-ai/agent'

const ok = verifyChain(records)
console.log({
  ok,
  finalHash: records.at(-1)?.payload.hash,
  runRoot: result.evidenceHashChainHead,
})

verifyChain returns false if:

  • a record is out of order
  • a prevHash does not match the prior record's hash
  • a payload byte was modified after recording

A single byte flip is detected.

Sign the run-root

ts
import { LocalKeySigner } from '@fuze-ai/agent-signing'

const signer = await LocalKeySigner.fromFile('~/.fuze/agent-key')

const signature = await signer.sign(result.evidenceHashChainHead)

For Sovereign deployments, replace LocalKeySigner with KmsSigner from @fuze-ai/agent-signing-kms. The KMS-backed signer enforces deny-on-export at the key policy level.

Verify the signature

ts
import { verifySignature } from '@fuze-ai/agent-signing'

const valid = await verifySignature({
  message: result.evidenceHashChainHead,
  signature,
  publicKey: await signer.publicKey(),
})

Anchor to the transparency log

ts
import { anchorToTransparencyLog } from '@fuze-ai/agent-transparency'

const proof = await anchorToTransparencyLog({
  runRoot: result.evidenceHashChainHead,
  signature,
  publicKey: await signer.publicKey(),
  logUrl: 'https://transparency.example.org',
})

The returned proof includes the inclusion path and the witnessed checkpoint. This is the artifact an auditor checks to confirm the run-root was published to a write-once log before any decision was acted upon.

Verify the inclusion proof

ts
import { verifyInclusion } from '@fuze-ai/agent-transparency'

const included = await verifyInclusion(proof)

Export the bundle

ts
import { exportEvidenceBundle } from '@fuze-ai/agent'

await exportEvidenceBundle({
  runId: result.runId,
  records,
  runRoot: result.evidenceHashChainHead,
  signature,
  proof,
  outPath: './bundle.zip',
})

The bundle is what an auditor receives. Every claim about the run is reproducible from this archive plus the public verifying key and the transparency log's published checkpoints.

Next: run an eval suite against your agent.