SilvectorDocs
Silvector docsConceptsUpdated 2026-11-07

Concept

Receipt

A signed record of one decision that anyone can check offline.

Definition

A receipt is a body and an Ed25519 signature. The signature covers the canonical JSON of the body: keys sorted by UTF-16 code unit, integers only, no whitespace, strings escaped as JavaScript's JSON.stringify escapes them. The public key is published at /.well-known/jwks.json under the kid the body names in agent_kid, and a receipt verifies only under that key. Each receipt names the one before it in the same tenant stream through prev.

Shape
interface SignedReceipt {
  body: {
    v: "ag47.receipt.v1";
    ts_edge: number;
    tenant: string;
    agent_kid: string;          // signing key id
    delegation: string | null;
    action_hash: string;        // sha256 of method, path, authority, request id
    policy_bundle_hash: string;
    flag_state_hash: string;
    outcome: Outcome;
    rule_id: string;
    prev: string;               // hash of the previous receipt, or 64 zeros
    bypass: boolean;
    verification?: Verification;
  };
  sig: string;                  // base64url Ed25519 over canonical(body)
}

Invariants

InvariantChecked by
Receipts verify offline, reject any change to the body, and replay to the same bytes.packages/engine/src/engine.test.ts: "receipts verify offline, reject tamper, and replay to the same bytes"
Canonical JSON sorts keys and rejects floats.packages/engine/src/engine.test.ts: "canonical json sorts keys and rejects floats"
A receipt signed by any published key other than the one its agent_kid names does not verify.apps/docs/test/replay.test.ts: "a receipt signed by another published key does not verify"
Receipts chain per tenant.apps/api/src/api.test.ts: "decide receipts chain per tenant and story runs continue the stream (AG4-278)"
Every quickstart on this site verifies a real receipt in CI.apps/docs/scripts/run-quickstarts.mjs

Out of scope

  • A global chain. The prev chain is per tenant stream inside one Worker isolate; a restarted isolate starts a new stream at 64 zeros. Nothing orders receipts across isolates yet: the published transparency head appends recent receipt hashes only to a per-data-center cache, best effort, up to 64 leaves.
  • The request body. A receipt hashes the action; it does not store the content.
  • Whether the sender was an AI agent, unless verification says verified.

Evidence grade

Evidence grade Rung 2 · Operational
Covers
Which rule decided this request and that the record was not changed after signing. Not whether the sender was an AI agent.
Confidence
None stated. No calibrated confidence applies to this record.
Calibration
Uncalibrated
Known error rates
None measured. Silvector publishes no detection error rate yet.
Basis
  • Deterministic decide() rule allow.default
  • Ed25519 signature over the canonical receipt body, checked against the published JWKS
Bias risks
  • The decision is only as good as the facts it was given. A request with no signature claim is not_identified, which is not a finding that a person or an agent sent it.
Next step
To ask who was behind a series of requests, keep the receipts and escalate to rung 3 with at least two independent signal families. How escalation works
Assigned by
These docs, from the rung definitions. The grade is not a signed field of the receipt.