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
83 changes: 83 additions & 0 deletions adr/0021-multiple-identities-alsoknownas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# ADR 0021: Support Multiple Identities via `alsoKnownAs` in the Trust Manifest

## Status
Proposed

## Date
2026-07-24 (Proposed)

**Participants:** Alexander Shenshin (DSR Corporation), Darrel Miller (Microsoft), Jeffrey Damick (Amazon), Junjie Bu (Google), Ramiz Polic (Cisco), Sam Betts (Cisco)

## Context
A catalog entry can declare exactly one cryptographic identity through the `trustManifest.identity` field (with an optional `identityType` hint). Real artifacts frequently hold more than one verifiable identity at the same time, for example:

- a SPIFFE ID (`spiffe://acme.com/ns/finance/sa/finance-a2a-pod`) for runtime/workload identity, and
- a DID (`did:web:acme-corp.com`) for organizational/publisher-anchored identity.

There is no first-class place to list additional identities. Authors are forced to add them through `attestations[]` or `metadata`, where consumers do not reliably look for them and cannot treat them as verifiable subject identities ([issue #52](https://github.com/Agent-Card/ai-catalog/issues/52)). The workarounds are inadequate:

- **Attestations are limiting.** An `Attestation` is a typed claim with evidence, designed for compliance documents. There is no standard attestation type meaning "this is another identity of the same subject", so alternate identities cannot be discovered or pinned programmatically.
- **Metadata is opaque.** Per the spec, consumers SHOULD ignore metadata keys they do not recognize, so a second identity placed there is invisible and non-interoperable.
- **Conflicts with the domain-alignment rule.** The rule binding the `identity` trust domain to the publisher domain of the entry's `identifier` is written for a single identity and gives no guidance for identities in other trust domains.

Use cases requiring multiple identities include: co-equal runtime and publisher identities, relying parties that can only resolve a subset of identity schemes, and migration/rotation between identity schemes without breaking existing consumers.

A key constraint shaped this decision: **declared identities MUST be verifiable through the trust bundle.** An identity claim a consumer cannot verify is worse than no claim at all.

## Decision
The Trust Manifest gains an OPTIONAL `alsoKnownAs` member: an array of Identity Alias objects, each asserting an alternative identity of the **same subject** as the canonical `identity` field through a REQUIRED `identity` URI and an OPTIONAL `identityType` hint.

```json
"trustManifest": {
"identity": "spiffe://acme.com/ns/finance/sa/finance-agent-pod",
"identityType": "spiffe",
"alsoKnownAs": [

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

does this need a type or at least the possibility of a type?

Perhaps:

"alsoKnownAs" : [
  { 
   "identity": "did:web:acme-corp.com:agent:finance",
   "identityType": "did:web",
  }
]

@ramizpolic ramizpolic Aug 27, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for raising this @jdamick. I patched the spec to use an array of Identity Alias objects mirroring the canonical identity/identityType pair. This also covers your use case as consumers can pick the alias by explicit type rather than inferring from schemes (which is not always possible as described in #91 (comment)).

{
"identity": "did:web:acme-corp.com:agent:finance",
"identityType": "did"
}
],
"signature": "eyJhbGciOiJFUzI1NiJ9..detached-jws-signature"
}
```

Normative rules:

1. `identity` remains the single **canonical** subject identifier, used for referencing and equivalence checking. Aliases are co-equal for verification purposes but never canonical. Aliases MAY also record former identifiers of the subject, preserving continuity after a rename or a migration between identities or schemes.
2. `alsoKnownAs` MUST NOT contain two aliases with the same `identity` value, and no alias `identity` may equal the canonical `identity`; element order carries no significance.
3. Each alias MAY carry its own `identityType` hint, OPTIONAL when the type is evident from the URI scheme. The top-level `identityType` describes only the canonical `identity`.
4. The domain-alignment rule applies only to `identity`. Aliases MAY belong to different trust domains or identity schemes.
5. Aliases are publisher claims verified through the existing Trust Manifest `signature`, which covers `alsoKnownAs` as manifest content. Consumers MUST NOT rely on an alias from a manifest whose signature is absent or fails verification. Because the signature proves the publisher claims the alias (not that the alias's trust domain acknowledges the link), authorization decisions inside the alias's trust domain SHOULD additionally rely on proof of control native to the alias scheme (e.g., a DID Document back-reference or DNS TXT record); such proofs are out of scope.
6. A consumer MAY select any verified identity, canonical or alias, matching the schemes it can resolve, rather than rejecting an entry whose canonical identity uses an unsupported scheme. Signature verification itself always keys off the canonical `identity`.

### Placement: inside the Trust Manifest, not on the Catalog Entry
We explicitly considered placing `alsoKnownAs` on the parent Catalog Entry (as a sibling of `identifier`) and rejected it:

- **Signature coverage is decisive.** The Trust Manifest is the only signed unit in the specification, the detached JWS is computed over the JCS-canonicalized manifest content. Fields on the Catalog Entry are not covered by any signature, so under the spec's catalog-poisoning threat model an attacker who can modify the catalog document could inject or strip entry-level aliases undetected. Inside the manifest, aliases are forgery-proof at trust Layer 2, satisfying the "verifiable through the trust bundle" constraint.
- **Separation of concerns (ADR-0015).** The entry's `identifier` is a logical *name* (`urn:air:...`) for discovery and routing; cryptographic *identities* live in the Trust Manifest. Alternative identities are identities, so they belong beside `identity`, not beside the URN.
- **Ecosystem precedent.** W3C DID Core defines `alsoKnownAs` on the DID Document with the same publisher-asserted, same-subject semantics adopted here. (DID Core uses plain URIs; this specification adopts a structured object so each alias can carry an explicit `identityType` hint, mirroring the canonical `identity`/`identityType` pair.)
- **Reuse for hosts.** Host Info carries a `trustManifest` too, so hosts gain multi-identity support without adding a new field to two parent structures.

The trade-off is that entries without a Trust Manifest cannot declare aliases. This is acceptable because an alias outside the trust bundle would be unverifiable by construction.

## Rationale
- **Interoperability**: A standard, first-class shape means consumers can programmatically discover alternate identities instead of relying on per-publisher attestation or metadata conventions.
- **Simple equivalence checking**: Keeping a single canonical `identity` avoids forcing consumers to choose which of N identities "names" the entry.
- **Backward compatible**: `alsoKnownAs` is OPTIONAL and additive. Existing manifests remain valid; existing consumers that ignore the field lose nothing they had before.
- **Verifiability by construction**: Because the field lives inside the signed manifest content, no new signing mechanism is needed, the existing JCS + detached JWS procedure covers it.

## Alternatives Considered
- **`identities[]`: an array of co-equal Identity objects** (each with `identity`, `identityType`, and optionally its own `signature`). Rejected: it makes equivalence checking harder, forces consumers to choose which identity to use when referencing the entry, and multiplies signature-verification paths. The variant dropping the primary `identity` entirely was also rejected as a breaking change to the existing spec. The adopted Identity Alias object retains the `identity`/`identityType` pair per alias, but keeps a single canonical `identity` and a single signature path.
- **`alsoKnownAs` as a plain URI array** (the W3C DID Core shape). Rejected: consumers would have to infer each alias's type from its URI scheme alone, which is ambiguous for schemes shared by multiple identity types (e.g., an `https:` URI may point to a JWK Set or be a `did:web` equivalent), and inconsistent with the explicit `identityType` hint available for the canonical identity.
- **`trustManifests[]`: multiple Trust Manifests per entry.** Fully independent trust metadata per identity (separate attestations, provenance, signatures). Rejected as disproportionate: the motivating use cases need equivalent identities for one subject, not parallel trust bundles, and multiple manifests reintroduce the "which manifest is authoritative?" problem at a larger scale.
- **`alsoKnownAs` on the Catalog Entry (parent structure).** Rejected for the placement reasons above, mainly that entry-level fields fall outside the signed trust bundle and therefore cannot satisfy the verifiability requirement.
- **Status quo (attestations/metadata workarounds).** Rejected as non-interoperable and non-verifiable, per the Context section.

## Consequences
- **Specification**: The Trust Manifest optional members, verification procedures (new "Verifying Alternative Identities" section), CDDL schema, data model diagram, and examples are updated.
- **Consumers**: Clients that verify Trust Manifest signatures automatically gain tamper-proof alias coverage. Clients unaware of the field are unaffected.
- **Publishers**: Publishers currently smuggling secondary identities through `attestations[]` or `metadata` SHOULD migrate them to `alsoKnownAs`.
- **Future work**: If per-alias proofs become necessary (e.g., alias-side attestations or inline alias-specific signatures), they can be added as OPTIONAL members of the Identity Alias object without breaking the existing form.

## Meeting Reference
Proposed from [issue #52](https://github.com/Agent-Card/ai-catalog/issues/52) discussion; `alsoKnownAs` was favored over an identities array in issue comments (2026-07). Update the Status and Date once the working group ratifies it.
1 change: 1 addition & 0 deletions docs/guides/adding-trust.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ All other fields are optional:
| Field | Description |
|---|---|
| `identityType` | Type hint for the identity URI: `"did"`, `"spiffe"`, `"dns"` |
| `alsoKnownAs` | Array of Identity Alias objects declaring alternative identities of the same subject |
| `trustSchema` | Describes the trust framework applied |
| `attestations` | Array of compliance and identity attestation objects |
| `provenance` | Array of provenance links (source code, OCI digests) |
Expand Down
103 changes: 101 additions & 2 deletions specification/ai-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,16 @@ 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.

An artifact may legitimately hold more than one verifiable identity at
the same time, for example a SPIFFE ID for runtime workload identity
and a DID for publisher-anchored organizational identity, or it may
retain a former identifier after a rename or migration between
identities or schemes. Additional identities are declared through the
OPTIONAL `alsoKnownAs` member (see [Optional Members](#optional-members)).
The `identity` field remains the single canonical subject identifier:
consumers MUST use `identity` when referencing the artifact's trust
subject or checking identity equivalence between entries.

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
Expand Down Expand Up @@ -561,9 +571,46 @@ present.
The following members are OPTIONAL:

`identityType`
: A string providing a type hint for the identity URI (e.g., "did",
: A string providing a type hint for the `identity` URI (e.g., "did",
"spiffe", "dns"). This field is OPTIONAL when the type is evident
from the URI scheme.
from the URI scheme. It describes only `identity`; each entry of
`alsoKnownAs` carries its own `identityType` member.

`alsoKnownAs`
: An array of Identity Alias objects, each asserting an identity of
the same subject as `identity` under an alternative identity
scheme. An Identity Alias object MUST contain:

`identity`
: A string containing a globally unique URI [[RFC3986]] that
identifies the same subject as the manifest's canonical
`identity`.

An Identity Alias object MAY contain:

`identityType`
: A string providing a type hint for the alias `identity` URI
(e.g., "did", "spiffe", "dns"). This member is OPTIONAL when
the type is evident from the URI scheme.

The following rules apply:

- An alias is an equivalent identity of the subject; the
manifest's `identity` alone remains canonical and is used for
referencing and equivalence checking. Aliases MAY also record
former identifiers of the subject, preserving continuity after
a rename or a migration between identities or schemes.
- The array is an unordered set: it MUST NOT contain two aliases
with the same `identity` value, and no alias `identity` may
equal the manifest's canonical `identity`.
- The domain-alignment rule defined in [Identity](#identity)
applies only to the canonical `identity`. Aliases MAY belong to
different trust domains or identity schemes.
- Aliases are publisher claims covered by the Trust Manifest
`signature`; no per-alias proof is required. Consumers MUST NOT
rely on an alias from a manifest whose signature is absent or
fails verification (see
[Verifying Alternative Identities](#verifying-alternative-identities)).

`trustSchema`
: A Trust Schema object as defined in [Trust Schema](#trust-schema-object).
Expand Down Expand Up @@ -621,6 +668,12 @@ provenance:
{
"identity": "did:web:acme.com:agent:finance",
"identityType": "did",
"alsoKnownAs": [
{
"identity": "spiffe://acme.com/ns/finance/sa/finance-agent-pod",
"identityType": "spiffe"
}
],
"trustSchema": {
"identifier": "urn:trust:acme-enterprise-v1",
"version": "1.0",
Expand Down Expand Up @@ -972,6 +1025,34 @@ Manifest signature. Consumers MUST treat `publisher` fields as advisory
unless a verified `publisher-identity` attestation cryptographically
binds `publisher.identifier` to the signed manifest's `identity`.

### Verifying Alternative Identities

Aliases need no verification procedure of their own. Because the
signed payload covers `alsoKnownAs`, verifying the Trust Manifest
`signature` as described in
[Trust Manifest Signatures](#trust-manifest-signatures) also verifies
every listed alias: a valid signature proves that the publisher
controlling the canonical `identity` claims each alias as an
equivalent identity of the subject. Consumers MUST NOT rely on aliases
from a manifest whose signature is absent or fails verification.

Signature verification itself always keys off the canonical identity:
the verification key is resolved from `identity` as described in
[Trust Manifest Signatures](#trust-manifest-signatures). A consumer
that cannot resolve the canonical identity scheme cannot verify the
signature and therefore cannot rely on any alias.

Once the signature is verified, a consumer MAY use whichever identity,
canonical or alias, fits the identity schemes its tooling operates
on. For example, after verifying the signature through a resolvable
canonical DID `identity`, the consumer can use a SPIFFE ID alias to
match the artifact's runtime workload identity.

Note that the signature proves the publisher claims the alias, not
that the alias's own trust domain acknowledges the link. Consumers
making authorization decisions inside the alias's trust domain MAY
additionally obtain proof of control native to the alias scheme.

### Verifying Artifact Integrity

When a Trust Manifest carries a `signature`, it MUST include a `subject`
Expand Down Expand Up @@ -1537,6 +1618,7 @@ classDiagram
class TrustManifest {
identity string
subject Subject
alsoKnownAs IdentityAlias[]
trustSchema TrustSchema
attestations Attestation[]
provenance ProvenanceLink[]
Expand All @@ -1548,6 +1630,10 @@ classDiagram
type string
digest string
}
class IdentityAlias {
identity string
identityType string
}
class TrustSchema {
identifier string
version string
Expand All @@ -1569,6 +1655,7 @@ classDiagram
CatalogEntry --> "0..1" TrustManifest : trustManifest
HostInfo --> "0..1" TrustManifest : trustManifest
TrustManifest --> "0..1" Subject : subject
TrustManifest --> "*" IdentityAlias : alsoKnownAs
TrustManifest --> "0..1" TrustSchema : trustSchema
TrustManifest --> "*" Attestation : attestations
TrustManifest --> "*" ProvenanceLink : provenance
Expand Down Expand Up @@ -1709,6 +1796,7 @@ Publisher = {
TrustManifest = {
identity: text,
? identityType: text,
? alsoKnownAs: [* IdentityAlias],
? trustSchema: TrustSchema,
? attestations: [* Attestation],
? provenance: [* ProvenanceLink],
Expand All @@ -1718,6 +1806,11 @@ TrustManifest = {
? extensions: { * text => any }
}

IdentityAlias = {
identity: text,
? identityType: text
}

TrustSchema = {
identifier: text,
version: text,
Expand Down Expand Up @@ -1771,6 +1864,12 @@ artifact types including a nested catalog packaging related artifacts:
"trustManifest": {
"identity": "spiffe://acme.com/ns/finance/sa/finance-a2a-pod",
"identityType": "spiffe",
"alsoKnownAs": [
{
"identity": "did:web:acme-corp.com:agent:finance-a2a",
"identityType": "did"
}
],
"attestations": [
{
"type": "publisher-identity",
Expand Down
16 changes: 15 additions & 1 deletion specification/respec-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,28 @@
"shortName": "ai-catalog",
"edDraftURI": "https://ai-catalog.io/",
"abstract": "This document defines the AI Catalog, a JSON format for discovering heterogeneous AI artifacts such as MCP servers, A2A agents, Claude Code plugins, datasets, and model cards. Each catalog entry declares the artifact's type via a media type and references or inlines the native artifact metadata, enabling a single discovery mechanism across protocols and platforms. The specification defines three conformance levels — Minimal, Discoverable, and Trusted — allowing implementations to start with a simple list of entries and progressively add host identity, well-known URI discovery, and verifiable trust metadata as needed. An optional Trust Manifest extension provides identity binding, compliance attestations, provenance tracking, and cryptographic signatures without wrapping or modifying the artifact's native format. Informative appendices define a substrate-neutral distribution-mapping contract and concrete bindings to OCI distribution registries and xRegistry, plus mappings to the MCP Registry server.json format and the Claude Code Plugins marketplace.",
"appendixHeaders": ["Data Model", "CDDL", "Example", "Mapping", "Acknowledgment", "Appendix", "IANA"],
"appendixHeaders": [
"Data Model",
"CDDL",
"Example",
"Mapping",
"Acknowledgment",
"Appendix",
"IANA"
],
"localBiblio": {
"xRegistry": {
"title": "xRegistry: A Universal Registry Specification",
"href": "https://github.com/xregistry/spec",
"status": "Draft",
"publisher": "CNCF"
},
"RFC3986": {
"title": "Uniform Resource Identifier (URI): Generic Syntax",
"href": "https://www.rfc-editor.org/rfc/rfc3986",
"status": "RFC",
"publisher": "IETF"
},
"RFC8785": {
"title": "JSON Canonicalization Scheme (JCS)",
"href": "https://www.rfc-editor.org/rfc/rfc8785",
Expand Down
Loading