Skip to content

Feature: Cryptographic action receipts for enterprise agent governance (AAR) #7353

Open

Description

Problem

AutoGen enables multi-agent conversations and workflows for enterprise use cases. Enterprise deployments require verifiable audit trails — not just logs, but cryptographic proof of:

  • Which agent was instructed to do what
  • What each agent actually executed
  • What data was consumed and produced
  • Whether outputs were tampered with between agents

Current observability (LLM traces, conversation logs) captures what happened but can't prove it to an external auditor or compliance system.

Proposal: Agent Action Receipt (AAR) Integration

AAR v1.0 provides exactly this:

  • Ed25519 signatures over canonicalized JSON (JCS-SORTED-UTF8-NOWS)
  • SHA-256 input/output hashing — proves data without revealing sensitive content
  • Selective disclosure — share minimum information per party (aligned with Mastercard's Verifiable Intent standard)
  • Chain-able receipts — multi-agent conversations produce a verifiable receipt chain

Enterprise relevance

  • Mastercard Verifiable Intent (announced March 5, 2026 with Google, IBM, Fiserv) — AAR includes bidirectional mapping
  • HIPAA/SOC2 audit requirements → AAR provides non-repudiable action evidence
  • x402 (Coinbase) compatible for agent payment verification

SDK

npm install botindex-aar

TypeScript. Single dependency (tweetnacl). Express middleware or manual builder. Python SDK in development.

Happy to discuss integration approach or contribute a PR. MIT licensed.

Activity

  1. Cyberweasel777 commented on Mar 7, 2026

    @Cyberweasel777
    Author

    Update: companion standard shipped — Session Continuity Certificates (SCC).

    For AutoGen's enterprise multi-agent patterns, SCC addresses the agent identity continuity requirement: prove that an agent has maintained consistent identity across sessions, with cryptographic key rotation support.

    Same Ed25519 + JCS crypto stack. One keypair for both standards.

  2. passionworkeer commented on Mar 11, 2026

    @passionworkeer

    Great proposal for enterprise governance. For implementation, consider a pluggable architecture where different receipt formats can be supported. Also, think about backward compatibility with existing agent workflows.

  3. fjnunezp75 commented on Mar 15, 2026

    @fjnunezp75

    Interesting timing on this — GPU-Bridge (https://gpubridge.io) is running x402 in production for exactly this use case.

    When an agent calls our inference endpoints (LLM, embeddings, STT, image gen), it sends USDC on Base L2 via x402. The HTTP 402 response includes the payment details; the agent settles onchain; the service responds with the result + a signed receipt.

    What this gives you for your AAR use case:

    • The payment transaction on Base is public and immutable — it proves the agent authorized the call at a specific block height
    • The x402 receipt includes the payload hash, so you can verify the request wasn't tampered with
    • No shared secret, no API key rotation — the "credential" is just the agent's wallet

    The gap I can confirm from production: x402 proves the agent paid, but doesn't natively prove what the service returned. Your AAR spec filling in the output hash/signature side is the missing piece to close that loop.

    Our coinbase/x402 ecosystem PR (#1619) has some notes on the flow if useful: https://github.com/coinbase/x402/pull/1619

  4. aelitium-dev commented on Mar 15, 2026

    @aelitium-dev

    The gap fjnunezp75 pointed out is exactly the missing layer at the LLM boundary.

    x402 proves the agent paid.
    AAR proves the agent acted.

    But between those two events there is an LLM call, and neither spec currently defines what "prove what the model returned" means in verifiable terms.

    One approach is to treat the LLM call itself as a verifiable artifact.

    For example, an evidence bundle could expose:

    {
      "request_hash":  "sha256:...",
      "response_hash": "sha256:...",
      "binding_hash":  "sha256:..."
    }

    Where binding_hash = sha256(canonical({request_hash, response_hash})).

    Verification can be done offline:

    aelitium verify-bundle ./bundle
    aelitium compare bundleA bundleB
    

    So the AAR evidenceRef field could reference something like:

    type: "aelitium/binding-bundle"
    

    This keeps the receipt generic while giving the LLM evidence layer concrete semantics.

    Reference implementation (Python, Apache-2.0):
    https://github.com/aelitium-dev/aelitium-v3

  5. fjnunezp75 commented on Mar 15, 2026

    @fjnunezp75

    The evidence bundle structure you propose closes the gap cleanly. The binding_hash is the critical piece — it ties the payment (x402) to the action (AAR) to the model output, so all three can be verified independently without trusting any single component.

    The missing implementation detail: who signs the binding_hash? If it is the provider, you are back to trusting the provider. If it is the agent, the agent cannot sign what the provider returned without the provider cooperating. The only way to make it trustless is if both parties sign — provider signs the response_hash, agent signs the binding (payment_tx + request_hash + response_hash), and both signatures are persisted.

    We are building toward this at GPU-Bridge: x402 proves the agent paid, and we are working on HMAC-signed receipts where both the payment and the response hash are included. The bilateral signature model is the right target. Happy to share implementation notes if useful for the AAR spec.

  6. Cyberweasel777 commented on Mar 15, 2026

    @Cyberweasel777
    Author

    fjnunezp75 — the bilateral signature problem is the right question. You're correct that single-party signing doesn't achieve trustlessness.

    AAR v1.1 (just shipped) adds evidenceRef to address exactly this layering:

    {
      "evidenceRef": [
        {
          "type": "x402/payment-proof",
          "hash": { "alg": "sha256", "digest": "..." },
          "uri": "base:0xTxHash..."
        },
        {
          "type": "aelitium/binding-bundle",
          "hash": { "alg": "sha256", "digest": "..." },
          "issuer": "provider"
        },
        {
          "type": "agent/binding-countersign",
          "hash": { "alg": "sha256", "digest": "..." },
          "issuer": "agent"
        }
      ]
    }

    The receipt itself carries the agent's Ed25519 signature (proves the agent attests to the action). The evidenceRef entries point to the provider's signed response hash and the on-chain payment proof. Three independent signatures, three independent verification paths, no single trust root.

    The bilateral model you're building at GPU-Bridge is the right target. The spec change we just pushed: Cyberweasel777/agent-action-receipt-spec@0a24988

    On the practical side — we also just shipped compliance intelligence endpoints that agents can query before making x402 payments. Regulatory exposure checks, threat radar, jurisdiction risk scoring. If an agent is about to pay for inference from a provider in a sanctioned jurisdiction, it can check first:

    GET /api/botindex/compliance/exposure?project=gpu-bridge
    

    That's the full loop: discover the service (Agorion) → compliance check (BotIndex) → pay (x402) → execute → prove it happened (AAR with bilateral evidence).

    Catarina Pereira (@aelitium-dev) — evidenceRef with type: "aelitium/binding-bundle" is now in the schema. Spec + example: https://github.com/Cyberweasel777/agent-action-receipt-spec/blob/main/examples/evidence-ref-receipt.json

    Would be interested in GPU-Bridge's HMAC receipt format — if it can map to an evidenceRef entry, agents get interop across both approaches without choosing one.

  7. fjnunezp75 commented on Mar 15, 2026

    @fjnunezp75

    The three-layer evidenceRef structure solves the trust root problem cleanly. Three independent signatures with three independent verification paths means no single party can forge the complete bundle — you need the on-chain tx hash (immutable), the provider signature (their key), and the agent countersign (agent key).

    The Ed25519 countersign is the right choice for agents — stateless, fast, no key management overhead compared to secp256k1.

    On the GPU-Bridge implementation side: we are currently signing provider responses with HMAC-SHA256 using a per-account secret (simpler to implement, not trustless). The move to provider Ed25519 + agent countersign is the next step, and your evidenceRef structure gives us a clean schema to target.

    Two questions on the AAR v1.1 spec:

    1. The type — what does it bind specifically? Just response_hash + payment_hash, or does it include the input (prompt/request) hash as well? Including the input is important to prove the provider received and processed the specific request, not just that a payment occurred.

    2. On revocation: if an agent key is compromised, how does the spec handle invalidating countersigned receipts? Is there a key rotation mechanism, or does receipt validity become permanently dependent on the original key state at signing time?

    Will read through the 0a24988 commit now.

  8. aelitium-dev commented on Mar 15, 2026

    @aelitium-dev

    Good question on the input hash — in AELITIUM the bundle already includes it.

    The structure today is:

    • request_hash — canonical hash of model + messages (the input)
    • response_hash — hash of what the provider returned
    • binding_hash = sha256({request_hash, response_hash})

    So the bundle proves the specific request ↔ response pairing, not just that a response existed.

    That makes it possible to bind the full chain you outlined:

    payment_tx → request_hash → response_hash → binding_hash → agent countersign

    Signatures can then be layered on top without changing the bundle structure (provider signs response_hash, agent countersigns the binding).

    The evidenceRef structure you added maps cleanly to this — the bundle stays deterministic, and the receipt layer handles attestations.

    Happy to align field names if useful for the AAR spec.

  9. fjnunezp75 commented on Mar 15, 2026

    @fjnunezp75

    The binding_hash approach is clean — sha256({request_hash, response_hash}) creates a deterministic anchor that can be independently verified by anyone with the inputs. The payment_tx → request_hash → response_hash → binding_hash → agent countersign chain is exactly the full audit trail for autonomous agent transactions.

    On key rotation: we are implementing this at GPU-Bridge using HMAC-SHA256 with per-account secrets today (simpler, not trustless). Moving to Ed25519 with the binding_hash structure you describe would give us proper trustlessness. The alignment you mention on field names would be useful — we want our receipt format to be compatible with the AAR spec so operators can use standard tooling to verify both.

    Specific proposal: if , we could emit it directly in our job response alongside the existing fields. The agent can then countersign it using its own key. The payment_tx is already on-chain via x402 and can be referenced by hash.

    Would it make sense to draft a minimal compatibility mapping between the AAR evidenceRef fields and what GPU-Bridge currently emits? We could PR it to the spec repo if useful.

  10. aelitium-dev commented on Mar 16, 2026

    @aelitium-dev

    Good questions.

    In AELITIUM the bundle already includes the input as well:

    • request_hash — hash of the model + messages
    • response_hash — hash of the provider response
    • binding_hash = sha256(canonical({request_hash, response_hash}))

    So it proves the specific request ↔ response pairing, not just that a response existed.

    That fits the chain you described:

    payment_tx → request_hash → response_hash → binding_hash → agent countersign

    From the AELITIUM side the bundle is deterministic evidence; signatures can be layered on top without changing the bundle structure.

    A rough mapping could be:

    • evidenceRef.type → "aelitium/binding-bundle"
    • evidenceRef.uri → GPU-Bridge job / receipt identifier (if exposed)
    • evidenceRef.hash.digest → bundle or binding digest, depending on how the spec wants to point at it

    If GPU-Bridge emits a stable response hash with the job result, mapping that into an evidenceRef entry looks straightforward.

    Would it help to sketch a minimal example mapping between the GPU-Bridge receipt fields and the AAR evidenceRef structure?

  11. razashariff commented on Mar 16, 2026

    @razashariff

    MCPS (MCP Secure) takes a similar approach — per-message ECDSA P-256 signatures over MCP tool calls and responses, with agent identity passports, nonce binding, and replay protection. MCPS focuses on the transport/message layer while AAR focuses on the action/audit layer — they'd be complementary. Published as an IETF Internet-Draft, 180 security tests, zero dependencies, drop-in middleware for any MCP server. github.com/razashariff/mcps

  12. razashariff commented on Mar 16, 2026

    @razashariff

    MCPS (MCP Secure) provides the cryptographic foundation for tamper-evident action receipts. Every MCP message — tool call or response — gets a signed envelope containing:

    • passport_id: which agent made the call (cryptographic identity, not a session ID)
    • nonce: unique per-message, prevents replay
    • timestamp: bound with a 5-minute window
    • message_hash: SHA-256 of the full JSON-RPC body
    • signature: ECDSA P-256 binding all fields

    The onAudit callback emits a structured event for every verification outcome (accepted / rejected / replay / forgery), providing the building blocks for a tamper-evident audit chain. These events include the passport_id, method, timestamp, and outcome — ready for SIEM ingestion (we provide examples for Splunk, Datadog, ELK, and CloudWatch).

    For enterprise compliance, MCPS maps to 23 SOC 2 Trust Service Criteria — CC7 (system monitoring) is directly addressed by the audit event stream.

    Published as an IETF Internet-Draft and available on npm as mcp-secure (zero dependencies).

  13. creatorrmode-lead commented on Mar 23, 2026

    @creatorrmode-lead

    This maps closely to what we built in AgentVeil Protocol:

    1. Ed25519 identity — W3C DID (did:key) with challenge-response verification
    2. Hash-chained audit trail — SHA-256 chain, anchored to IPFS via Merkle tree proofs
    3. EigenTrust reputation — transitive trust from peer attestations, computed via power iteration
    4. Dispute resolution — contest unfair attestations, arbitrator resolves, overturned ratings excluded from reputation
    5. Sybil detection — collusion clusters, same-owner cross-attestation penalty

    Where this differs from AAR: AAR proves a specific action happened (compliance). AVP answers a different question — "should I delegate work to this agent?" — based on cumulative peer attestations and algorithmically computed trust scores.

    These approaches are complementary — AAR for per-action audit, AVP as a trust enforcement layer for ongoing agent-to-agent trust assessment.

    Production: https://agentveil.dev

  14. FransDevelopment commented on Mar 24, 2026

    @FransDevelopment

    The AAR spec addresses execution-level receipts (proving what an agent did). There's a complementary layer: proving who the agent is before it acts.

    The Open Agent Trust Registry (OATR) provides runtime-level identity verification. Agent runtimes register Ed25519 public keys. The registry is a signed JSON manifest that can be verified locally without network calls.

    This composes with AAR:

    Layer Question Approach
    Identity (OATR) Who is this agent and is its runtime authorized? Ed25519 JWT attestation verified against signed registry
    Execution (AAR) What did the agent actually do? Cryptographic action receipts with hash chains

    Together: OATR proves the agent is authorized before it acts, AAR proves what it did after it acts. The identity binds to the receipt: the same Ed25519 key that signs the OATR attestation can be referenced in the AAR receipt, creating a single cryptographic chain from authorization through execution.

    This pattern is already working in production. ArkForge builds execution attestation receipts that include agent_identity (a DID) and delegation_path (referencing the OATR registry). The Agent Identity Working Group recently completed a 5-step conformance test: OATR attestation, encrypted channel, DID resolution, registry verification, and receipt cross-check, all verified by three independent projects using the same Ed25519 key.

    MIT licensed, 7 registered issuers, automated CI-verified registration. The npm SDK is @open-agent-trust/registry.

  15. razashariff commented on Mar 26, 2026

    @razashariff

    Update: the message integrity approach for cryptographic action receipts is now in the OWASP MCP Security Cheat Sheet (Section 7, merged this week). Covers signing, nonce-based replay protection, and tool definition pinning.

    For enterprise governance specifically, two additions that complement the receipt model:

    1. Pre-action trust verification -- before an agent executes a high-stakes action, check its behavioural trust score. A 5-dimension score (code attestation, execution success, behavioural consistency, operational tenure, anomaly history) gives more signal than binary allow/deny.

    2. Sanctions screening gate -- for any agent action involving financial transactions or external entities, screen the counterparty before execution. This is a regulatory requirement for human-initiated transactions and will extend to agent-initiated ones.

    The trust model is in draft-sharif-agent-payment-trust-00 on the IETF Datatracker.

  16. 400 remaining items

  17. Paraphern commented on Oct 13, 2026

    @Paraphern

    Three answers, one per thread of this exchange.

    Federico Blanco Sánchez-Llanos (@babyblueviper1) — the identity question, answered from the current format: no, not today. The pin envelope stores the launch config (cmd: {command, args}) as un-hashed metadata; tool hashes cover name + description + inputSchema. Identical surfaces served by different servers produce identical tool hashes today, and a floating npx selector (-y @scope/server) does not even record a version. The gap is already on our books with receipts, from two live corpus cases: dependency drift — mongodb-mcp-server@2.1.2, floating zod range, same tarball serving different numCandidates typing depending on which in-range minor resolved — and packaging drift — @supabase/mcp-server-supabase moved its stdio entry path with every tool hash intact; launch configs broke, contracts didn't. The v1.1 direction for PIN-SPEC: an identity block ({package, resolvedVersion, entryPath | url}) with an identityHash bound into the envelope — a surface pin should commit to who serves the surface, not just what it serves. We will also document your external_evidence composition (tool_surface_snapshot, pin as the opaque record) as the normative pattern in the spec — the two-check verification should be spec, not folklore.

    Daniel Yarmoluk (@Yarmoluk) — agreed: one field, no new artifact. One requirement: name the canonicalization in the envelope. Your sig_scheme is ed25519-jcs; the schema digest should carry the same discipline — a canonicalization_profile_id next to it, exactly as the registry in this thread already made a required evidence field. Otherwise two verifiers that canonicalize differently both "verify" and disagree.

    Liuyanfeng1234 — we hold the receipt for the hole you describe. Our 1.3.0 release changed one canonicalization policy (additionalProperties true/{} equivalence), and byte-identical schemas regraded: on the 1.3.0 re-run chrome-devtools dropped 27 false BREAKINGs, and mongodb moved from 4 BREAKING + 16 NOTATION (graded on 1.2.0) to 5 + 15 — same tarball pair, no server release in between. That is verifier drift, not server drift. The v1.1 direction: stamp the policy version into every pin, and surface a policy bump as its own class, separate from server drift — consumers should never blame the vendor for the verifier's change. Your lone-surrogate case and our equivalence case are the same lesson from opposite ends: the bytes match, the contract does not.

  18. babyblueviper1 commented on Oct 13, 2026

    @babyblueviper1

    GERALD MUDDLETHWACK (@Paraphern) the identity read matches the question. Tool hashes over name, description, and inputSchema, with the launch config left as un-hashed metadata, do not commit to who serves the surface. An identity block {package, resolvedVersion, entryPath | url} with identityHash inside the envelope is the right place for that. Server identity stays out of action_binding_tool_hash.

    If PIN-SPEC names this composition, these are the bytes that verify:

    • action_binding_tool_hash is sha256: plus the sha256 of the tool name as raw UTF-8. Description and inputSchema are outside it. The name is hashed as raw text, so a JSON-quoted string will not match.
    • action_binding_args_hash is sha256: plus the sha256 of the RFC 8785 encoding of args.
    • The pin is one external_evidence entry. evidence_type is the caller's label (tool_surface_snapshot is an example, not a type this endpoint special-cases). record is the pin's own saved text, kept opaque. We do not parse it, and we do not canonicalize the pin.
    • external_evidence_hash is sha256: plus the sha256 of the RFC 8785 encoding of the whole evidence array. That field is in the decision_ref preimage. canonicalization_version is its own preimage field, so a canonicalizer change is visible on its own.

    The binding commits to the bytes the caller supplied. It does not check record_sha256, it does not authenticate the pin, and it does not compare the snapshot to a later tools/list. The two checks stay split:

    1. The receipt recomputes: the action was approved against these exact bytes.
    2. Your graded diff, including the identity block, says the bound snapshot still matches this server.

    Grading and identity stay in the pin layer.

  19. Paraphern commented on Oct 13, 2026

    @Paraphern

    Federico Blanco Sánchez-Llanos (@babyblueviper1) this locks the composition down — thank you for writing the exact bytes. The spec's composition section now carries them exactly (PIN-SPEC §7.1): the evidence entry stays opaque on your side (no parse, no canonicalization of the record), evidence_type documented as the caller's label with tool_surface_snapshot as the recommended example rather than a reserved type, and the two checks split exactly as you wrote them — the receipt binds the bytes; the graded diff, identity block included, answers whether the surface still matches this server.

    Two alignments worth naming: your canonicalization_version preimage field and our POLICY class are the same principle enforced at both layers — a canonicalizer change must be visible on its own, never folded into a silent regrade. And the raw-UTF-8 note on action_binding_tool_hash (a JSON-quoted name will not match) is exactly the boundary a spec has to write down; it is in the doc now, credited to this thread.

  20. babyblueviper1 commented on Oct 13, 2026

    @babyblueviper1

    GERALD MUDDLETHWACK (@Paraphern) §7.1 matches the /review preimage. The paragraph above it, and one soundness bullet, describe a different hash.

    The receipt does not canonicalize record. record stays the pin's saved text. external_evidence_hash is sha256: plus the SHA-256 of the RFC 8785 encoding of the whole evidence array, and that is the field in the decision_ref preimage. A verifier who runs JCS on the pin and hashes those bytes will not recompute the receipt. The §7 sentence that says the receipt hashes the record under its own canonicalization into decision_ref should say this instead.

    The bullet that says consumers hashing the record must record a canonicalization_profile_id is the other case. The profile belongs next to a digest of structured fields. record has none.

    One gap on the pin side, because check 2 has to be repeatable: in the §2.1 example, identityHash is a member of the object it hashes. Hash the identity fields with that member absent, and store the digest beside them.

    POLICY and canonicalization_version are the same principle at the two layers. They are not the same field.

  21. Paraphern commented on Oct 13, 2026

    @Paraphern

    Federico Blanco Sánchez-Llanos (@babyblueviper1) this locks the composition down — thank you for writing the exact bytes. The spec's composition section now carries them exactly (PIN-SPEC §7.1): the evidence entry stays opaque on your side (no parse, no canonicalization of the record), evidence_type documented as the caller's label with tool_surface_snapshot as the recommended example rather than a reserved type, and the two checks split exactly as you wrote them — the receipt binds the bytes; the graded diff, identity block included, answers whether the surface still matches this server.

    Two alignments worth naming: your canonicalization_version preimage field and our POLICY class are the same principle enforced at both layers — a canonicalizer change must be visible on its own, never folded into a silent regrade. And the raw-UTF-8 note on action_binding_tool_hash (a JSON-quoted name will not match) is exactly the boundary a spec has to write down; it is in the doc now, credited to this thread.

  22. babyblueviper1 commented on Oct 13, 2026

    @babyblueviper1

    GERALD MUDDLETHWACK (@Paraphern) confirmed on 6a62bc95. §7 puts external_evidence_hash in the preimage and leaves record as the pin's saved text. The profile bullet applies to structured-field digests. identityHash covers {package, resolvedVersion, entryPath, url} with that member absent.

    Those four fields still need §3's sorted-key form. In the example, package is written before entryPath. Under §3 the order is entryPath, package, resolvedVersion, url. A digest of the example's written order will not match a digest under §3.

  23. Paraphern commented on Oct 13, 2026

    @Paraphern

    Federico Blanco Sánchez-Llanos (@babyblueviper1) fixed — including the §3 table row for hash, which had the same unsorted list. Hash inputs are now the §3 canonical form everywhere, independent of file display order.

    Your review is now a rule on our side: a hostile pass before every spec commit. The passes that followed are in the change history (revisions 3-7) — credited to this thread.

  24. babyblueviper1 commented on Oct 13, 2026

    @babyblueviper1

    GERALD MUDDLETHWACK (@Paraphern) confirmed on 4ea3504e.

    hash is { description, inputSchema, name }. identityHash covers { entryPath, package, resolvedVersion, url } with that member absent. Both lists are §3 sorted-key order. §7.1 is unchanged.

  25. Paraphern commented on Oct 17, 2026

    @Paraphern

    PIN-SPEC v1.1 is implemented and live in rugsnare 1.4.3 (npm, spec): the identity block with identityHash (over the four fields with the member absent, per this thread), policyVersion stamped in every pin, IDENTITY and POLICY classes, and byte-stable pin serialization verified across checkout locations.

    POLICY works as speced in §3.1: a legacy pin (no policyVersion) produces exactly one POLICY finding with exit 1 and no re-graded tool classes - the run stops, because each graded class under a policy the approver never saw would look like ordinary drift to anyone who scrolled past the warning line. Verified on the public npm package with a clean stand.

    The 38 legacy snapshots in our drift-watch catalog will self-heal on the next run: each gets POLICY, each gets re-pinned, and the catalog rolls forward under the current policy. No silent re-grades anywhere.

    Promised in this thread and in snyk/agent-scan#482 - done as of rugsnare 1.4.3.

  26. Yarmoluk commented on Oct 23, 2026

    @Yarmoluk

    GERALD MUDDLETHWACK (@Paraphern) agreed, the schema digest should name its canonicalization. I'll carry a canonicalization_profile_id next to it, same JCS profile as the signature.

  27. Yarmoluk commented on Nov 4, 2026

    @Yarmoluk

    Follow-up to my Oct 23 note: ckg-receipt 0.4.0 carries canonicalization_profile_id: "jcs-rfc8785-v1" beside receipt_ref, the digest over the structured payload, and in /verify responses. It sits outside the signed payload, so earlier receipt_refs recompute unchanged. source_content_hash stays a raw-bytes digest with no profile, per Federico Blanco Sánchez-Llanos (@babyblueviper1)'s point that the profile belongs next to a digest of structured fields.

  28. klenioaraujo commented on Nov 6, 2026

    @klenioaraujo

    This proposal is well-specified and the four questions it lists are the right ones. I want to add a fifth that is missing, and to note a property the current AAR design does not have.

    The missing question: what was excluded, and on what basis?

    The four listed questions — which agent was instructed, what it executed, what data was consumed and produced, whether outputs were tampered with — all describe the positive path. They record what happened. For an agent that makes retrieval decisions, the negative path matters equally: documents that were available and were not used.

    Consider an agent that retrieves 10 documents from a corpus of 50,000 and answers from them. A signed receipt proves those 10 were the ones fed to the generator and that nothing was altered afterwards. It does not distinguish between:

    • the retriever examined all 50,000 and 10 were the most similar, versus
    • the retriever examined 500 candidates and 10 were the most similar among those

    Both produce an identical, validly-signed receipt. For an enterprise governance use case, that difference is often the whole question. "The agent did not cite document X" is very different evidence depending on whether X was examined.

    The cheapest fix is one field: candidates_examined. It costs nothing to compute, it is impossible to fake without breaking the signature, and it immediately distinguishes exhaustive from heuristic retrieval.

    The missing property: receipts prove non-alteration, not non-removal.

    Ed25519 over canonical JSON proves that a receipt was not modified after signing. It does not prove that a receipt was not deleted from the log. An attacker with access to the receipt store can remove entry 47; entries 46 and 48 remain individually valid, the signature of each verifies, and a verifier that checks receipts one at a time sees nothing wrong.

    What catches this is chaining: each receipt carries the hash of its predecessor, so removing one breaks the link and the verifier reports where. The chain is cheap — a SHA3-256 over the previous hash plus the current canonical payload — and it changes the security claim from "not altered" to "not altered and not removed".

    I have this running: 6 records chained, then one removed from the middle, then verification. The verifier reports the exact block with expected_prev differing from found_prev. Zero false negatives on the removal test, because the link is arithmetic, not a heuristic.

    Both points are additive to the AAR spec rather than changes to it. The canonical-JSON-plus-Ed25519 core is sound; what I am suggesting is one extra signed field (candidates_examined) and one extra structural property (predecessor hash), because enterprise governance questions are usually about what was not done, and a receipt that only proves what was done answers half the question.

    Reproducible notebook on the chaining property: https://www.kaggle.com/code/kleniopadilha/winnex-tracer-jus-due-diligence-contratual (the audit-record cells at the end).

  29. Yarmoluk commented on Nov 6, 2026

    @Yarmoluk

    Klenio Araujo Padilha (@klenioaraujo) — both proposals help make the verification guarantees more precise.

    For exclusion evidence, a signed candidates_examined count prevents later alteration, but doesn't establish that the count was correct when signed.

    For deterministic graph retrieval, I'd bind the receipt to:

    • an immutable, retrievable graph snapshot and its digest;
    • the traversal implementation/version, starting nodes, filters, ordering, and stopping limits;
    • the visited count and a digest of the canonically ordered visited IDs;
    • the returned evidence IDs and hashes.

    An auditor could then reproduce the selection and compare its digest. That makes the reported traversal independently checkable, while keeping a separate question explicit: whether the original execution actually followed it.

    On chaining, predecessor links expose interior gaps when checked. Tail truncation needs an additional reference: a previously retained terminal hash and sequence count, trusted independently of the presented log. A count stored only alongside that log can be truncated with it.

    Would you extend the notebook with both cases—interior removal, and a shortened tail that passes chain verification until compared against the retained checkpoint? That would make the boundary of each guarantee concrete.

  30. klenioaraujo commented on Nov 6, 2026

    @klenioaraujo

    You were right, and the measurement changed what I had to write.

    I ran the three cases on a real SHA3-256 chain. Two results that matter for this thread:

    Interior removal. Eight records, delete the fourth, verify:

    violations: 1 | verified: False
    broken at block 3
    expected_prev: c67e5960...
    found_prev:    b4e4c3a7...
    

    Detected and localised, as expected.

    Tail truncation. Delete the last three:

    blocks on disk      : 5 of 8
    truncation_detected : True
    expected last hash  : 6e063f37...
    found last hash     : a59d83e5...
    

    Detected — because verify_integrity() in winnex-tracer 1.1.3 retains the expected terminal hash
    out-of-band and compares it on every run. That is the mechanism you specified: a previously
    retained terminal hash, checked independently of the presented log. The earlier version of the
    library did not do this and would have passed the truncated log silently, which is why I had
    written the negative result into the notebook first. Your point was correct for the version that
    existed when you made it.

    And the boundary you named is still the boundary. My Case 3 is: what if the attacker rewrites
    the log and the retained reference together? Then verification passes. Any scheme whose
    reference is stored alongside the data it validates can be truncated with that data — which is
    your sentence, exactly. The guarantee is a property of where the reference lives, not of the
    chain. Without an external anchor (RFC 3161 TSA, a value published in a channel the owner does not
    control, another system's independent state), the reference is decoration.

    On candidates_examined correctness. I agree it is not fixable by a receipt, and I do not
    think it should be attempted there. A signature binds a value; it cannot vouch that the value was
    true when bound. What can make the count checkable is the thing you proposed for the graph case: a
    corpus digest the verifier recomputes against, which turns the count from a claim into a
    derivation. The receipt carries the evidence; the verifier re-derives it.

    Notebook with all three cases runnable, including the two that pass and the one that should not:
    https://www.kaggle.com/code/kleniopadilha/hash-chain-what-it-detects-and-what-it-doesnt-v2

    I am not proposing to change the AAR shape here. I am pointing at the layer underneath it: if a
    receipt is going to make a claim about retrieval, the retriever has to be able to produce evidence
    stronger than a count, and that evidence has to be re-derivable by the verifier. Whether that
    belongs in AAR v1.1 as an optional field is your call, not mine.

  31. giskard09 commented on Nov 6, 2026

    @giskard09

    This is the two-layer split trail-head-ref-v1 (argentum-core) ships: continuity is the hash chain — removes/reorders break the link, same test you named here, Daniel. completeness is a periodic checkpoint anchored outside the log owner's control, catching exactly what Klenio's Case 3 just confirmed by construction: tail truncation, which continuity alone can't see (a shorter log and a truncated one look identical internally). The spec traces this split to your distinction between "the records included" and "every action recorded" in this same thread, and the tail-truncation test to babyblueviper1's run here too. Without the anchor, a retained terminal hash lives next to the data it validates, so rewriting both together still works — the spec calls this out explicitly (window_anchor: "uncertified" is a warning, not a weak pass, because an uncertified start is indistinguishable from a rewritten one).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions