Skip to content

Spec Proposal: Registry Federation Interoperability — Mutual Trust & Cross-Registry Provenance #53

Description

@ejoaquin

The Problem

As agents proliferate across enterprise boundaries, the same logical agent increasingly appears in multiple registries — scanned, imported, or federated. Today, ARD defines discovery within a single catalog but has no mechanism for:

  1. Bilateral trust between registries — When Registry B scans agents from Registry A, there is no protocol for Registry A (the publisher) to approve, scope, or revoke that relationship. The scanned party has no voice. There is no equivalent of the browser "padlock" that signals to end-users whether a federated entry is verified and consented-to by its source.

  2. Provenance tracking for federated entries — When the same agent exists in multiple registries, consumers cannot determine which is the authoritative (canonical) source, who maintains it, or whether they're looking at a stale copy. This leads to duplicated metadata, inconsistent lifecycle states, and unclear accountability.

  3. Cross-registry lifecycle and version sync — Deprecating an agent in its source registry does not propagate to registries that federated it. Consumers in downstream registries continue discovering sunset agents with no signal that the canonical source has moved on.

These gaps become acute in B2B scenarios where organizations expose agents to partners (e.g., a cloud provider's agents federated into a customer's enterprise registry, or vice versa).

Prior Art & Dependencies

This proposal builds directly on existing community work:

Design Principle: Dual Trust Anchors (DNS + DID)

This proposal is intentionally designed to serve both trust models present in the ARD community:

