Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/action-receipts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ three operations:
| `07-receipt-chain-gap.json` | `receipt_invalid` | unknown | The receipt does not link to the expected predecessor. |
| `08-same-party-self-report.json` | `receipt_valid_accepted` with warning | `accepted` | The evidence verifies, but the issuer is not independent from the gateway. |
| `09-unsupported-physical-completion.json` | `receipt_invalid` | unknown | Base TRACE cannot verify the asserted physical-completion claim. |
| `10-action-ref-not-recomputable.json` | `receipt_invalid` | unknown | The declared `action_ref` is not the digest of its own preimage, so it binds nothing. |
| `11-call-id-mismatch.json` | `receipt_invalid` | unknown | An authentic receipt bound to a different call. |
| `12-session-id-mismatch.json` | `receipt_invalid` | unknown | An authentic receipt from a different session. |
| `13-evidence-hash-mismatch.json` | `receipt_invalid` | unknown | The receipt is authentic but the detached evidence was swapped after signing. |
| `14-receipt-issuer-key-untrusted.json` | `receipt_invalid` | unknown | The issuer key is not in the verifier's pinned set. |
| `15-receipt-from-future.json` | `receipt_invalid` | unknown | Issued after the verification time, so an upper bound on age never rejects it. |
| `16-decision-not-in-enum.json` | `receipt_invalid` | unknown | An unrecognised decision verb, which must not read as accept or reject. |

Fixtures `10`–`16` each pin down one rule that the verifier applies and that no fixture
previously exercised. Every one was a check a conforming implementation could have
omitted entirely while passing this set. Two matter beyond tidiness: without
`issuer_key_untrusted` a receipt authenticates itself, and without
`evidence_hash_mismatch` the signature covers a digest whose document may have been
replaced. They pin their own deterministic test key, since the private half of the key
used by `01`–`09` is not published; `gen_rule_coverage_vectors.py` regenerates them
byte-for-byte and only public JWKs appear in the files.

