docs(crosswalks): Acta decision receipts as a second action-receipt profile#98
docs(crosswalks): Acta decision receipts as a second action-receipt profile#98tomjwxf wants to merge 4 commits into
Conversation
…rofile Non-normative cross-walk, following the acceptance bar from agentrust-io#97: Acta (draft-farley-acta-signed-receipts-02) is described as external evidence composed with TRACE's existing 3.3.2 action-receipt pattern, not a schema change to the Trust Record. Field mapping table uses exact TRACE and Acta field names. Boundary section restates 2.4's scope limits for the software (non-embodied) case explicitly. Five real conformance fixtures (examples/action-receipts/acta/), generated by an actual Ed25519 signer over JCS-canonical bytes, not hand-typed: valid accepted, valid denied (the negative-outcome case agentrust-io#95 asks for), signature/ key mismatch, broken chain, and stale policy_digest (valid signature, stale authorization binding -- a distinct check from cryptographic verification). Generator script included; every fixture re-verified against its stated result before commit.
|
🔴 Contributor Check: HIGH
Automated check by AgenTrust Contributor Check. |
…ce bar 1. Add the mismatched session/call binding fixture the acceptance bar explicitly lists (06-session-binding-mismatch.json): a validly signed receipt presented under the wrong session. Passes signature verification, fails the binding check; pairs with 05 to show that signature validity alone accepts genuine receipts in the wrong context. 2. Make the digest/chain-head reference concrete: new section showing an illustrative Trust Record evidence entry (chain head + issuer/key metadata, no embedded schema), with the chain head being the real, recomputable digest of the fixture chain's latest receipt. Also: parent_receipt_hash now consistently sha256:-prefixed (the real link in 02 previously used bare hex while the tampered value in 04 was prefixed); 02 and 04 regenerated and re-verified accordingly.
carloshvp
left a comment
There was a problem hiding this comment.
Requesting changes
- The fixtures are not
draft-farley-acta-signed-receipts-02envelopes, despite being presented as exact/independently verifiable Acta fixtures. Draft-02 §2 requires a top-level{ "payload": {...}, "signature": { "alg", "kid", "sig" } }envelope, with common fields such astype,issued_at, andissuer_idinsidepayload; §3.1 defines the access-decision fields astype: "protectmcp:decision",tool_name,decision,reason, etc. These fixtures instead putv,type,algorithm,kid,issuer, andissued_atat the top level, use a bare signature string, and place a different custom shape underpayload. A conforming draft-02 verifier cannot decode them, so the cross-walk's field mapping and the README's independent-verification claim are currently misleading.
Please regenerate the fixtures using the draft-02 envelope and field names (or explicitly define this as a separate profile rather than claiming draft-02 conformance), then update the mapping and verifier instructions accordingly. Add an automated check that parses each fixture against the claimed Acta schema and verifies the expected positive/negative result; otherwise this drift can recur while the repository's Python tests still pass.
Validation: compared the PR diff and generated fixtures against the published draft-02 receipt structure and access-decision schema; repository Python 3.11/3.12 and CodeQL checks pass, while the contributor gate currently fails. I did not run an Acta verifier because the PR does not add one and the envelopes fail schema inspection before signature verification.
carloshvp
left a comment
There was a problem hiding this comment.
Requesting changes
The cross-walk direction is useful, but two fixtures do not currently exercise the verifier obligations they claim to cover:
-
examples/action-receipts/acta/03-signature-key-mismatch.jsonis byte-for-byte identical to01-valid-accepted.json. Following the README's independent-verification steps with the publishedsigner-public-key.txt, fixture 03 verifies successfully. The unrelated key exists only transiently insidegen.mjs, so the committed fixture set gives an independent verifier no mismatched key or key-resolution input with which to reproduce the advertised failure. Please commit the mismatched public-key/key-set context (or encode a resolvablekidmismatch) and make the verification instructions deterministic. -
04-broken-chain.jsonchangesparent_receipt_hashafter signing, so it only proves that payload tampering invalidates an Ed25519 signature. It does not test chain verification. A valid issuer can sign a receipt whoseparent_receipt_hashdoes not match the actual predecessor; that signature must pass while the separate chain-link check fails. Please generate fixture 04 by signing an intentionally wrong parent hash, then verify both conditions independently: signature valid, predecessor digest mismatch.
Validation performed on head 82e56463aaf0a641933d93a68d891d4066573cbd:
- Independent Ed25519 verification over compact, recursively key-sorted payload JSON with the committed signer public key:
01PASS,03PASS,04FAIL,05PASS,06PASS. - Confirmed
01-valid-accepted.jsonand03-signature-key-mismatch.jsonare byte-identical.
I stopped at these conformance blockers because they need to be corrected before the fixtures can support the cross-walk's interoperability claims.
… make every negative case a committed, reproducible artifact Addresses both change-request reviews on agentrust-io#98: 1. Envelope conformance: fixtures are now actual draft-02 receipts: { payload, signature: { alg: EdDSA, kid, sig } }, access-decision payload per s3.1 (type protectmcp:decision, tool_name, decision, reason, policy_digest, opaque session_id), issuer_id matching signature.kid, kid in the s2.1.1 recommended sb:issuer:<base58-12> format, PureEdDSA over JCS(payload) per s5.6, and previousReceiptHash as the bare-hex s5.7 hash over the predecessor's ENTIRE envelope including its signature. The crosswalk doc and field mapping are rewritten against the real draft field names. 2. Fixture 03 is no longer byte-identical to 01: it is signed by a second, committed key (mismatched-signer-public-key.txt) while claiming the signer's kid, so resolving kid to signer-public-key.txt fails deterministically from committed bytes alone. 3. Fixture 04 now tests chain verification, not signature tampering: it is VALIDLY SIGNED over a wrong previousReceiptHash (a real receipt hash, just the wrong predecessor's), so the signature check passes and only recomputation against the actual predecessor fails. The docs no longer claim chain and signature failures collapse; under s5.7 they are distinct checks, and 04 is the fixture that proves it. 4. Automated check: tests/test_acta_fixtures.py verifies every fixture's structure against the draft-02 envelope and its expected positive/negative result from the new machine-readable expected.json, using only existing project dependencies (rfc8785 for JCS, cryptography for Ed25519). Full suite passes locally (97 tests). This also cross-validates the JS generator's canonicalization against the Python RFC 8785 reference implementation.
|
@carloshvp both reviews were correct, and thank you for validating against the published draft rather than taking the PR's description at its word. All three findings are fixed in 9053f36: 1. Envelope conformance. You were right: the fixtures used an internal implementation shape, not the draft-02 envelope. They are now conforming receipts: 2. Fixture 03. Now signed by a second, committed key ( 3. Fixture 04. Your framing was the correct one and the docs now say so: it is validly signed over a wrong Automated check. On the |
carloshvp
left a comment
There was a problem hiding this comment.
Comment review
Re-reviewed current head 9053f367cb7eec1d1324e94a77966d8c8f21b6b7. This update resolves the findings from both earlier change-request reviews:
- The fixtures now use the draft-02
{payload, signature}envelope and access-decision field names. - Fixture 03 is signed by the committed second key while retaining the claimed
kid; verification with the resolved signer key fails deterministically. - Fixture 04 is validly signed but points to the wrong predecessor; signature verification passes and the independent chain-link check fails.
tests/test_acta_fixtures.pynow checks envelope structure and the declared signature, chain, policy-freshness, and session-binding outcomes.
Validated locally:
uv run --project /tmp/trace-spec-pr98 --extra dev python -m pytest -q tests/test_acta_fixtures.py→18 passed.uv run --project /tmp/trace-spec-pr98 --extra dev python -m pytest -q tests→97 passed.- GitHub checks on this head: Python 3.11, Python 3.12, CodeQL, and Python analysis pass. The remaining failing
gatecheck is the maintainer-approval gate.
No further blocker from my re-review. This single follow-up supersedes and consolidates my two earlier reviews.
imran-siddique
left a comment
There was a problem hiding this comment.
Comment, not a block. This needs a maintainer decision (cc @carloshvp) on one governance point.
Technically the crosswalk is accurate and correctly non-normative: the anchors resolve, the field names match the fixtures, the six Ed25519 fixtures self-verify in CI, and the schema is untouched per #97.
The decision before merge: the crosswalk elevates a non-working-group individual I-D (draft-farley-acta-signed-receipts-02) and points to a company-owned test-vector repo as "the shared conformance suite," framed as a peer profile. The PR does not disclose that the submitter is the draft's author. Whether trace-spec wants to give an external individual draft peer standing is a maintainer call.
Two tightening items if it proceeds:
evidence_hash -> payload_digestmaps to a field present in none of the six fixtures, so it is asserted, not demonstrated.- Acta has no per-call identifier, so the
linked_call_idobligation from #97 is only partially satisfiable; call it a gap rather than a clean mapping.
Minor: "exact TRACE terms as used in section 3.3.2" overstates it; 3.3.2 is prose and the field names live only in the informative examples README.
Addresses the acceptance bar @carloshvp set in #97, one point at a time:
Done as a cross-walk doc (
docs/crosswalks/acta-decision-receipts.md), following the exact pattern of the existing OWASP cross-walk: non-normative, no schema/wire-format change, registered inmkdocs.ymlthe same way. Acta stays an external, independently specified format (draft-farley-acta-signed-receipts, rev 02); nothing here touchesschema/trace-claim.json. The doc includes a concrete (illustrative, non-normative) Trust Record evidence entry showing exactly that shape: chain head + issuer/key metadata, where the chain head is the real, recomputable digest of the fixture chain's latest receipt.The field-mapping table walks the exact shape from spec §3.3.2 /
examples/action-receipts/README.md(issuer, issuer_key_id, linked_call_id/session_id, evidence_hash, previous_receipt_hash, decision, signature) against the exact Acta envelope fields, and restates the five verifier checks from the embodied profile for this one, plus a sixth (policy_digestfreshness) that is not a signature check and should not be conflated with one.Six fixtures in
examples/action-receipts/acta/, all real (an actual Ed25519 signer over JCS-canonical bytes, not hand-typed JSON; generator included): valid accepted, valid denied (the rejection case), signature/key mismatch, broken chain, stale policy_digest, and mismatched session/call binding. The last two are deliberately receipts whose signatures verify cleanly but which fail a non-cryptographic check (stale authorization basis, wrong session), to keep those distinctions explicit rather than folding them into the crypto check.Restated directly against §2.4 and the #34 external-execution-evidence boundary, generalized to the software (non-embodied) case this profile covers.
Every fixture and every doc-internal anchor link was checked against this repo's actual spec headings and fixture format before commit, not assumed. Happy to adjust anything that does not fit the maintainers' preferred shape for this.