Skip to content

Bind signed Trust Manifests to release coordinates - #108

Open
jonathanhefner wants to merge 1 commit into
Agent-Card:mainfrom
jonathanhefner:agent/bind-trust-manifest-release-subject
Open

jonathanhefner wants to merge 1 commit into
Agent-Card:mainfrom
jonathanhefner:agent/bind-trust-manifest-release-subject

Conversation

@jonathanhefner

@jonathanhefner jonathanhefner commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

A signed Trust Manifest currently binds its claims to an artifact representation through subject.type, subject.digest, and optionally subject.url. The containing Catalog Entry’s identifier and version remain outside that signature, even though they determine the artifact’s logical name and participate in catalog version selection.

A catalog writer can therefore retain a valid signed manifest and the exact artifact bytes while assigning them to another logical artifact or relabeling an old release with a higher version. In the latter case, latest-version or minimum-version selection can return vulnerable code while every existing per-entry signature, type, URL, and digest check succeeds.

This PR requires every Subject to identify the artifact it describes. For a Subject on a Catalog Entry, subject.identifier must match entry.identifier. For a signed entry Trust Manifest, entry.version and subject.version must either both be omitted or both be present and exactly equal. This gives the signed version one unambiguous role as an authenticated restatement of the entry’s release coordinate.

ADR-0025 records the decision and its rationale. The specification, threat model, CDDL, examples, mapping guidance, and author documentation are updated accordingly.

Rationale

A top-level catalog signature authenticates the catalog signer’s snapshot, including its identifiers and versions. It does not necessarily establish that the Trust Manifest signer assigned those release coordinates, because the catalog operator and artifact publisher may be different principals. Conversely, an entry Trust Manifest does not protect collection-wide structure. The two signatures make different statements and provide complementary guarantees.

Keeping subject.url optional preserves mirror and relocation support. The signed logical identifier, applicable version, media type, and digest are sufficient to bind the claims to an artifact release and its exact representation without binding that release to one location.

Scope

This PR does not redefine TrustManifest.identity or specify signing-key selection for did:web, SPIFFE, or other identity mechanisms. It also does not define what the subject of a signed Host Trust Manifest represents. The current specification requires such a manifest to carry a subject without defining its Host-specific meaning; that pre-existing discrepancy requires a separate decision and should be reviewed in concert with this proposal.


AI usage disclosure: This PR was developed with AI assistance under my direct supervision and review.

@mindpower

Copy link
Copy Markdown
Member

LGTM, thanks @jonathanhefner . I took some time to read thoroughly through both this and PR #110. Both make a lot of sense to me, help address critical issues, and bring much-needed clarity to the trust binding model.

T4 (version rollback / relabeling) was definitely a major blind spot under the old model. If the detached JWS only covers the byte digest, an attacker or compromised mirror could easily serve an older vulnerable release and bump entry.version to look like a new patch without breaking the signature.

A few things I really like about this change:

  • Duplicating identifier and version in subject instead of signing the entire CatalogEntry is the right call. It keeps signatures intact when registries or aggregators add metadata, search scores, or tags.
  • Making subject.version conditional on entry.version keeps unversioned tools simple without leaving a rollback hole for versioned ones.
  • Keeping subject.url optional avoids breaking CDN mirrors or air-gapped enterprise setups, while still letting publishers pin endpoints if they need to.


A Subject object MUST contain:

`identifier`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. This is a great call to include the identifier here for the trust binding.

? extensions: { * text => any }
}

Subject = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. This is the right follow up to address the security concerns raised in #47

@mindpower mindpower left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@muscariello muscariello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mechanism and examples check out end to end.

@jonathanhefner one gap before merge:

subject.version is only required to match when entry.version is present, so an entry can just drop its version field to escape the comparison entirely.

The same relabeling attack this PR closes, via deletion instead of forgery. I'd suggest making the binding symmetric: if either side has a version, both must, and must match.

Require every `Subject` to identify the artifact it describes, and require signed entry manifests to bind any catalog version they rely on. Verify those signed release coordinates against the containing entry so valid artifact bytes and trust claims cannot be transplanted or relabeled as another release.

Record the design rationale in ADR-0025, extend the threat model, and align the normative prose, CDDL, examples, xRegistry mapping, and author guidance. Clarify that catalog-level integrity remains complementary to publisher-signed release binding.

Signed-off-by: Jonathan Hefner <jonathan@hefner.pro>
@jonathanhefner
jonathanhefner force-pushed the agent/bind-trust-manifest-release-subject branch from ca62b29 to 5453884 Compare September 14, 2026 00:07
@jonathanhefner

Copy link
Copy Markdown
Contributor Author

@muscariello Thank you for reviewing! ❤️

subject.version is only required to match when entry.version is present, so an entry can just drop its version field to escape the comparison entirely.

The same relabeling attack this PR closes, via deletion instead of forgery. I'd suggest making the binding symmetric: if either side has a version, both must, and must match.

The thinking behind the asymmetry was that a Trust Manifest could sign a version and be then propagated to catalogs that omit entry.version per:

Like `displayName` and `description`, `version` can restate a value
the referenced artifact already carries (an A2A Agent Card
`version`, an MCP Server Card `version`), and when a single entry
references such an artifact the entry `version` SHOULD be omitted to
avoid drift — the consumer can read it from the artifact. Unlike

I don't know if the described attack would be quite the same as version relabeling, because the catalog entry wouldn't be making a claim about the version, and because the signed subject would provide the actual version.

However, the carve out might not be worth the logical complexity, so I pushed a change to require:

- `subject.version` and `entry.version` MUST either both be absent or both be
  present and exactly equal.

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.

3 participants