`tests/test_action_receipt_fixtures.py` recomputes each digest, verifies each
signature against the pinned key, checks session and call binding, enforces
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "action-ref-not-recomputable",
"description": "The action's declared action_ref is not the digest of its own canonical preimage. A verifier that trusts the declared value instead of recomputing it would accept an action reference that binds nothing.",
"profile": "trace.action_receipt.conformance.v0",
"context": {
"call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"require_receipt": true,
"verification_time": "2026-07-06T15:24:00Z",
"max_receipt_age_seconds": 300,
"expected_previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
},
"action": {
"agent_id": "spiffe://factory.example/agent/ros2-fibonacci/dev",
"action_type": "ros2.action.example_interfaces/Fibonacci",
"action_scope": "/abort_fibonacci_process",
"action_timestamp": "2026-07-06T15:22:13Z",
"action_ref": "sha256:dededededededededededededededededededededededededededededededede"
},
"trusted_issuer_keys": {
"did:web:factory.example:safety-controller#ed25519-coverage-2026q3": {
"kty": "OKP",
"crv": "Ed25519",
"x": "GUrISuzIssGcot1U1RFv02lETpxCYoQ0tz421ALSdqE"
}
},
"evidence": {
"terminal_state": "accepted",
"physical_completion_claim": "none",
"completeness_claim": "not_proven"
},
"receipt": {
"issuer": "did:web:factory.example:safety-controller",
"issuer_key_id": "did:web:factory.example:safety-controller#ed25519-coverage-2026q3",
"issuer_independence": "separate_process",
"linked_call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"action_ref": "sha256:dededededededededededededededededededededededededededededededede",
"evidence_type": "application/vnd.agentrust.action-receipt+json",
"evidence_hash": "sha256:cf43260df01d89b5ce9ed0dce484ae1e3331fb477efda92cd0fd9365df9089d7",
"previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1",
"issued_at": "2026-07-06T15:22:15Z",
"decision": "accepted",
"signature": "Cbe4Tbr1XBZLqwnLTlel_ABjjQecA5-E0ygcI9qMP9xf0z8CmMKrGL8uIZqBlV3bKE4CY2IJRUH_WCoMnn2_BQ"
},
"expected": {
"status": "receipt_invalid",
"controller_outcome": "unknown",
"failures": [
"action_ref_invalid"
],
"warnings": []
}
}
54 changes: 54 additions & 0 deletions examples/action-receipts/conformance/11-call-id-mismatch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "call-id-mismatch",
"description": "An authentic receipt bound to a different call. Without this check a valid receipt from one call could be presented as evidence for another.",
"profile": "trace.action_receipt.conformance.v0",
"context": {
"call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"require_receipt": true,
"verification_time": "2026-07-06T15:24:00Z",
"max_receipt_age_seconds": 300,
"expected_previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
},
"action": {
"agent_id": "spiffe://factory.example/agent/ros2-fibonacci/dev",
"action_type": "ros2.action.example_interfaces/Fibonacci",
"action_scope": "/abort_fibonacci_process",
"action_timestamp": "2026-07-06T15:22:13Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034"
},
"trusted_issuer_keys": {
"did:web:factory.example:safety-controller#ed25519-coverage-2026q3": {
"kty": "OKP",
"crv": "Ed25519",
"x": "GUrISuzIssGcot1U1RFv02lETpxCYoQ0tz421ALSdqE"
}
},
"evidence": {
"terminal_state": "accepted",
"physical_completion_claim": "none",
"completeness_claim": "not_proven"
},
"receipt": {
"issuer": "did:web:factory.example:safety-controller",
"issuer_key_id": "did:web:factory.example:safety-controller#ed25519-coverage-2026q3",
"issuer_independence": "separate_process",
"linked_call_id": "01986d7c-0000-7c68-9ff8-000000000000",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034",
"evidence_type": "application/vnd.agentrust.action-receipt+json",
"evidence_hash": "sha256:cf43260df01d89b5ce9ed0dce484ae1e3331fb477efda92cd0fd9365df9089d7",
"previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1",
"issued_at": "2026-07-06T15:22:15Z",
"decision": "accepted",
"signature": "X-kzP-GgEpcy30o8jzylNKN_QU86WldO4vN9mHziBnuOo_vGKio1_oRa1voVQWN396wQ82HCMH3vTTm9aVrKCg"
},
"expected": {
"status": "receipt_invalid",
"controller_outcome": "unknown",
"failures": [
"call_id_mismatch"
],
"warnings": []
}
}
54 changes: 54 additions & 0 deletions examples/action-receipts/conformance/12-session-id-mismatch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "session-id-mismatch",
"description": "An authentic receipt from a different session. Session binding is what stops a receipt being replayed into an unrelated run.",
"profile": "trace.action_receipt.conformance.v0",
"context": {
"call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"require_receipt": true,
"verification_time": "2026-07-06T15:24:00Z",
"max_receipt_age_seconds": 300,
"expected_previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
},
"action": {
"agent_id": "spiffe://factory.example/agent/ros2-fibonacci/dev",
"action_type": "ros2.action.example_interfaces/Fibonacci",
"action_scope": "/abort_fibonacci_process",
"action_timestamp": "2026-07-06T15:22:13Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034"
},
"trusted_issuer_keys": {
"did:web:factory.example:safety-controller#ed25519-coverage-2026q3": {
"kty": "OKP",
"crv": "Ed25519",
"x": "GUrISuzIssGcot1U1RFv02lETpxCYoQ0tz421ALSdqE"
}
},
"evidence": {
"terminal_state": "accepted",
"physical_completion_claim": "none",
"completeness_claim": "not_proven"
},
"receipt": {
"issuer": "did:web:factory.example:safety-controller",
"issuer_key_id": "did:web:factory.example:safety-controller#ed25519-coverage-2026q3",
"issuer_independence": "separate_process",
"linked_call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T09:00:00Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034",
"evidence_type": "application/vnd.agentrust.action-receipt+json",
"evidence_hash": "sha256:cf43260df01d89b5ce9ed0dce484ae1e3331fb477efda92cd0fd9365df9089d7",
"previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1",
"issued_at": "2026-07-06T15:22:15Z",
"decision": "accepted",
"signature": "lvIqDgwunh5ou0AIwNuJyDIXGYv7hGH_-S2KuAZzB8nwnER-_ee2QuTzTspT4zlVCRfA_O9UiUFkfFBGLt4rBA"
},
"expected": {
"status": "receipt_invalid",
"controller_outcome": "unknown",
"failures": [
"session_id_mismatch"
],
"warnings": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "evidence-hash-mismatch",
"description": "The receipt is authentic but its evidence_hash does not match the detached evidence supplied with it. The signature covers the digest, not the document, so only recomputation catches a swapped evidence body.",
"profile": "trace.action_receipt.conformance.v0",
"context": {
"call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"require_receipt": true,
"verification_time": "2026-07-06T15:24:00Z",
"max_receipt_age_seconds": 300,
"expected_previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
},
"action": {
"agent_id": "spiffe://factory.example/agent/ros2-fibonacci/dev",
"action_type": "ros2.action.example_interfaces/Fibonacci",
"action_scope": "/abort_fibonacci_process",
"action_timestamp": "2026-07-06T15:22:13Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034"
},
"trusted_issuer_keys": {
"did:web:factory.example:safety-controller#ed25519-coverage-2026q3": {
"kty": "OKP",
"crv": "Ed25519",
"x": "GUrISuzIssGcot1U1RFv02lETpxCYoQ0tz421ALSdqE"
}
},
"evidence": {
"terminal_state": "rejected",
"physical_completion_claim": "none",
"completeness_claim": "not_proven"
},
"receipt": {
"issuer": "did:web:factory.example:safety-controller",
"issuer_key_id": "did:web:factory.example:safety-controller#ed25519-coverage-2026q3",
"issuer_independence": "separate_process",
"linked_call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034",
"evidence_type": "application/vnd.agentrust.action-receipt+json",
"evidence_hash": "sha256:cf43260df01d89b5ce9ed0dce484ae1e3331fb477efda92cd0fd9365df9089d7",
"previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1",
"issued_at": "2026-07-06T15:22:15Z",
"decision": "accepted",
"signature": "f1bVg8ZaBv6SaT5JGzs0J4XkzyVkvhS5wGNit-790NFeHAGLfw1kalsiqBxGC8HFNUVjHRcW2urOPl8A90sbDw"
},
"expected": {
"status": "receipt_invalid",
"controller_outcome": "unknown",
"failures": [
"evidence_hash_mismatch"
],
"warnings": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "receipt-issuer-key-untrusted",
"description": "The receipt names an issuer key the verifier has not pinned. A signature verifies against whatever key it names; only a pinned set decides whether that key was ever entitled to speak.",
"profile": "trace.action_receipt.conformance.v0",
"context": {
"call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"require_receipt": true,
"verification_time": "2026-07-06T15:24:00Z",
"max_receipt_age_seconds": 300,
"expected_previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
},
"action": {
"agent_id": "spiffe://factory.example/agent/ros2-fibonacci/dev",
"action_type": "ros2.action.example_interfaces/Fibonacci",
"action_scope": "/abort_fibonacci_process",
"action_timestamp": "2026-07-06T15:22:13Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034"
},
"trusted_issuer_keys": {
"did:web:factory.example:safety-controller#ed25519-some-other-key": {
"kty": "OKP",
"crv": "Ed25519",
"x": "GUrISuzIssGcot1U1RFv02lETpxCYoQ0tz421ALSdqE"
}
},
"evidence": {
"terminal_state": "accepted",
"physical_completion_claim": "none",
"completeness_claim": "not_proven"
},
"receipt": {
"issuer": "did:web:factory.example:safety-controller",
"issuer_key_id": "did:web:factory.example:safety-controller#ed25519-coverage-2026q3",
"issuer_independence": "separate_process",
"linked_call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034",
"evidence_type": "application/vnd.agentrust.action-receipt+json",
"evidence_hash": "sha256:cf43260df01d89b5ce9ed0dce484ae1e3331fb477efda92cd0fd9365df9089d7",
"previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1",
"issued_at": "2026-07-06T15:22:15Z",
"decision": "accepted",
"signature": "f1bVg8ZaBv6SaT5JGzs0J4XkzyVkvhS5wGNit-790NFeHAGLfw1kalsiqBxGC8HFNUVjHRcW2urOPl8A90sbDw"
},
"expected": {
"status": "receipt_invalid",
"controller_outcome": "unknown",
"failures": [
"issuer_key_untrusted"
],
"warnings": []
}
}
54 changes: 54 additions & 0 deletions examples/action-receipts/conformance/15-receipt-from-future.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "receipt-from-future",
"description": "The receipt is issued after the verification time. A future-dated receipt is not stale, so the freshness ceiling alone never rejects it, and a verifier that only checks an upper bound on age accepts it.",
"profile": "trace.action_receipt.conformance.v0",
"context": {
"call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"require_receipt": true,
"verification_time": "2026-07-06T15:24:00Z",
"max_receipt_age_seconds": 300,
"expected_previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
},
"action": {
"agent_id": "spiffe://factory.example/agent/ros2-fibonacci/dev",
"action_type": "ros2.action.example_interfaces/Fibonacci",
"action_scope": "/abort_fibonacci_process",
"action_timestamp": "2026-07-06T15:22:13Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034"
},
"trusted_issuer_keys": {
"did:web:factory.example:safety-controller#ed25519-coverage-2026q3": {
"kty": "OKP",
"crv": "Ed25519",
"x": "GUrISuzIssGcot1U1RFv02lETpxCYoQ0tz421ALSdqE"
}
},
"evidence": {
"terminal_state": "accepted",
"physical_completion_claim": "none",
"completeness_claim": "not_proven"
},
"receipt": {
"issuer": "did:web:factory.example:safety-controller",
"issuer_key_id": "did:web:factory.example:safety-controller#ed25519-coverage-2026q3",
"issuer_independence": "separate_process",
"linked_call_id": "01986d7c-6b2f-7c68-9ff8-3e2f9d0db337",
"session_id": "trace-session-2026-07-06T15:22:11Z",
"action_ref": "sha256:c2e97956de8b86d825780fcecea601a6eea02616855e6f0aa19d10bd351fe034",
"evidence_type": "application/vnd.agentrust.action-receipt+json",
"evidence_hash": "sha256:cf43260df01d89b5ce9ed0dce484ae1e3331fb477efda92cd0fd9365df9089d7",
"previous_receipt_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1",
"issued_at": "2026-07-06T15:26:00Z",
"decision": "accepted",
"signature": "RAYxeAudhBQ9tiJ-XspITdRS7CgZE49pY13yLi9az8QR3UpmWcX5JJKah4rSvmq93DojaT4ODzLpM6tA0v4GCA"
},
"expected": {
"status": "receipt_invalid",
"controller_outcome": "unknown",
"failures": [
"receipt_from_future"
],
"warnings": []
}
}
Loading
Loading