1. DNS-anchored enterprise model (aligns with #42, #44, #45):

  • Trust negotiation via /.well-known/ard-approvals.json
  • Publisher identity anchored to a corporate domain (urn:air:acme.com:...)
  • Verification method: DNS ownership proof, TLS certificate, HTTPS endpoint
  • Use case: Enterprise B2B federation (e.g., cloud provider ↔ customer registry)

2. DID-based decentralized model (aligns with #47):

  • Trust negotiation via DID Document service endpoints (when publisher has no DNS domain)
  • Publisher identity anchored to a cryptographic key (did:plc:abc123...)
  • Verification method: DID resolution + signature verification
  • Use case: Independent publishers, atproto-native agents, cross-platform federation without corporate domain requirements

How they coexist:

The mutualTrust.verificationMethod field declares which model is in use:

  • "domain-dns" → resolve via /.well-known/ard-approvals.json
  • "did-service" → resolve via DID Document service endpoint of type ArdApprovals

Both paths produce the same trust state machine (unverified → one_way → mutual → revoked). The federation protocol is agnostic to which anchor is used — it cares about the outcome (bilateral consent verified) not the mechanism (DNS vs. DID).

This dual-anchor design ensures the spec does not structurally exclude domainless publishers (#47) while remaining fully functional for the DNS-anchored enterprise majority today.

Federation Lifecycle: DISCOVER → AGREE → ENFORCE

This proposal frames federation as a three-phase governance lifecycle. DISCOVER is the ARD base spec — catalog format, search API, and identity verification. This proposal adds the AGREE and ENFORCE layers that make federation governable:

Phase What It Resolves Spec Components
DISCOVER What exists? Where does it live? Who published it? What versions are available? ARD ai-catalog.json, POST /search, ANS/DNS identity, cardProvenance, contentIntegrity, releasePolicy — publisher-declared and registry-recorded metadata
AGREE Who consents to federate? What scope and operational terms apply? mutualTrust (state machine + handshake), executionContract (usage policy, data handling, quota terms) — negotiated bilaterally, then recorded in the catalog
ENFORCE Is this specific invocation authorized right now? Runtime policy gates consume the agreed terms (from AGREE) and verification metadata (from DISCOVER) — identity propagation, quota governance, trust state checks, observability

DISCOVER (ARD) establishes what CAN be reached. AGREE (this proposal) establishes the business contract that defines what IS permitted. ENFORCE (runtime) guarantees that runtime behavior conforms to what was agreed — or the call is rejected.

Field lifecycle across phases:

All federation metadata fields are defined in the catalog (Agent Card schema) at the DISCOVER layer — this is where their structure lives and where consumers first see them. However, they have different authoring models:

Field Defined at DISCOVER? Agreed at AGREE? Consumed at ENFORCE? Authoring Model
cardProvenance ✅ Published in card ✅ Verified Registry-recorded (consuming registry writes origin when federating)
contentIntegrity ✅ Published in card ✅ Verified Registry-computed (consuming registry hashes at sync time)
releasePolicy ✅ Published in card ✅ Version checked Publisher-declared (unilateral — publisher states what versions exist)
mutualTrust ✅ Field exists in card ✅ State finalized after handshake ✅ Trust state checked Bilateral — field is defined in the catalog, but value only becomes mutual after both parties complete REQUEST → APPROVE → CONFIRM
executionContract ✅ Signaled in card (hasQuotaGovernance, usagePolicyRef) ✅ Full terms negotiated ✅ Terms enforced Bilateral — card advertises that terms exist; specific terms are agreed during handshake

All fields live in the catalog. DISCOVER-layer fields are authored unilaterally (publisher declares or registry records). AGREE-layer fields are defined in the catalog schema but finalized through bilateral negotiation — their values update upon completion of the contract handshake.

Scope note: This proposal stays within ARD's "What can I find?" role. It identifies the metadata fields the catalog needs to carry so that downstream governance systems (AGREE, ENFORCE) have what they depend on, and specifies when those fields update. AGREE and ENFORCE themselves — how consent is negotiated, how terms are enforced at runtime — remain outside ARD's scope. They are defined here as open protocol extensions for registry implementations (e.g., MCP Gateway Registry #40, enterprise-managed registries, platform registries) to adopt alongside the ARD base spec.

Proposal

Part 1: Mutual Trust Protocol (mutualTrust)

Add an optional mutualTrust object to catalog entries that establishes bilateral consent between a publishing registry and a consuming/federating registry.

State Machine:

unverified → one_way → mutual → [revoked]
  • unverified — Entry discovered but no trust relationship exists
  • one_way — One party has verified the other (e.g., scanner verified publisher's domain) but consent is not bilateral
  • mutual — Both parties have verified and approved the relationship
  • revoked — Trust withdrawn by either party

Publisher Approval Endpoint:

Trust Handshake Mechanism:

The trust establishment follows a request → approve → confirm flow where both parties are explicitly identified at each step:

Step 1: REQUEST
  Consuming Registry (partner.com) → Publisher (acme.com)
  "I want to federate your agents in scope [finance:*]"

Step 2: APPROVE
  Publisher (acme.com) → adds entry to /.well-known/ard-approvals.json
  "I approve partner.com to federate my finance agents"

Step 3: CONFIRM
  Consuming Registry (partner.com) → publishes /.well-known/ard-federation-requests.json
  "I confirm I am federating acme.com's finance agents"

Result: state = "mutual" (both parties have published verifiable consent)

Requesting Party Record — The consuming registry hosts /.well-known/ard-federation-requests.json:

{
  "schemaVersion": "1.0",
  "registry": "urn:air:partner.com:registry:main",
  "registryDisplayName": "Partner Corp Agent Registry",
  "federationRequests": [
    {
      "publisherIdentifier": "urn:air:acme.com",
      "publisherDomain": "acme.com",
      "requestedScope": ["urn:air:acme.com:finance:*"],
      "requestedVersions": "stable",
      "requestedAt": "2026-04-15T00:00:00Z",
      "purpose": "Enable our sales agents to discover and invoke Acme's finance tools",
      "contactEmail": "platform-team@partner.com"
    }
  ]
}

Approving Party Record — The publisher hosts /.well-known/ard-approvals.json:

{
  "schemaVersion": "1.0",
  "publisher": "urn:air:acme.com",
  "publisherDisplayName": "Acme Corp",
  "approvedRegistries": [
    {
      "registryIdentifier": "urn:air:partner.com:registry:main",
      "registryDisplayName": "Partner Corp Agent Registry",
      "registryDomain": "partner.com",
      "scope": ["urn:air:acme.com:finance:*"],
      "versionPolicy": "stable-only",
      "approvedAt": "2026-05-01T00:00:00Z",
      "approvedBy": "platform-admin@acme.com",
      "expiresAt": "2027-05-01T00:00:00Z",
      "trustDuration": "time-bound",
      "constraints": {
        "allowInvocation": true,
        "allowRedistribution": false
      }
    }
  ]
}

Mutual Verification:

A registry verifying trust state MUST check both endpoints:

  1. Publisher's ard-approvals.json contains the consuming registry → confirms publisher consent
  2. Consumer's ard-federation-requests.json contains the publisher → confirms consumer intent

When both are present and scopes align → state: "mutual". If only one side has published → state: "one_way". Both parties are explicitly named and verifiable in their respective records.

Version Selection in Federation:

When multiple agent versions are available (see companion proposal: Agent Release Policy), both parties can scope federation to specific versions:

Consumer siderequestedVersions in ard-federation-requests.json:

Value Meaning
"all" Federate all available versions (stable, deprecated, preview)
"stable" Only federate versions with status: "stable"
"stable+preview" Include preview/beta versions for early integration testing
">=2.0.0" Semver range — only federate versions matching this constraint
["2.0.0", "3.0.0-beta"] Explicit list — only these specific versions

Publisher sideversionPolicy in ard-approvals.json:

Value Meaning
"all" Consumer may federate any available version
"stable-only" Consumer may only federate stable releases (no preview/beta)
"current" Consumer may only federate the latest stable version (auto-upgrades on sync)
">=2.0.0" Semver floor — consumer may federate any version at or above this
["2.0.0", "2.1.0"] Explicit allowlist — only these versions may be federated

Resolution rule: The effective federated versions are the intersection of:

  1. What the consumer requested (requestedVersions)
  2. What the publisher approved (versionPolicy)
  3. What is actually available (availableVersions from the Release Policy)

Example: Publisher approves "stable-only", consumer requests ">=2.0.0", and the agent has versions 1.5.2 (stable), 2.0.0 (stable), 3.0.0-beta (preview) available → only v2.0.0 is federated.

Sync behavior when versions change:

  • Publisher releases new stable version → automatically federated if it satisfies both policies
  • Publisher deprecates a version → federated copy is marked deprecated on next sync
  • Publisher publishes preview → only federated if both sides allow preview
  • Consumer narrows their requestedVersions → excess versions removed on next sync

This ensures both parties retain control: the publisher decides what CAN be shared, and the consumer decides what they WANT.

Trust Duration Model:

Publishers MUST explicitly declare whether trust is indefinite or time-bound:

trustDuration Behavior
indefinite Trust remains valid until explicitly revoked by the publisher. No expiration. expiresAt is omitted or null.
time-bound Trust is valid only until expiresAt. After expiry, the federating registry MUST treat the relationship as expired (not revoked) and MAY attempt re-verification.
auto-renewing Trust auto-renews at expiresAt unless the publisher revokes. Consuming registries SHOULD re-validate the /.well-known/ard-approvals.json endpoint at renewal time to confirm continued approval.

Expiration vs. Revocation:

These are distinct states with different consumer implications:

  • Expired (trustDuration: "time-bound" + past expiresAt) — Trust lapsed. The federation relationship may be re-established by the consuming registry requesting re-approval. Federated entries SHOULD be flagged as stale but not removed.
  • Revoked (publisher removes entry from ard-approvals.json or sets "revoked": true) — Trust actively withdrawn. Federating registries MUST stop serving the affected entries and SHOULD notify administrators.

Example: Indefinite trust

{
  "registryIdentifier": "urn:air:strategic-partner.com:registry:main",
  "scope": ["urn:air:acme.com:*"],
  "approvedAt": "2026-01-01T00:00:00Z",
  "trustDuration": "indefinite",
  "constraints": {
    "allowInvocation": true,
    "allowRedistribution": true
  }
}

Catalog Entry Extension:

{
  "identifier": "urn:air:acme.com:finance:invoice-processor",
  "mutualTrust": {
    "state": "mutual",
    "publisherApprovalUri": "https://acme.com/.well-known/ard-approvals.json",
    "verifiedAt": "2026-06-01T12:00:00Z",
    "verificationMethod": "domain-dns",
    "revalidationInterval": "P30D"
  }
}

Visual Indicator:

Registries SHOULD render a trust indicator (analogous to the browser padlock) combining trust state AND content integrity:

  • 🔒 mutual + content fresh — Full confidence: both parties verified, content matches canonical
  • 🔒⚠️ mutual + content stale — Trusted relationship, but copy may have drifted; verify before relying
  • ⚠️ one_way — Partially verified
  • expired — Trust lapsed; content may be outdated
  • revoked — Trust withdrawn

Part 2: Agent Card Provenance (cardProvenance)

Add an optional cardProvenance object that tracks where an entry lives relative to its authoritative source:

{
  "identifier": "urn:air:acme.com:finance:invoice-processor",
  "cardProvenance": {
    "residency": "federated",
    "canonicalSource": {
      "registryUri": "https://registry.acme.com",
      "registryIdentifier": "urn:air:acme.com:registry:main",
      "lastSyncedAt": "2026-06-20T08:00:00Z",
      "syncProtocol": "ard-federation/1.0"
    },
    "owner": {
      "organization": "Acme Corp",
      "contact": "platform-team@acme.com",
      "identityUri": "did:web:acme.com"
    },
    "lifecycle": {
      "status": "active",
      "publishedAt": "2026-03-15T00:00:00Z",
      "lastModifiedAt": "2026-06-15T00:00:00Z"
    }
  }
}

Residency Enum:

Value Meaning
owned This registry is the canonical/authoritative source
federated Entry imported from a trusted external registry; metadata resolves from canonical source on sync
mirrored Read-only copy cached for availability; always defers to canonical

Anti-Duplication Rule:

Registries federating an entry MUST NOT independently modify mutable metadata fields (lifecycle, capabilities, description). These MUST be resolved from the canonicalSource during sync. Only registry-local annotations (e.g., internal tags, access policies) may be added.

Lifecycle Extension (building on #45):

When the canonical source updates lifecycle state (e.g., deprecated), federating registries MUST propagate this within one sync interval. This ensures consumers in downstream registries are never unknowingly using a deprecated agent.

Part 2b: Content Integrity (contentIntegrity)

Trust and freshness are orthogonal axes. A fully mutual, non-expired federation can serve a card that's silently diverged from canonical — because the trust state machine governs relationship health, not content accuracy. To close this gap, add a contentIntegrity object as a sibling to mutualTrust inside cardProvenance:

{
  "identifier": "urn:air:acme.com:finance:invoice-processor",
  "cardProvenance": {
    "residency": "federated",
    "canonicalSource": { "..." },
    "contentIntegrity": {
      "canonicalDigest": "sha256:a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
      "digestScope": "full-card",
      "lastReconciled": "2026-06-28T08:00:00Z",
      "reconciliationInterval": "PT6H",
      "driftDetected": false
    },
    "owner": { "..." },
    "lifecycle": { "..." }
  }
}

Field Definitions:

Field Type Description
canonicalDigest string Hash of the canonical card content at last reconciliation (algorithm-prefixed, e.g. sha256:...)
digestScope enum What's hashed: full-card (the complete metadata envelope, excluding localAnnotations) or bundle (the underlying artifact/payload the consumer actually invokes)
digestExclusions array Fields structurally excluded from digest computation. Default: ["localAnnotations"]
lastReconciled datetime When this digest was last verified against the live canonical source
reconciliationInterval duration (ISO 8601) How frequently this copy is reconciled against canonical
driftDetected boolean Whether the last reconciliation found the local copy diverged from canonical

Why this is separate from trust:

A trusted federation serving a stale card is normal operation (sync interval hasn't elapsed yet), not a trust failure. Folding freshness into the trust states (unverified → one_way → mutual → revoked) would conflate two independent concerns. A card can be simultaneously:

  • Trust = mutual (relationship healthy) + Content = fresh (digest matches) → ✅ Full confidence
  • Trust = mutual (relationship healthy) + Content = stale (digest mismatch) → ⚠️ "Trusted but verify"
  • Trust = revoked → ❌ Don't use, regardless of content state

Verification by any party:

Any consumer or third-party auditor can independently verify a federated card:

  1. Fetch the canonical card from canonicalSource.registryUri
  2. Compute hash over the card content (per digestScope)
  3. Compare to canonicalDigest
  4. If mismatch → the copy has drifted; check lastReconciled to assess staleness

This is the load-bearing part of the padlock: the indicator attests content, not just relationship.

Composability:

Part 2c: Registry-Local Annotations (localAnnotations)

A federating registry often needs to attach operational metadata to a card: internal tags, access policy, risk labels, routing hints, SLA tiers, or "approved for this workspace" state. These fields are valuable to the host registry's consumers but are not authored by the publisher and should never be confused with canonical content.

The localAnnotations block provides a structurally explicit boundary for registry-local metadata. It is the only top-level field excluded from digestScope: "full-card" computation:

{
  "identifier": "urn:air:acme.com:finance:invoice-processor",
  "name": "Invoice Processor",
  "capabilities": ["..."],
  "cardProvenance": {
    "residency": "federated",
    "canonicalSource": { "..." },
    "contentIntegrity": {
      "canonicalDigest": "sha256:a3f2b8c9...",
      "digestScope": "full-card",
      "digestExclusions": ["localAnnotations"],
      "lastReconciled": "2026-06-28T08:00:00Z",
      "driftDetected": false
    }
  },

  "localAnnotations": {
    "addedBy": "registry.partner.com",
    "addedAt": "2026-07-01T10:00:00Z",
    "fields": {
      "internalPriority": "high",
      "riskLabel": "low",
      "approvedForWorkspace": true,
      "slaOverride": "gold",
      "routingHint": "us-west-2"
    }
  }
}

Field Definitions:

Field Type Description
addedBy string Registry identifier (domain or DID) that authored these annotations. Establishes accountability.
addedAt datetime When the annotations were last modified
fields object Arbitrary key-value pairs scoped to the host registry. Schema is registry-defined, not spec-prescribed.

Design rationale — two complementary boundaries:

The digest and localAnnotations solve the same boundary problem at two different layers:

  1. Cryptographic boundary (canonicalDigest) — machine-verifiable. Proves publisher content is intact. Detects tampering or drift.
  2. Structural boundary (localAnnotations) — developer-visible. Makes the canonical/local split obvious without requiring consumers to re-hash.

A downstream runtime can now answer two questions without computation:

  • Is this field canonical? → If it's outside localAnnotations, yes.
  • Who is accountable for this field? → If it's inside localAnnotations, the addedBy registry.

Without this structural split, a consuming runtime may treat a local risk label, trust badge, or policy note as if the publisher authored it. With the split, the padlock stays over the canonical bytes, and registries can enrich cards for operational needs without mutating the source-of-truth.

Constraint: Registries MUST NOT place publisher-originated fields inside localAnnotations, and MUST NOT add registry-local fields outside it. Violating either direction breaks the provenance guarantee.

Part 3: Federation Sync (Informative)

While the full wire-level sync protocol is beyond this initial proposal, the following semantics are implied:

  • Registries SHOULD support periodic polling of canonical sources for metadata updates
  • Registries MAY support event-push (webhook) for real-time propagation
  • Conflict resolution: canonical source ALWAYS wins for mutable fields
  • Sync failures SHOULD be surfaced to registry administrators

Extension: Execution Contract & Usage Governance

The execution contract defines the operational terms enforced at runtime — including usage governance. A separate companion proposal defines the usagePolicy object for token quota governance at federation boundaries:

The Agent Card signals execution contract terms at discovery time via an executionContract block:

{
  "executionContract": {
    "hasQuotaGovernance": true,
    "degradationModes": ["informational-only", "block"],
    "usagePolicyRef": "https://registry.acme.com/contracts/invoice-agent/usage"
  }
}

This enables consumers to see that quota governance exists before federating, while the full terms are negotiated during the AGREE phase.

Discovery Value

  • Trust at a glance: Consumers can filter to only mutual-trust entries — ensuring every agent they discover has been approved by its publisher for their registry
  • Single source of truth: Federated entries always resolve back to canonical, eliminating stale copies
  • Cross-org governance: Organizations retain control over which partners can federate their agents, with revocation capability
  • Audit trail: When + how trust was established is recorded and verifiable
  • Lifecycle consistency: Deprecation in one place means deprecation everywhere

Relationship to Existing Spec Sections

Section Interaction
§5.1 (Identity) mutualTrust.verificationMethod builds on existing domain/DID verification
§5.2 (Attestations) Complementary — attestations prove claims; mutualTrust proves relationships
§3.3 (Artifact Envelope) cardProvenance is a new optional top-level field alongside trustManifest
§7.1 (Search Facets) mutualTrust.state and cardProvenance.residency are natural new facets

Discussion Questions

  1. Should mutualTrust live inside trustManifest (extending §5) or as a sibling top-level object?
  2. Should cardProvenance.lifecycle be merged with or reference Spec Proposal: No Lifecycle or Deprecation Model #45's lifecycle proposal — or should they be the same object?
  3. For DID-based publishers (Spec proposal: recognize did:plc identity method for domainless publishers (§5.1 resolution + Appendix C URN branch) #47), should the approval endpoint move from .well-known to a DID Document service entry?
  4. Should the anti-duplication rule be normative (MUST) or advisory (SHOULD) for v1.0?
  5. Is there interest in a formal Federation Sync Protocol as a separate spec extension, or should this remain informative guidance?
  6. Should usagePolicy quota enforcement be publisher-side only, or should the spec define a consumer-side pre-check mechanism (e.g., GET /quota/status) to avoid rejected calls?

Reference Implementation

The MCP Gateway and Registry (Apache 2.0, #40) already implements peer-to-peer federation with canonical source tracking and could serve as the reference implementation for this proposal.

Spec updated (July 8, 2026):

  • Added "Federation Lifecycle: DISCOVER → AGREE → ENFORCE" framing section
  • Added Part 2c: Registry-Local Annotations (localAnnotations) — structural boundary for registry-added metadata, excluded from digestScope
  • Added digestExclusions field to contentIntegrity
  • Added "Extension: Execution Contract & Usage Governance" with pointer to companion proposal

These additions incorporate feedback from @olijboyd (content integrity / trust ≠ freshness separation) and @0xbrainkid (publisher-authored vs. registry-local provenance boundary). No breaking changes to previously defined fields.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions