Compare commits

..

No commits in common. "timmy/3-path-proof-receipt" and "main" have entirely different histories.

View File

@ -1,98 +0,0 @@
# 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": "<sha256-of-exact-action-request>",
"result_sha256": "<sha256-of-exact-observed-result>"
},
"path": {
"model": {
"id": "<provider/model-version>",
"invocation": "<provider-or-gateway-invocation-id>"
},
"tools": {
"count": 1,
"trace_sha256": "<sha256-of-canonical-ordered-tool-events>"
},
"policy": {
"bundle_sha256": "<sha256-of-effective-policy-bundle>"
},
"code": {
"repo": "<immutable-repository-identity>",
"commit": "<full-commit-id>",
"entrypoint": "<path-or-image-digest>"
}
},
"evidence": {
"uri": "sha256:<content-addressed-evidence-bundle-digest>"
},
"attestation": {
"issuer": "<runtime-recorder-identity>",
"key_id": "<verification-key-id>",
"alg": "Ed25519",
"sig": "<base64url-signature>"
}
}
```
`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?