From 5d825e97c0514d8c50ae8941bdf036d0e1769c31 Mon Sep 17 00:00:00 2001 From: "Timmy Jr." Date: Fri, 7 Aug 2026 14:42:05 +0000 Subject: [PATCH] Draft signed path proof receipt --- docs/path-proof-receipt.md | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 docs/path-proof-receipt.md diff --git a/docs/path-proof-receipt.md b/docs/path-proof-receipt.md new file mode 100644 index 0000000..81b7a51 --- /dev/null +++ b/docs/path-proof-receipt.md @@ -0,0 +1,98 @@ +# Path Proof Receipt v0 + +## Decision + +The smallest useful receipt is a **runtime-signed commitment to an action and its observed execution path**. It must be emitted by a trusted recorder at the model/tool boundary, not composed by the agent whose claim is being checked. + +A self-reported model name or an unsigned transcript is provenance metadata, not proof. + +## Receipt + +The wire form is RFC 8785 canonical JSON. Hashes are lowercase SHA-256 hex. The signature covers the canonical JSON object with `attestation.sig` omitted. + +```json +{ + "v": 0, + "action": { + "kind": "gitea.issue.labels.replace", + "target": "stackchain/stackchain-lab-loop#3", + "request_sha256": "", + "result_sha256": "" + }, + "path": { + "model": { + "id": "", + "invocation": "" + }, + "tools": { + "count": 1, + "trace_sha256": "" + }, + "policy": { + "bundle_sha256": "" + }, + "code": { + "repo": "", + "commit": "", + "entrypoint": "" + } + }, + "evidence": { + "uri": "sha256:" + }, + "attestation": { + "issuer": "", + "key_id": "", + "alg": "Ed25519", + "sig": "" + } +} +``` + +`tools.trace_sha256` commits to a canonical ordered array of `{seq, tool, request_sha256, response_sha256}` records. The evidence bundle contains the exact request/result bytes, ordered tool records, effective policy bundle, model gateway record, and code checkout/image metadata. Secrets may be replaced by typed redaction commitments before hashing; the redaction manifest is part of the bundle. + +## Why each field survives minimization + +- `action` binds the receipt to the consequential effect rather than merely to an agent run. +- `model.id` plus `model.invocation` lets the recorder correlate the claim with gateway/provider evidence. A model string alone proves nothing. +- `tools.trace_sha256` commits to the complete ordered tool path without inflating the receipt. +- `policy.bundle_sha256` identifies the policy actually loaded, including system/developer/project rules after assembly—not just a policy version label. +- `code.commit` and `entrypoint` identify both source and the invoked path. For non-Git execution, `entrypoint` must be an immutable image/module digest. +- `evidence.uri` makes all commitments independently retrievable and rejects a receipt whose evidence was discarded. +- `attestation` establishes who observed these facts and detects mutation. + +Removing any one of these leaves one requested claim—actual model, tool, policy, code path, or action—unbound or unverifiable. + +## Recorder requirements + +1. The recorder is outside the agent process and intercepts model calls, tool dispatch/results, policy assembly, and the final action result. +2. It derives model identity from the authenticated gateway/provider response, never from assistant text. +3. It hashes exact bytes with media type and canonicalization rules recorded in the evidence bundle. +4. It emits the receipt only after observing the consequential result and stores the evidence bundle under its digest. +5. Its signing key is hardware- or service-isolated from agent-controlled tools. Key metadata states issuer, validity interval, and revocation status. + +## Verification algorithm + +A verifier MUST: + +1. Resolve a trusted key for `attestation.issuer` and `key_id`; reject revoked or out-of-validity keys. +2. Recreate RFC 8785 canonical bytes without `attestation.sig` and verify Ed25519. +3. Fetch `evidence.uri`, hash it, and require equality with the URI digest. +4. Recompute the action request/result, ordered tool trace, and effective policy hashes from evidence. +5. Verify the model invocation against authenticated gateway/provider evidence. +6. Resolve the immutable code identity and require that `entrypoint` belongs to that checkout/image. +7. Apply action-specific checks (for example, read Gitea state back and compare it with `result_sha256`). + +Any missing evidence or unsupported identity yields **unverified**, not partial success. + +## Threat boundary + +This receipt proves only what the trusted recorder observed. It does not prove the provider ran the advertised weights unless the provider supplies a separately trusted model attestation, and it cannot survive compromise of both recorder and signing key. Those limitations must be reported explicitly rather than papered over with transcript hashes. + +## Adversarial-review questions + +1. Can the `model.invocation` be replayed or bound to a different request? +2. Is one `policy.bundle_sha256` sufficient to represent dynamic policy/tool authorization changes during the run? +3. What canonical evidence-bundle format permits selective disclosure without making redactions ambiguous? +4. Should code provenance require a signed build/SLSA subject instead of a Git commit for production actions? +5. Which action-specific freshness field (trusted timestamp, nonce, or transparency-log index) is the minimum needed to prevent receipt replay?