Skip to content

feat(predicates): Decision Receipt predicate for agent governance attestations#549

Closed
tomjwxf wants to merge 3 commits into
in-toto:mainfrom
tomjwxf:feat/decision-receipt-predicate
Closed

feat(predicates): Decision Receipt predicate for agent governance attestations#549
tomjwxf wants to merge 3 commits into
in-toto:mainfrom
tomjwxf:feat/decision-receipt-predicate

Conversation

@tomjwxf
Copy link
Copy Markdown

@tomjwxf tomjwxf commented Apr 16, 2026

Summary

Proposes a new predicate type for attesting to access control decisions made by AI agents and physical sensor devices.

predicateType: `https://veritasacta.com/attestation/decision-receipt/v0.1\`

Motivation

AI agents are increasingly making consequential decisions (invoking tools, accessing APIs, controlling physical actuators). There is no existing in-toto predicate for attesting to these decisions with policy evidence. The closest predicates are:

  • SLSA Provenance: attests to how an artifact was built, not what decisions governed its creation
  • Simple Verification Result: attests to whether an artifact passed a policy, but doesn't capture the decision chain or policy digest
  • Runtime Trace: captures execution traces, but not authorization decisions with policy evidence

The Decision Receipt predicate fills this gap: what decision was made, by whom, under what policy, with what evidence, and how it chains to previous decisions.

Why in-toto, not a custom format

This predicate was suggested by @Hayden-IO in sigstore/rekor#2798, where we demonstrated Ed25519-signed decision receipts anchored in Rekor via DSSE. Hayden noted that in-toto attestations (`application/vnd.in-toto+json`) track publicly used predicate types, which is a better fit than a vendor-specific DSSE payload type.

Moving from `application/vnd.scopeblind.receipt+json` to an in-toto predicate means:

  • Receipts slot into the existing sigstore/in-toto/SLSA verification pipeline
  • Standard tooling (cosign, rekor-cli, in-toto verifiers) can process them
  • The predicate type is discoverable in this registry

What the predicate captures

Field Type Description
`decision` string allow, deny, or alert
`reason` string Human-readable explanation
`policyId` string Which policy governed the decision
`policyDigest` DigestSet SHA-256 of the policy file at evaluation time
`issuedAt` Timestamp When the decision was made
`sequence` integer Position in the receipt chain
`previousReceiptDigest` DigestSet Hash link to the previous receipt (tamper-evident chain)
`issuerId` string Who signed (key fingerprint or DID)
`outputDigest` DigestSet Digest of the action output
`metadata` object Optional: deviceId, sessionId, framework

Two domains, one schema

The same predicate works for both software agents and physical devices:

Software: An AI agent invokes a Bash tool. The policy allows it. A decision receipt is signed by the agent host.

Physical: A cold chain sensor reads 22.4C (above the 18C limit). The policy denies shipment release. A decision receipt is signed by the device's secure element.

Both produce the same `predicateType`, verify with the same tooling, and can be anchored in the same Rekor log.

Three worked examples included

  1. Software agent tool call (allow) with protect-mcp framework metadata
  2. Physical sensor reading (deny) with device ID and cold chain policy
  3. Session summary anchored in Sigstore Rekor with log index

Existing implementations

The receipt format is already production-deployed across multiple ecosystems:

Follows the template

Spec follows `spec/predicates/template/template.md` with all required sections: Purpose, Use Cases, Prerequisites, Model, Schema, Parsing Rules, Fields, Examples, Changelog.

Using `https://veritasacta.com/\` namespace for v0.1. Happy to discuss promotion to `https://in-toto.io/attestation/\` namespace if the predicate is vetted.

cc @Hayden-IO (context from rekor#2798)

@tomjwxf tomjwxf requested a review from a team as a code owner April 16, 2026 08:10
Proposes a new predicate type for attesting to access control decisions
made by AI agents and physical sensor devices.

predicateType: https://veritasacta.com/attestation/decision-receipt/v0.1

The predicate captures: decision outcome (allow/deny/alert), the policy
that governed it (id + digest), input/output evidence hashes, and
hash-chain links to previous receipts for tamper-evident ordering.

Designed for two domains using the same schema:
- Software agent tool calls (MCP, LangChain, Pydantic AI, etc.)
- Physical sensor readings (cold chain, safety equipment, provenance)

Three worked examples included:
1. Software agent tool call (allow)
2. Physical sensor reading (deny due to temperature excursion)
3. Session summary anchored in Sigstore Rekor

Existing implementations:
- protect-mcp (npm, 10K+ monthly downloads) -- reference implementation
- @veritasacta/verify (npm, Apache-2.0) -- offline verification CLI
- Microsoft AGT (3 PRs merged) -- enterprise consumer
- AWS Cedar for Agents (2 PRs merged) -- policy engine WASM bindings
- Sigstore Rekor (issue #2798) -- transparency log anchoring via DSSE
- 8 framework adapters across npm and PyPI
- IETF draft-farley-acta-signed-receipts -- wire format specification

Follows the template at spec/predicates/template/template.md.

Signed-off-by: tommylauren <[email protected]>
@tomjwxf tomjwxf force-pushed the feat/decision-receipt-predicate branch from 6104081 to d471f84 Compare April 16, 2026 09:10
tomjwxf pushed a commit to tomjwxf/agent-governance-toolkit that referenced this pull request Apr 17, 2026
…itives

Four additions landing after the initial PR:

1. Receipt Lifecycle ASCII diagram in "The Receipt Format" section.
   Visualizes mint → JCS canonical → Ed25519 sign → store → verify so
   readers can see why the determinism invariant holds.

2. A real Cedar policy block in §4 (Composing with Cedar Policies).
   Previously the section described the CedarDecision API shape without
   showing what a policy producing one actually looks like. Now shows a
   10-line permit/forbid policy and links out to cedar-for-agents for
   the full schema generator.

3. Neutral anchoring primitives subsection in §6 (Cross-Implementation).
   Names Sigstore Rekor and in-toto attestations as the cross-org
   verification fabric beyond the four implementations. References
   sigstore/rekor#2798 and in-toto/attestation#549.

4. New §7 "Emitting Receipts as SLSA Provenance". When an AI agent is
   itself the builder, the receipt chain IS the per-step build log.
   Shows the exact byproducts JSON shape for carrying a receipt chain
   inside a SLSA provenance v1 attestation, referencing the draft
   agent-commit build type at refs.arewm.com/agent-commit/v0.1 and the
   active slsa-framework/slsa#1594 and microsoft#1606 discussions.

No new dependencies. All APIs still verified against the merged
examples/protect-mcp-governed/ and examples/physical-attestation-governed/
reference code.
tomjwxf pushed a commit to tomjwxf/refs.arewm.com that referenced this pull request Apr 17, 2026
Implements the pattern you described in issue arewm#1 and in SLSA #1594:
additional attestations about the same subject, produced by identities
distinct from the builder, are referenced via ResourceDescriptor entries
in byproducts rather than inlined under the builder's signature.

Changes (all additive, no breaking changes to v0.2 observation schema):

1. New subsection "Referenced attestations" in the Byproducts section,
   describing the digest + URI + annotations.predicateType shape and the
   three-step verifier flow for fetching and checking referenced attestations
   independently of the builder's signature.

2. A decision-receipts ResourceDescriptor entry added to the existing
   coordinator + implementer JSON example, alongside the observed-* entries.
   Shows the canonical shape:
     name, digest, uri, annotations.{predicateType, signerRole, ...}
   Uses the in-toto predicate type URI proposed at in-toto/attestation#549.

3. A parsing-rule clarifying that byproduct entries carrying both `digest`
   and `uri` SHOULD be routed as ResourceDescriptor references rather than
   as inline observation data, with the predicateType used for verifier
   routing when present.

4. Design Rationale subsection "Why additional attestations use
   ResourceDescriptor references", explaining trust-domain separation:
   inlining would imply content-level verification the builder cannot do;
   cross-signing would obscure the original signer's identity; the
   reference pattern keeps the buildType composable as new attestation
   types emerge (decision receipts, behavioral telemetry, compliance).

5. Changelog entry for this update, explicitly noted as additive.

Motivated by your comment on issue arewm#1:
  "best potential way to integrate additional attestations might be to
  add it to byproducts, but not in-lining those attestation properties.
  If the supervisor is aware of these additional attestations then it
  could potentially insert a ResourceDescriptor in byproducts with a
  digest and URI pointing to where the attestation is stored."

The "decision-receipts" byproduct in the example references an in-toto
attestation signed by a supervisor-hook identity, matching the
"who makes the claim" separation you asked about in SLSA #1594.

Happy to iterate on naming (e.g., the `signerRole` key), on the exact
predicate type URI (in-toto/attestation#549 is still in review), or to
split this into a v0.3 tag if you would rather keep v0.2 frozen at
timestamp-only.

Refs:
  arewm#1
  slsa-framework/slsa#1594
  in-toto/attestation#549
…escriptor

Aligns this predicate with the agent-commit SLSA build type so that a SLSA
provenance attestation and a Decision Receipt attestation about the same
subject compose cleanly without cross-signing trust domains.

Additions:

- New "Relationship to SLSA Provenance" section explaining that the receipt
  signer (supervisor-hook identity) is deliberately distinct from the SLSA
  provenance signer (builder-platform identity), and that composition happens
  via a ResourceDescriptor reference in the SLSA byproducts, not by inlining
  the receipt content inside the SLSA envelope.

- A worked example of the byproduct entry as it appears in an agent-commit
  provenance, including `predicateType`, `signerRole: "supervisor-hook"`,
  chain length, and genesis/final receipt hashes. Pattern tracks the
  companion PR at arewm/refs.arewm.com#2 (agent-commit v0.2 update).

- Three-step verifier flow for consuming both attestations: verify SLSA
  signature against builder identity; fetch, digest-check, and verify the
  referenced receipt attestation against the supervisor identity named in
  `issuerId`; cross-reference subjects and interpret the chain per this
  predicate's semantics.

- Clarification that `issuerId` (concrete signing identity) and the SLSA
  byproduct's `signerRole` annotation (logical role of that identity
  relative to the builder) are complementary fields, not duplicative.

- Changelog and References updated to point at the agent-commit build type
  and slsa-framework/slsa#1594.

No changes to the predicate schema or field semantics. Purely additive
documentation on how this predicate composes with SLSA.

Refs:
  arewm/refs.arewm.com#1, arewm/refs.arewm.com#2 (agent-commit composition)
  slsa-framework/slsa#1594 (SLSA-for-agents)
@tomjwxf
Copy link
Copy Markdown
Author

tomjwxf commented Apr 17, 2026

Pushed 5b367fb adding a "Relationship to SLSA Provenance" section to the predicate doc. Motivation: this predicate and SLSA Provenance attest to different properties of the same subject under different trust domains, and the composition pattern matters for downstream verifiers.

Summary of the update:

  • Decision Receipt is signed by the supervisor identity that runs the policy gate (e.g., a PreToolUse hook in the agent host).
  • SLSA Provenance is signed by the builder-platform identity (e.g., the agent-commit build type documented at refs.arewm.com/agent-commit/v0.2).
  • These are distinct trust domains. Cross-signing receipts under the builder key would obscure the supervisor identity in downstream verification.
  • Composition uses ResourceDescriptor in the SLSA byproducts: the builder records the receipt attestation by digest + URI + predicateType, without cross-signing its content.

The pattern is documented in the companion PR at arewm/refs.arewm.com#2, which adds the ResourceDescriptor section to the SLSA agent-commit build type. This in-toto predicate doc now points at that as the canonical byproduct carrier.

No schema or field changes. Purely additive composition documentation.

Happy to iterate on the SLSA composition wording if reviewers prefer a different framing, or to split this out into a separate follow-up PR once the core predicate lands.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 17, 2026

Strong +1 on adding Decision Receipt as an in-toto predicate. The gap analysis is correct — SLSA Provenance attests to build, Simple Verification Result attests to pass/fail without chain, Runtime Trace captures execution without authorization semantics. Decision Receipt is the missing artifact for "what was authorized, under what policy, with what evidence, chained to what came before."

Filing a related observation rather than a blocking change request: this predicate and a Governance Attestation predicate would be natural siblings rather than overlapping types. The distinction worth preserving:

  • Decision Receipt (this PR): one policy evaluation. Atomic. Per-tool-call granularity. Hash-chained within a session.
  • Governance Attestation: the structural statement about an agent's authority at a moment in time — delegation chain root, signing key, scope envelope, revocation status. Session-level rather than action-level.

They compose: a Decision Receipt references a Governance Attestation by hash, and the Attestation binds the agent's authority chain. Consumers can verify either independently — "was this specific decision authorized" vs "is this agent authorized to be making decisions right now" — without conflating the two questions.

APS ships governance_attestation envelopes today (JWS, Ed25519, JWKS-verifiable at /api/v1/public/trust/:agentId), and the A2A #1717 thread has three implementations (APS, MolTrust, SINT) converging on a shared canonical shape. If the in-toto predicate registry is open to a sibling PR under veritasacta.com/attestation/ namespace (or equivalent), happy to file once this lands.

On the PR itself — the decision / policy_id / policy_digest / parent_receipt_hash field set is tight. One small addition that generalizes without breaking: an optional delegation_chain_root field. Two receipts with the same policy_id can represent very different authority contexts, and the root reference closes that gap for verifiers that care about authority, not just policy.

Adjacent context: ScopeBlind/agent-governance-testvectors just landed a multi-implementation conformance suite (protect-mcp / protect-mcp-adk / sb-runtime / aps-governance-hook) that verifies receipts against @veritasacta/verify. Once this predicate is accepted, the testvectors repo is a natural place to ship in-toto-predicate-compliant fixtures for cross-implementation verification — existing driver surface, just swap the envelope wrapper.

@tomjwxf
Copy link
Copy Markdown
Author

tomjwxf commented Apr 18, 2026

@aeoess thanks, the sibling-predicate framing is the right call. Agreed on the atomic (per-decision) vs structural (session-level delegation context) split as two distinct predicate types rather than one conflated predicate.

On your concrete suggestion to add an optional delegation_chain_root field to Decision Receipt: happy to do that as part of this PR. It generalizes without breaking, gives verifiers that care about authority-not-just-policy the hook they need, and stays optional so consumers without delegation semantics ignore it. Will push a commit today.

The proposed field shape:

"delegationChainRoot": {
  "sha256": "<hash of the root delegation attestation covering the agent authority that made this decision>"
}

Using delegationChainRoot (camelCase) to match the other optional fields in the predicate (previousReceiptDigest, outputDigest). Types as a DigestSet per the existing conventions.

On the Governance Attestation sibling predicate: yes, I think this should be a separate in-toto predicate PR under your authorship rather than an extension to this one. Two reasons:

  1. Different author with domain authority. APS has shipped governance attestations in production (JWS, Ed25519, JWKS-verifiable). This predicate should be authored by someone who owns the implementation and the trust model. That is APS, not me.
  2. Different trust-domain assumptions. Decision Receipt signs the policy decision. Governance Attestation binds the agents right to make decisions to a principal delegation chain. Those are two distinct problems; one predicate per problem is the in-toto convention.

Happy to:

  • Land this Decision Receipt PR with delegationChainRoot as an optional field that references Governance Attestation by digest.
  • Write a short "Sibling predicates" subsection in the Decision Receipt doc naming Governance Attestation (when it exists) as the parallel structural attestation.
  • Cross-reference both PRs from each other once yours is open. Naming is yours to pick; candidates I can think of: governance-attestation, delegation-chain, authority-attestation, or the in-toto-native agent-authority-chain.

Also picking up the testvectors note: yes, ScopeBlind/agent-governance-testvectors is the natural home for in-toto-predicate conformance fixtures once this predicate lands. The repo is designed to add fixture subdirectories per predicate type, so once the wire format stabilizes here, we can ship a fixtures/in-toto-decision-receipt/ set that exercises both v0.1 flat-payload and DSSE-wrapped shapes. Targeting that as a v0.3 of testvectors.

On 4 implementations cross-verifying at exit 0: confirmed via your APS driver submission. The cross-verification story is now multi-vendor, multi-language, multi-envelope-shape.

Will push the delegationChainRoot addition today. cc @Hayden-IO for context in case this touches the Rekor anchoring pattern.

…ing predicate section

Per @aeoess review suggestion on in-toto#549: add an optional delegationChainRoot
field to the Decision Receipt predicate so verifiers that care about
authority (not just policy) can cross-reference a sibling Governance
Attestation predicate without cross-signing trust domains.

Additions (additive, no breaking changes):

1. Optional delegationChainRoot field in the schema, typed as DigestSet
   per existing conventions. Points at the root of the delegation chain
   under which the agent was authorized to make the decision. Present
   in authority-chain-referenced mode, absent in operator-signed mode.

2. Fields table entry documenting the two modes (operator-signed vs
   authority-chain-referenced) and when to populate the field.

3. Changelog note clarifying that delegationChainRoot is optional and
   does not affect conformance for deployments without delegation
   semantics.

4. New 'Sibling predicates' section explaining that Decision Receipt is
   an atomic, per-call predicate and that a Governance Attestation
   predicate (forthcoming, authored by APS per aeoess) is the parallel
   session-level structural attestation. The two compose via the
   delegationChainRoot reference.

This change is additive. Consumers that do not understand the field
ignore it. The receipt remains a valid Decision Receipt whether or not
the field is present.
@aeoess
Copy link
Copy Markdown

aeoess commented Apr 18, 2026

delegationChainRoot: DigestSet camelCase with the other optional fields is the right call. Accepting.

On the Governance Attestation sibling predicate — agreed, that's a separate PR under APS authorship. Will draft the predicate spec this week following in-toto conventions. Scope: bind the agent's authority to act (delegation chain root, principal signature, scope narrowing invariants, Values Floor attestation) as structural context that the Decision Receipt references by digest. JWS + Ed25519 per the APS gateway's existing /api/v1/public/trust/:agentId shape so it's verifiable offline against JWKS without introducing new signing primitives.

Once your PR lands with delegationChainRoot, I'll open the Governance Attestation predicate PR cross-referencing it from both sides — your note about the sibling-predicates subsection in the Decision Receipt doc is the right coordination pattern.

aeoess added a commit to aeoess/hermes-aps-delegation that referenced this pull request Apr 18, 2026
Three modules in one repo:
- charter/ — operator-signed root delegation YAML with skill_creation
  axes (allowed, domains, persistent, max_modifications_per_window,
  modification_window_seconds) plus standard APS scopes. Validator + 3
  example charters (strict/moderate/permissive).
- src/hermes_aps/skill_receipt.py — Ed25519 signer producing in-toto
  Decision Receipt v0.1 envelopes for skill-creation events.
  delegationChainRoot links every receipt to the charter digest.
- src/hermes_aps/tool_call_receipt.py — per-invocation receipts binding
  each tool call to skill_version_hash and parent_skill_version_hash.

Verifier walks the chain, checks signatures, sequence/previousReceipt
linking, and monotonic narrowing against the charter. 12 tests, three
worked examples (allowed mutation, denied mutation, 3-revision chain).

Predicate conformance: in-toto/attestation#549 Decision Receipt v0.1.
Hermes hook surfaces are stubbed and marked TODO[hermes-hook]; see
README "Assumed Hermes interfaces". v0.1.0 target end of April,
coordinated with NousResearch/hermes-agent#11692.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
imran-siddique pushed a commit to microsoft/agent-governance-toolkit that referenced this pull request Apr 19, 2026
* docs: add Tutorial 33 — Offline-Verifiable Decision Receipts

Teaches the decision-receipt layer that sits between internal audit logs
(Tutorial 04) and artifact signing (Tutorial 26): per tool-call Ed25519
signatures over JCS-canonical payloads, hash-chained across the session,
verifiable offline by any party with the public key.

Mirrors the existing `examples/protect-mcp-governed/` (PR #1159) and
`examples/physical-attestation-governed/` (PR #1168) reference code,
uses their exact APIs, and cross-references Tutorials 01, 04, 07, 08,
12, 26, and 27.

Adds two entries to docs/tutorials/README.md:
- Supply Chain Security section (alongside 25, 26, 27)
- "Enterprise compliance" learning path step 6

Standards covered: RFC 8032 (Ed25519), RFC 8785 (JCS), Cedar (AWS),
IETF draft-farley-acta-signed-receipts.

* docs: strengthen Tutorial 33 with SLSA integration and anchoring primitives

Four additions landing after the initial PR:

1. Receipt Lifecycle ASCII diagram in "The Receipt Format" section.
   Visualizes mint → JCS canonical → Ed25519 sign → store → verify so
   readers can see why the determinism invariant holds.

2. A real Cedar policy block in §4 (Composing with Cedar Policies).
   Previously the section described the CedarDecision API shape without
   showing what a policy producing one actually looks like. Now shows a
   10-line permit/forbid policy and links out to cedar-for-agents for
   the full schema generator.

3. Neutral anchoring primitives subsection in §6 (Cross-Implementation).
   Names Sigstore Rekor and in-toto attestations as the cross-org
   verification fabric beyond the four implementations. References
   sigstore/rekor#2798 and in-toto/attestation#549.

4. New §7 "Emitting Receipts as SLSA Provenance". When an AI agent is
   itself the builder, the receipt chain IS the per-step build log.
   Shows the exact byproducts JSON shape for carrying a receipt chain
   inside a SLSA provenance v1 attestation, referencing the draft
   agent-commit build type at refs.arewm.com/agent-commit/v0.1 and the
   active slsa-framework/slsa#1594 and #1606 discussions.

No new dependencies. All APIs still verified against the merged
examples/protect-mcp-governed/ and examples/physical-attestation-governed/
reference code.

* docs(tutorial-33): add sidebar on operator-signed vs authority-chain modes

Per @aeoess review on #1197: the four implementations listed in the
cross-implementation section make different identity-binding choices
that matter for deployment selection. This sidebar names them
explicitly so readers evaluating receipts for their environment can
pick the right mode.

- Operator-signed mode (protect-mcp, protect-mcp-adk, sb-runtime):
  sufficient for internal audit, single-regulator evidence, single-
  tenant compliance. The signer is the operator's supervisor hook.
- Authority-chain-referenced mode (asqav / APS governance hook):
  additionally required for cross-org agent commerce, multi-tenant
  regulated environments, and use cases where principal authority is
  itself auditable. Receipts reference a delegation-chain root.

Both modes verify against @veritasacta/verify and use the same outer
receipt structure; the distinction is the presence of an optional
delegation_chain_root field in the payload.

Cross-references arewm/refs.arewm.com#1 for the parallel authority-
chain attestation proposal as a SLSA byproduct.

---------

Co-authored-by: tommylauren <[email protected]>
tomjwxf pushed a commit to ScopeBlind/hermes-decision-receipts that referenced this pull request Apr 19, 2026
Ed25519-signed decision receipts for Nous Research Hermes agent tool
calls. Companion to aeoess/hermes-aps-delegation on the delegation/
identity side; this repo ships the decision-receipt / per-tool-call
evidence layer.

Conforms to draft-farley-acta-signed-receipts-01. Same receipt format as
protect-mcp, protect-mcp-adk, and sb-runtime, so receipts emitted here
cross-verify with @veritasacta/verify and interoperate with those
implementations.

Extensions over the generic receipt shape for Hermes composition:

- skill_version_hash: identifies which Hermes skill produced the tool
  call. Composes with hermes-aps-delegation's charter-enforcement side.
- parent_skill_version_hash: prior skill version in the revision chain.
- delegation_chain_root: references APS delegation root (authority-chain
  mode from Tutorial 33 §6).

Structure:

- src/hermes_decision_receipts/signer.py — Ed25519 + JCS, mirrors
  protect-mcp-adk signing pattern.
- examples/01_basic_tool_call.py — minimum viable use.
- examples/02_chain_with_aps.py — composition with APS delegation root.
- tests/test_signer.py — pytest coverage for signing, verification,
  chain linking, sequence monotonicity, JCS ASCII-only keys,
  composition surface.
- .github/workflows/ci.yml — Python 3.10/3.11/3.12 + ruff + examples.

v0.1.0 coordinated release targeted for end of April 2026, matching
aeoess/hermes-aps-delegation#1.

Hermes-specific runtime wiring is stubbed (Hermes internals aren't
public yet); the signing layer works and is exercised by the examples
against synthesized tool calls.

Refs:
- NousResearch/hermes-agent#11692
- aeoess/hermes-aps-delegation
- draft-farley-acta-signed-receipts-01
- in-toto/attestation#549

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@arian-gogani
Copy link
Copy Markdown

the decision receipt predicate fills a real gap. we've been building a bilateral extension of this format at nobulex where each decision gets two signatures: one pre-execution (authorization) and one post-execution (result binding). just submitted that as a PR to microsoft's agent governance toolkit.

cross-implementation interop on JCS + SHA-256 is proven between TypeScript and Python. three test vectors produce byte-identical digests.

the bilateral shape (authorization_hash + authorization_signature + result_hash + result_signature) would be worth reflecting in the predicate spec so a single attestation carries both "was this authorized?" and "what happened after?" happy to contribute test vectors.

@arian-gogani
Copy link
Copy Markdown

the decision receipt predicate fills a real gap in the in-toto predicate set. we've been building the bilateral extension of this format at nobulex — each decision gets two signatures: one pre-execution (authorization) and one post-execution (result binding). just submitted that as a PR to microsoft's agent governance toolkit (#1333), which was merged today.

cross-implementation interop on the canonicalization path (JCS + SHA-256) is already proven between TypeScript and Python implementations. three test vectors produce byte-identical digests across both.

would be worth reflecting the bilateral shape in the predicate spec so the in-toto attestation can carry both the authorization and the result in one envelope.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 23, 2026

Congrats on #1333 landing in Microsoft's toolkit. That's a real cross-org interop signal for the bilateral shape. The TS↔Python byte-identical digests mean the composition surface is tight enough to extend into the predicate spec without breaking downstream verifiers.

Agreed on reflecting the bilateral shape in the in-toto predicate. Carrying authorization and result in one envelope matters because post-execution binding is where detection-only governance and real enforcement diverge. If the authorization signature and the result signature both reference the same canonical action tuple (agent id, tool, param hash, scope), an auditor can distinguish a request that was authorized-and-executed-as-authorized from one that was authorized-but-drifted-at-execution. That's the invariant worth pinning in the spec.

One addition worth including at the predicate level: a migration attestation field for the case where the pre-execution signature and post-execution signature span a key rotation. SINT is working on this as a structural precondition on the envelope (sint-ai/sint-protocol#178) and APS anchors the migration event in the delegation chain hash. Either shape composes with the bilateral predicate as long as the migration attestation itself is append-only-referenceable. Otherwise a party can manufacture a "key rotated at T+1.5" claim to bridge a forged pre-execution signature to a real post-execution one.

On canonicalization cross-verification: APS is on JCS (RFC 8785) for ConstraintEnvelope and DelegationToken hashing. We also use in-toto Decision Receipt v0.1 (predicateType: https://veritasacta.com/attestation/decision-receipt/v0.1) as the receipt envelope in aeoess/hermes-aps-delegation, which puts us directly in the composition surface you're defining here. Happy to publish a small fixture that exercises the JCS edge cases (nested null, key ordering, recursive sort) against both sides of the bilateral shape, so in-toto predicate conformance can include a round-trip check. Target Monday.

Adjacent question: who's on the in-toto side for the predicate review? The bilateral shape is close enough to Decision Receipt that reviewer alignment on whether these are one predicate (DecisionReceipt with optional pre/post signatures) or two distinct kinds (BilateralDecisionReceipt as separate predicate type) will move the spec faster. Either works technically; the schema versioning story differs.

aeoess added a commit to aeoess/agent-passport-system that referenced this pull request Apr 23, 2026
…terop

10 frozen test vectors (RFC 8785) that let SINT, in-toto Decision
Receipt (arian), and APS prove byte-identical canonical output
before exchanging signed bilateral delegation receipts.

- Deterministic Ed25519 keypair: seed = SHA-256("aps-canonicalize-fixture-v1")
- Vectors cover nulls, Unicode key ordering, empty containers, deep
  nesting, string escapes, number edge cases (-0), arrays of objects,
  plus realistic bilateral-receipt and migrationAttestation envelopes
- npm run test:fixtures verifies the SDK canonicalizer against the
  committed fixture; 10/10 pass

Committed to pshkv (A2A#1718) and arian (in-toto/attestation#549) for
announcement Monday 2026-04-27.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@arian-gogani
Copy link
Copy Markdown

thanks @aeoess. the key rotation point is the right gap to flag — without a migration attestation field the bilateral shape can be bridged by a forged rotation claim. worth pinning in the predicate spec as append-only-referenceable regardless of which migration primitive wraps it (SINT structural, APS delegation-chain).

on one predicate vs two: my lean is one predicate (DecisionReceipt) with optional authorization and result signature fields. the single-signature case is common enough that forcing a separate BilateralDecisionReceipt predicate fragments the verifier surface unnecessarily. existing receipts stay valid, bilateral adopters add two fields.

looking forward to the JCS fixtures Monday and happy to run them through the TS side for a round-trip check.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 23, 2026

@arian-gogani, one predicate with optional authorization and result signature fields is the right call for the integration surface. Fragmenting into two predicates (DecisionReceipt and BilateralDecisionReceipt) forces verifiers to branch on predicate name before they can process the envelope, which is a worse shape for interop than a single predicate where the bilateral adopters add the two optional fields.

On the key-rotation / migration-attestation gap: agreed it pins as append-only-referenceable regardless of which migration primitive wraps it. For APS specifically, the delegation-chain migration shape is stable enough to fixture. migration-attestation-shape is one of the 10 vectors at aeoess/agent-passport-system/fixtures/bilateral-delegation/. The shape carries {from_chain_root, to_chain_root, rotation_signature, rotation_timestamp, from_epistemic_state, to_epistemic_state} so a verifier can bridge the pre-rotation and post-rotation receipts without trusting an unsigned "this is the same entity" claim.

JCS fixtures are ready for Monday's round-trip. All 10 vectors pass on our TS side at fixtures/bilateral-delegation/ with deterministic byte-for-byte output across identical inputs with reordered keys, nested null, empty containers, Unicode normalization edge cases, escape sequences, and numeric edge cases (trailing zeros, exponent notation, negative zero). The SDK bilateral-receipt property tests reproduce all 10 in CI at tests/property-bilateral-receipt.test.ts.

Two small things to flag for the round-trip:

  1. The migration-attestation-shape fixture assumes rotation_signature is Ed25519 over JCS(from_chain_root || to_chain_root || rotation_timestamp). If your TS implementation expects a different concatenation order or a different serialization of the triple before signing, surface it at the fixture level and we align on one ordering.

  2. For the single-predicate-with-optional-fields shape, the APS side emits authorization_signature and result_signature as top-level predicate fields when present, not nested under a bilateral object. If your implementation nests them, that is a schema divergence worth catching at Monday's fixture review rather than in production.

Looking forward to the round-trip. v2.3.0-alpha on npm since yesterday, npm install agent-passport-system@alpha pulls the emitter.

@arian-gogani
Copy link
Copy Markdown

thanks @aeoess. running the round-trip Monday morning.

flagging both items in advance so we resolve at the fixture level rather than surfacing them mid-test:

  1. rotation_signature concatenation order: nobulex emits Ed25519(JCS({from_chain_root, to_chain_root, rotation_timestamp})) over the canonicalized object, not over the byte-concatenation of the three values. if APS expects byte-concatenation from_chain_root || to_chain_root || rotation_timestamp, that's a schema divergence we should align on at fixture level. propose JCS-of-object as the canonical form since it matches the rest of the bilateral-receipt envelope and removes ordering ambiguity. happy to adopt either if APS is set on the byte-concat shape.

  2. authorization_signature / result_signature placement: nobulex currently nests these under a bilateral object on the predicate. will lift to top-level for the round-trip to match the APS shape. one-line patch at the emitter; will land before Monday so the byte-match holds.

will post results from running the 10 vectors at aeoess/agent-passport-system/fixtures/bilateral-delegation/ through @nobulex/crypto once they're through. also pulling agent-passport-system@alpha to verify the receiver side.

arian-gogani pushed a commit to arian-gogani/nobulex that referenced this pull request Apr 26, 2026
Implements snake_case top-level field shape for round-trip with APS
bilateral-delegation fixtures (per aeoess on in-toto/attestation#549).

Verified deterministic across key order.

Refs: in-toto/attestation#549
@arian-gogani
Copy link
Copy Markdown

update on schema alignment commitment from earlier — the predicate emitter is in main: packages/core/src/proof/in-toto-emitter.ts.

verified on a sample receipt:

=== Schema check ===
Top-level keys: authorization_hash, authorization_signature, result_hash, result_signature, signer_public_key, timestamp
Has bilateral wrapper: false
Has snake_case auth_sig: true
Has camelCase auth_sig: false

=== Canonicalization check ===
Deterministic across key order: true
Length: 605 bytes

snake_case top-level matches APS shape. JCS canonical bytes deterministic across key order. ready for the Monday round-trip against agent-passport-system/fixtures/bilateral-delegation/ — will pull agent-passport-system@alpha and run all 10 vectors through the emitter, confirm byte-match, and post results here.

on rotation_signature concatenation order: emitter currently constructs the migration_attestation object with from_chain_root / to_chain_root / rotation_timestamp as separate fields. signing path is open to either (a) Ed25519 over JCS({from_chain_root, to_chain_root, rotation_timestamp}) or (b) Ed25519 over the byte-concatenation. fixture-level alignment Monday will resolve which one APS expects, and the emitter takes a one-line patch either way.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 26, 2026

@arian-gogani rotation_signature is JCS-of-object on our side, not byte-concat. We canonicalize the rotation payload as an object and sign the canonical bytes. So your nobulex shape and APS should byte-match without touching the fixture.

The migration-attestation-shape vector at fixtures/bilateral-delegation/canonicalize-fixture-v1.json has canonical_sha256 446b5431790e8afb201b2e19790268ae3d4c39b94a10923ef3e43cc92958bbfd. That's the digest your TS canonicalizer should hit on the same input.

On signature placement: top-level on the predicate is what APS does, so your lift from the bilateral nest is consistent.

Deterministic keypair from the fixture readme so you can reproduce end-to-end:

  • seed: 4f3d8defea1e82c1705c35d97ee4db046c6313ba83855a7d0de04a44f04c834a
  • pubkey: 16bd0f3e8181e93d58c23268ee0d5f4d5b70b3ce66fc246c0f5d7ec3dda9ab80

All 10 vectors pass on our side. [email protected] is on npm under the alpha tag, property-bilateral-receipt.test.ts in CI.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 27, 2026

@arian-gogani — quick confirmation for Monday's round-trip and the Apr 30 cross-impl test:

Pulled agentgraph.co/.well-known/cte-test-vectors.json today; we byte-match all four current vectors (envelope, verdict, scope_violation, composition_failure). The two hex literals from your Apr 23 22:48Z A2A#1752 comment (envelope 50fcb652... and verdict a7b8bc19...) don't appear in the current well-known. Looks like a version bump from CTEF v0.3.0 to v0.3.1 (the §6.3 verdict surface and §4.6 delegation_chain_root composition look post-Apr-23).

Confirming the live v0.3.1 well-known is the canonical surface for Monday + Apr 30. If a different fixture is canonical, point me at it.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 27, 2026

@arian-gogani, public follow-up to my 00:44Z note. The APS Governance Attestation predicate is now live and ready for the Apr 30 round-trip.

Repo: https://github.com/aeoess/governance-attestation-predicate (Apache-2.0)

Predicate type: https://aeoess.com/attestation/governance/v0.1

Composition shape with Decision Receipt: a Governance Attestation binds the agent's authority-to-act (delegation chain root, principal signature, scope narrowing invariants, Values Floor attestation hash) and exposes a governance_attestation_id that Decision Receipts can reference by digest in subject.digest.sha256. Walking the chain proves both axes independently: what the agent decided AND what authorized the agent to be in that position.

Conformance: 5 fixture vectors deterministically reproducible from a fixed Ed25519 seed, JCS-canonical, JWS-signed. Three negative vectors (expired-window, monotonic-narrowing-violated, chain-root-mismatch) verify the rejection paths. Composition test exercises the full round-trip with tampering detection.

If the test runner shape from agentgraph.co/.well-known/cte-test-vectors.json is the canonical surface for Apr 30, happy to mirror the byte-match interface at aeoess.com/.well-known/governance-attestation-vectors.json.

— Tymofii Pidlisnyi (aeoess)

@arian-gogani
Copy link
Copy Markdown

Monday round-trip results — 10/10 byte-match against aeoess/[email protected] fixtures/bilateral-delegation/canonicalize-fixture-v1.json:

=== APS Bilateral-Delegation Fixture v1 ===
Spec: JCS — RFC 8785
Canonicalization: APS SDK canonicalizeJCS
Vector count: 10
Seed sha256: 4f3d8defea1e82c1705c35d97ee4db046c6313ba83855a7d0de04a44f04c834a

MATCH | nested-null-preservation
MATCH | key-ordering-unicode
MATCH | empty-containers
MATCH | deeply-nested
MATCH | string-escape-tab
MATCH | string-escape-unicode
MATCH | numeric-edge-cases
MATCH | array-of-objects
MATCH | bilateral-receipt-shape
MATCH | migration-attestation-shape

=== Result: 10/10 byte-match ===

migration-attestation-shape lands on the canonical_sha256 you posted (446b5431790e8afb201b2e19790268ae3d4c39b94a10923ef3e43cc92958bbfd). bilateral-receipt-shape matches the top-level snake_case predicate the emitter produces (no bilateral wrapper).

rotation_signature concatenation order on our side is JCS-of-object — matches APS, no fixture change needed.

Cleanly unblocked for Apr 30. Pulling aeoess/governance-attestation-predicate next to wire in the cross-axis composition (Decision Receipt referencing governance_attestation_id by digest in subject.digest.sha256).

@marcelamelara
Copy link
Copy Markdown
Contributor

Thanks for this proposal. The in-toto maintainers have discussed this proposal, and have agreed to close this proposal because we believe the intent of this predicate can be achieved with a Simple Verification Result (SVR) predicate with extension fields (see an example).

Please attend the next in-toto community meeting on May 1, if you'd like to reopen the discussion.

@aeoess
Copy link
Copy Markdown

aeoess commented Apr 28, 2026

@arian-gogani — 10/10 byte-match is a clean unblock. migration-attestation-shape landing on 446b5431790e8afb201b2e19790268ae3d4c39b94a10923ef3e43cc92958bbfd and bilateral-receipt-shape on the top-level snake_case predicate means the canonicalization surface is tight enough to extend without breaking anything downstream. JCS-of-object on rotation_signature aligns cleanly.

For the Apr 30 cross-axis composition with aeoess/governance-attestation-predicate: Decision Receipt referencing governance_attestation_id by digest in subject.digest.sha256 walks both axes independently — what the agent decided and what authorized it to be in that position.

@marcelamelara — taking the SVR-with-extension-fields direction seriously. Will read the SVR optional-policy-information example carefully before May 1 to understand the constraint shape on extension fields. The two questions worth raising at the meeting are (1) whether the composition I described above (a Decision Receipt referencing a separate Governance Attestation predicate by digest, exposing both axes for independent walks) lands as a single SVR with extension fields or as two SVRs cross-referencing, and (2) whether the bilateral-shape fields nobulex and APS just byte-matched (authorization_hash, authorization_signature, result_hash, result_signature at top level) sit naturally in the SVR extension surface or need adjustment to fit. Will plan to attend May 1.

— Tymofii Pidlisnyi (aeoess)

aeoess added a commit to aeoess/agent-passport-system that referenced this pull request Apr 29, 2026
…terop

10 frozen test vectors (RFC 8785) that let SINT, in-toto Decision
Receipt (arian), and APS prove byte-identical canonical output
before exchanging signed bilateral delegation receipts.

- Deterministic Ed25519 keypair: seed = SHA-256("aps-canonicalize-fixture-v1")
- Vectors cover nulls, Unicode key ordering, empty containers, deep
  nesting, string escapes, number edge cases (-0), arrays of objects,
  plus realistic bilateral-receipt and migrationAttestation envelopes
- npm run test:fixtures verifies the SDK canonicalizer against the
  committed fixture; 10/10 pass

Committed to pshkv (A2A#1718) and arian (in-toto/attestation#549) for
announcement Monday 2026-04-27.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
arian-gogani pushed a commit to arian-gogani/nobulex that referenced this pull request Apr 30, 2026
Anyone can clone and run node scripts/verify-aps-byte-match.mjs to
independently confirm 10/10 byte-match against APS bilateral-delegation
fixtures. Outputs a verification receipt JSON on success.

Addresses aeoess and kenneives framing on A2A #1786: upgrades
Nobulex byte-match from stated claim to independently reproducible.

Refs: a2aproject/A2A#1786, in-toto/attestation#549
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants