Compare commits
No commits in common. "timmy/3-path-proof-receipt" and "main" have entirely different histories.
timmy/3-pa
...
main
|
|
@ -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?
|
||||
Loading…
Reference in New Issue
Block a user