diff --git a/adr/0024-entry-trust-manifest-publisher-domain-alignment.md b/adr/0024-entry-trust-manifest-publisher-domain-alignment.md new file mode 100644 index 0000000..ccd9822 --- /dev/null +++ b/adr/0024-entry-trust-manifest-publisher-domain-alignment.md @@ -0,0 +1,66 @@ +# ADR-0024: Define Publisher-Domain Alignment for Entry Trust Manifests + +## Status + +Proposed + +## Date + +2026-09-02 + +## Context + +The Trust Manifest required the domain in its `identity` to align with the +publisher domain in the containing Catalog Entry's `identifier` without +defining how to obtain or compare those domains. Non-`urn:air` identifiers do +not necessarily communicate a publisher domain, so implementations could not +apply the requirement interoperably. + +Alignment can check consistency between the `{publisher}` domain in the +Catalog Entry's `identifier` and the domain in the Entry Trust Manifest's +`identity`. A Trust Manifest signature covers its `identity`, but does not by +itself authenticate the containing Catalog Entry's `identifier`. Alignment +cannot prove domain control, publisher or signer authorization, or +authenticity; those guarantees require verification and an independent trust +policy. + +## Decision + +Entry Trust Manifests are supported only on Catalog Entries whose `identifier` +is publisher-authorized and uses the standard `urn:air` syntax defined in +Catalog Entry. Non-`urn:air` identifiers remain valid for Catalog Entries that +do not include an Entry Trust Manifest. + +Catalog operators may reproduce an Entry Trust Manifest together with an +identifier assigned by the publisher or its authorized delegate. + +AI Catalog defines how to obtain the domain used for publisher-domain +comparison from three identity URI forms: + +- the resolution domain of a `did:web` identity, excluding any port; +- the host of an HTTPS identity, excluding any port; and +- the trust domain of a SPIFFE identity when it is an IDNA2008 domain name. + +The applicable URN, URI, `did:web`, SPIFFE, and IDNA standards govern syntax +and validity; AI Catalog does not redefine their syntax. AI Catalog compares +complete IDNA2008 domain names serialized in ASCII form exactly after ASCII +case normalization. Ports, paths, queries, and fragments remain part of an +identity and may affect its interpretation or resolution, but they are ignored +when comparing its domain with the publisher domain. + +When no comparison domain can be obtained or the domains are not aligned, +consumers disregard the Entry Trust Manifest's claims and apply their policy +for an entry without a usable Trust Manifest. The optional `identityType` +member is informational: consumers obtain the domain from the `identity` URI +itself, not from this hint. + +Supporting another identity URI form would require defining how to obtain its +domain for comparison. + +## Consequences + +- Alignment means consistency, not authentication or authorization. +- Non-`urn:air` identifiers remain valid for Catalog Entries that do not + include an Entry Trust Manifest. +- Ports, paths, queries, and fragments remain part of an identity even though + they do not participate in publisher-domain comparison. diff --git a/docs/examples/multi-protocol-agent.md b/docs/examples/multi-protocol-agent.md index 0f95ac4..6741008 100644 --- a/docs/examples/multi-protocol-agent.md +++ b/docs/examples/multi-protocol-agent.md @@ -36,7 +36,7 @@ An agent that supports both MCP and A2A protocols can be represented as a single ] }, "trustManifest": { - "identity": "urn:air:acme-corp.com:agent:finance", + "identity": "did:web:acme-corp.com:agent:finance", "attestations": [ { "type": "SOC2-Type2", diff --git a/docs/guides/adding-trust.md b/docs/guides/adding-trust.md index 5c630cc..cff6685 100644 --- a/docs/guides/adding-trust.md +++ b/docs/guides/adding-trust.md @@ -55,7 +55,7 @@ Trust builds on the three conformance levels: "type": "application/a2a-agent-card+json", "url": "...", "trustManifest": { - "identity": "urn:air:acme-corp.com:a2a:finance", + "identity": "did:web:acme-corp.com:a2a:finance", "attestations": [...] } } @@ -68,13 +68,20 @@ Trust builds on the three conformance levels: A Trust Manifest is an object on a Catalog Entry (or Host Info object) with one required field: `identity` -: A globally unique URI that identifies this artifact. **Its trust domain must align with the publisher domain in the containing entry's `identifier`.** This binding ties trust claims to the authorized publisher. +: A globally unique URI that identifies this artifact. + +For a Trust Manifest on a Catalog Entry: + +- The containing Catalog Entry's `identifier` must be a publisher-authorized `urn:air` identifier. +- The publisher domain in the Catalog Entry's `identifier` must exactly match the domain obtained from the Trust Manifest's `identity`, ignoring ASCII case. AI Catalog defines how to obtain that domain from `did:web`, HTTPS, and SPIFFE identities. + +This alignment is a consistency check; it does not prove publisher authorization or control of the domain. See the [full specification](../specification.md) for the normative rules. All other fields are optional: | Field | Description | |---|---| -| `identityType` | Type hint for the identity URI: `"did"`, `"spiffe"`, `"dns"` | +| `identityType` | Optional hint describing the identity URI, such as `did` or `spiffe`; consumers use `identity` itself—not this hint—to determine the domain for publisher-domain alignment | | `trustSchema` | Describes the trust framework applied | | `attestations` | Array of compliance and identity attestation objects | | `provenance` | Array of provenance links (source code, OCI digests) | @@ -92,7 +99,7 @@ The simplest trust step is asserting publisher identity. Use an attestation of t ```json "trustManifest": { - "identity": "urn:air:acme-corp.com:a2a:finance", + "identity": "did:web:acme-corp.com:a2a:finance", "attestations": [ { "type": "publisher-identity", @@ -165,7 +172,7 @@ The `signature` field holds a detached JWS (RFC 7515): ```json "trustManifest": { - "identity": "urn:air:acme-corp.com:a2a:finance", + "identity": "did:web:acme-corp.com:a2a:finance", "attestations": [...], "signature": "eyJhbGciOiJFUzI1NiJ9..detached-jws-signature" } @@ -207,7 +214,7 @@ A Trust Manifest with identity, compliance attestation, provenance, and signatur "displayName": "Acme Financial Corp" }, "trustManifest": { - "identity": "urn:air:acme-corp.com:a2a:finance", + "identity": "did:web:acme-corp.com:a2a:finance", "trustSchema": { "identifier": "urn:trust:acme-enterprise-v1", "version": "1.0", diff --git a/docs/mappings/mcp-servers.md b/docs/mappings/mcp-servers.md index b332542..d261f46 100644 --- a/docs/mappings/mcp-servers.md +++ b/docs/mappings/mcp-servers.md @@ -66,7 +66,7 @@ server's Server Card and whose `type` is the known type "displayName": "Acme Financial Corp" }, "trustManifest": { - "identity": "urn:air:acme-corp.com:mcp:finance-server", + "identity": "did:web:acme-corp.com:mcp:finance-server", "attestations": [ { "type": "publisher-identity", diff --git a/specification/ai-catalog.md b/specification/ai-catalog.md index cbc6c18..403b43c 100644 --- a/specification/ai-catalog.md +++ b/specification/ai-catalog.md @@ -335,7 +335,9 @@ The following members are OPTIONAL: `trustManifest` : A Trust Manifest object as defined in [Trust Manifest](#trust-manifest) providing verifiable identity and trust metadata for this artifact. - See [Trust Manifest](#trust-manifest) for details. + A Trust Manifest carried here is an *Entry Trust Manifest*. See + [Entry Trust Manifest Publisher-Domain Alignment](#entry-trust-manifest-publisher-domain-alignment) + for its requirements. ### Resolving an Artifact's Display Name @@ -513,19 +515,75 @@ A Trust Manifest MUST contain: DID, SPIFFE ID, or URL; these are illustrative and the set of identity schemes is open. -When a Trust Manifest appears within a Catalog Entry, the `identity` -field's trust domain MUST align with the publisher domain in the -containing entry's `identifier` field. This binding ensures -trust claims are associated with the authorized publisher namespace even -when `identity` and `identifier` use different URI schemes. -Consumers MUST reject a Trust Manifest whose `identity` domain does not -align with the publisher domain in the containing entry's `identifier`. -The `identity` is carried here so domain binding is part of the signed -payload, rather than inferred only from unsigned entry context. - When a Trust Manifest appears on a Host Info object, `identity` SHOULD match the host's `identifier` field when present. +### Entry Trust Manifest Publisher-Domain Alignment + +This subsection applies only to a Trust Manifest on a Catalog Entry. A Catalog +Entry that includes a Trust Manifest MUST use a publisher-authorized `urn:air` +identifier with the standard syntax defined in [Catalog Entry](#catalog-entry). +The identifier MUST have been assigned by the artifact publisher or its +authorized delegate using the publisher's domain in its `{publisher}` +component. Non-`urn:air` identifiers remain valid for Catalog Entries that do +not include an Entry Trust Manifest. + +An independent catalog operator MAY reproduce an Entry Trust Manifest together +with its publisher-assigned identifier. + +The publisher domain is the `{publisher}` component of the Catalog Entry's +`urn:air` identifier. Consumers obtain the identity domain from a valid Trust +Manifest `identity` URI [[RFC3986]] as follows: + +- **`did:web`:** the domain used by the method's resolution rules + [[DIDWEB]], excluding any port; +- **HTTPS:** the URI host, excluding any port [[RFC9110]]; or +- **SPIFFE:** the trust domain of a valid SPIFFE ID [[SPIFFEID]], when that + trust domain is an IDNA2008 domain name [[RFC5890]] [[RFC5891]]. + +URI, DID, SPIFFE, and domain syntax validity are defined by their respective +standards, including [[RFC8141]]. A valid port remains part of the identity but +is not part of the domain used for publisher-domain comparison. Paths, queries, +and fragments likewise do not participate in the comparison. The `identity` +URI itself determines which rule above applies. The optional `identityType` +member is only a descriptive hint and MUST NOT be used to select a +domain-extraction rule. + +The publisher domain and identity domain MUST be valid IDNA2008 domain names in +ASCII form, with internationalized labels encoded as A-labels (the ASCII form +beginning with `xn--`) and without a trailing root dot. Consumers MUST compare +them for exact equality after converting ASCII letters to lowercase; suffix +matching and organizational-ownership heuristics MUST NOT be used. + +| Entry identifier | Trust Manifest `identity` | Alignment | +|---|---|---| +| `urn:air:example.com:agent:billing` | `did:web:example.com:agent:billing` | aligned | +| `urn:air:example.com:agent:billing` | `https://EXAMPLE.com:8443/keys/billing` | aligned | +| `urn:air:example.com:agent:billing` | `spiffe://example.com/billing` | aligned | +| `urn:air:example.com:agent:billing` | `did:web:service.example.com:billing` | not aligned | + +A consumer MUST disregard all claims in an Entry Trust Manifest if any of the +following conditions applies: + +- The Catalog Entry's `identifier` does not use the standard `urn:air` syntax. +- The Catalog Entry's `identifier`, the Trust Manifest's `identity`, or either + domain is invalid under the applicable standards or the requirements above. +- This subsection does not define how to obtain a domain from the Trust + Manifest's `identity` URI. +- The publisher domain and identity domain are not aligned. + +The consumer MUST then apply the same policy it would apply to a Catalog Entry +without a usable Trust Manifest. + +Alignment establishes consistency between the publisher domain in the Catalog +Entry's `identifier` and the domain in the Trust Manifest's `identity`. It does +not establish control of that domain, publisher or signer authorization, +signature validity, artifact integrity, publisher authenticity, or +trustworthiness. Those properties require the corresponding verification and +trust-anchoring procedures. A Trust Manifest signature covers its `identity`, +but does not by itself authenticate the containing Catalog Entry's +`identifier`. + When multiple entries share the same `identifier` (with different `version` values), each entry MAY carry its own Trust Manifest. There is no requirement that all versions carry identical trust metadata — trust @@ -1822,7 +1880,7 @@ artifact types including a nested catalog packaging related artifacts: "type": "application/parquet", "url": "https://data.acme-corp.com/market-2026q1.parquet", "trustManifest": { - "identity": "urn:air:acme.com:data:market-2026q1", + "identity": "did:web:acme.com:data:market-2026q1", "provenance": [ { "relation": "publishedFrom", @@ -1835,7 +1893,7 @@ artifact types including a nested catalog packaging related artifacts: ] }, "trustManifest": { - "identity": "urn:air:acme.com:plugin:finance-suite", + "identity": "did:web:acme.com:plugin:finance-suite", "signature": "eyJhbGciOiJFUzI1NiJ9..detached" }, "updatedAt": "2026-03-20T14:00:00Z" diff --git a/specification/respec-config.json b/specification/respec-config.json index 50c40ea..ba036a9 100644 --- a/specification/respec-config.json +++ b/specification/respec-config.json @@ -46,6 +46,18 @@ "href": "https://www.rfc-editor.org/rfc/rfc8288", "status": "RFC", "publisher": "IETF" + }, + "DIDWEB": { + "title": "did:web Method Specification", + "href": "https://github.com/w3c-ccg/did-method-web/blob/ea423c114e6f2537498ee6f94e8d794c64f60c18/index.html", + "status": "Draft Community Group Report", + "publisher": "W3C Credentials Community Group" + }, + "SPIFFEID": { + "title": "SPIFFE ID Standard", + "href": "https://github.com/spiffe/spiffe/blob/dc4e9d9b4eff8aa181a54cd330ff9f877186060e/standards/SPIFFE-ID.md", + "status": "Standard", + "publisher": "SPIFFE" } } }