skip to content
updated view markdown ->

Generated from agentregistrar/spec at v1.2.0 (tag v1.2.0). Do not edit this page; edit the spec and re-run pnpm sync:spec.

Agent Identity Relying Party Discovery v1 (agent-signup.json)

  • Status: Normative specification — v1.2.0. Maintained in agentregistrar/spec.
  • Protocol identifier: agent-identity-v1
  • Well-known URI: GET /.well-known/agent-signup.json
  • Version: v1.2.0 (see §1.3)
  • Companion document: scope-boundaries.md — the identity-vs-vendor boundary and the future-change review checklist.

This document is the single normative definition of the agent-signup.json discovery contract and the brokered signup handshake. The historical reference implementation's documentation carries a short summary that points here. Where any other document disagrees with this specification, this specification wins for identity-only v1.


1. Status, Version, Changelog & Normative Language

1.1 Scope of this document

This specification defines:

  • the discovery document served at GET /.well-known/agent-signup.json (Contract D.1);
  • the ToS binding model advertised by the document and enforced at signup;
  • the POST endpoints.agent_signup handshake, including stable error codes;
  • the identity facts of the signup receipt (Contract E) and the optional post-signup context pointer;
  • the optional broker-issued RP attestation (Contract G), the provider registration/status API that issues it (Contract H), and the broker metadata + attestation status surface (Contract I);
  • authenticated abuse reporting (Contract D.2) and authenticated receipt ingestion (Contract F);
  • the trust model, assurance levels, verification rules, and the status/error taxonomy;
  • the rules agents and Relying Parties MUST follow to be conformant.

The Agent Identity JWT assertion format itself is Contract A and is not redefined here. This specification only defines how an assertion is discovered and presented during brokered signup. MCP OAuth discovery (RFC 9728) and the Broker MCP API are also out of scope except where referenced for context. Contract F is defined here as a broker endpoint profile; MCP transport remains out of scope.

1.2 Changelog

Version Date Change
v1.2.0 2026-10-24 Adds Contracts G/H/I, optional rp_attestation, authenticated D.2, RP-signed E, authenticated F, the trust model, assurance levels, verification rules, and the status/error taxonomy. The wire protocol id stays agent-identity-v1; rp_attestation is OPTIONAL and v1.0.0/v1.1.0 documents remain conformant.
v1.1.0 2026-10-18 Additive clarifications: replay vs resolve (idempotent resolution, assertion_replayed reserved for conflicting reuse), the optional resolution: "created" | "resolved" success field, acceptance lifetime, a knowledge-of-text restatement, recommended account identity keys (§9.1), and credential handoff & recovery (§12.5). No field removals or renames; the wire protocol id stays agent-identity-v1; v1.0.0 implementors remain conformant.
v1.0.0 2026-10-17 Initial identity-only v1 specification. Discovery is identity-only; vendor catalogs, provisioning endpoints, legacy OTP endpoints, and broker receipt callbacks are removed from the protocol and are forbidden. Adds the optional signed post-signup context pointer.

Release record. v1.0.0 was recorded on 2026-10-17, once the identity-only AgentSignupDiscoverySchema, the checked-in agent-signup.schema.json, the conformance validator (src/validate-discovery.ts, validatorVersion 1.0.0), the VaporCloud reference implementation, and this documentation were confirmed to agree. Release notes, including the release evidence and the change process, are in release-notes-v1.0.0.md. The published adoption surface is the implementation registry in registry/README.md (machine-readable: registry/implementations.json).

Release record (v1.1.0). v1.1.0 was recorded on 2026-10-18 as a text-only, additive revision: it changes no schema/Zod/JSON-Schema, no validator, and no wire-protocol behaviour — it adds the optional resolution success field and clarifies replay vs resolve, acceptance lifetime, account identity keys, and credential handoff & recovery. Release notes, including the conformance-validator evidence and the change process, are in release-notes-v1.1.0.md. v1.0.0 implementors remain conformant.

Release record (v1.2.0). v1.2.0 is the 2026-10-24 revision. It adds the Contracts G/H/I text in §14–§19, the optional rp_attestation field (§5.2, §5.4), the authenticated abuse contract (§11.5) and receipt ingestion contract (§11.6), the v1.2 receipt rules (§11.1, §11.4), and renumbers the former §14–§17 to §20–§23. The release ships in the same tag with the JSON Schemas rp-attestation.schema.json, broker-metadata.schema.json, attestation-status.schema.json, signup-receipt.schema.json, abuse-report.schema.json, and the updated agent-signup.schema.json; the fixture matrix in fixtures/ with fixtures/expected.json; the validator (validatorVersion and package version 1.2.0) with pnpm validate:discovery, pnpm validate:fixtures, and pnpm validate:registry; and release-notes-v1.2.0.md. This branch is the reviewed artifact, not the publication: the tag, npm release, and GitHub release follow the merge (release notes §6). Document conformance never requires a live RP broker: only --verify-attestation contacts one.

1.3 Version identity

  • The protocol version is carried major-only in the discovery document's protocol field: agent-identity-v1.
  • This specification revision is v1.2.0. Minor/patch revisions are additive and do not change the protocol value; breaking changes require a new protocol value and a new discovery path (§13). Additive v1 changes are recorded in the changelog above and must pass the scope-boundaries.md review checklist; the process is written down in release-notes-v1.0.0.md §3.
  • rp_attestation (§5.2) is OPTIONAL. A v1.0.0 or v1.1.0 discovery document that omits it remains conformant; absence means unverified, never invalid. An attestation that is present but expired, ill-signed, or issued by an untrusted broker is a verification result, not a conformance failure.
  • This repository is the canonical location of this specification (until a rendered copy exists in the documentation site). The optional spec field points at a versioned copy and stays OPTIONAL; implementations may omit it, and the placeholder https://<canonical-spec-host>/… is illustrative only.

1.4 Normative language

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals.


2. Scope & Non-Goals

The protocol is an identity and brokerage protocol. Its responsibility ends once an agent is verified, delegated, has accepted the vendor's ToS, and the signup event has been recorded.

2.1 In scope

  • Relying Party discovery of identity-only signup parameters.
  • Delegated acceptance of the Relying Party's ToS, with an unambiguous content hash.
  • Presentation and verification of an Agent Identity assertion during brokered signup.
  • Identity/attribution traceability: which agent signed up with which provider, on whose behalf, under which ToS, when, and with what cryptographic proof.
  • An opaque, hash-pinned pointer to the vendor's post-signup integration guide (context).
  • Identity assertions, issuer trust (issuer_allowlist + issuer-hosted JWKS), and revocation (enforced by the issuer at assertion-minting time).
  • Relying Party identity: provider registration, origin/audience verification, RP signing keys, and the broker-issued RP attestation (§14, §15).
  • Local attestation verification, the trusted-RP-broker trust list, assurance levels, and the allow/warn/require/block policy (§17, §19).
  • RP-signed receipts and authenticated receipt ingestion (§11.4, §11.6).
  • Authenticated abuse reporting and broker metadata discovery (§11.5, §16).

2.2 Non-goals (explicitly excluded)

The following are vendor responsibilities and MUST NOT be represented as protocol surface in the discovery document or the signed receipt. They belong to the vendor's post-signup context guide (for example a SKILL.md):

  • product catalogs, SKUs, pricing tiers, free-tier quotas, or product specs;
  • resource provisioning APIs, instance lifecycle, resource endpoints, or resource credentials;
  • vendor API/SDK usage, operational runbooks, and vendor error catalogs;
  • legacy email/OTP signup endpoints implemented by the vendor;
  • vendor admin dashboards and instance lifecycle actions;
  • free-form vendor resource metadata of any kind.

Provisioning is a separate vendor API call the agent makes after signup by reading the vendor's guide. Brokered signup only creates or resolves the account.

Contract H (§15) is an administrative provider API, not a vendor capability surface: it registers the RP's origin, keys, and attestation and is never placed in discovery. MCP transport, broker federation governance, a verification SDK, and any provider resource catalog, provisioning, or credential custody remain out of scope in v1.2.

2.3 Naming note

"Provision" has two distinct meanings in this system. "Auto-provision an agent sub-identity" (broker-side agent registration) is in scope. "Provision a compute instance" is vendor scope. See scope-boundaries.md §3.


3. Roles & End-to-End Flow

3.1 Roles

Role Definition
Human owner Verified person who signs the Master Agent Delegation Agreement and is legally responsible for the agent.
Broker / issuer Mints signed Agent Identity assertions and publishes JWKS.
Agent Acting through an MCP harness; discovers the Relying Party, obtains an assertion, and completes signup.
Relying Party (RP) Service provider that publishes agent-signup.json and accepts assertions.

3.2 End-to-end flow

Agent                         Broker                         Relying Party
  |                              |                                |
  |-- GET /.well-known/agent-signup.json ------------------------>|
  |<-- identity-only discovery document --------------------------|
  |                              |                                |
  |-- identity_prove(aud, tos_url, tos_hash, text, scopes) ------>|
  |<-- signed Ed25519 assertion ----------------------------------|
  |                              |                                |
  |-- POST endpoints.agent_signup (Bearer assertion) ------------>|
  |                              |<-- verify via issuer JWKS -----|
  |<-- credentials + optional signed signup_token + context ------|
  |                              |                                |
  |-- (after signup) GET context.url, verify context.sha256 ----->|
  |-- POST <vendor provisioning API from guide> ----------------->|
  1. The agent fetches the discovery document only when the user targets that service (§8).
  2. The agent verifies the document, checks issuer_allowlist and required_scopes, and computes the ToS hash from terms.text (§6).
  3. The agent calls its Broker's identity_prove with the accepted ToS URL/hash and text to obtain a signed assertion scoped for audience (§7).
  4. The agent POSTs the assertion to endpoints.agent_signup.
  5. The RP verifies the assertion, completes signup, and returns provider credentials plus, where supported, a signed signup_token (Contract E) and the optional context pointer.
  6. The agent records the receipt with identity_record_signup (Contract F) so the broker persists the proof. The human sees the signup in the portal ledger.
  7. After signup, the agent fetches the vendor guide from context, verifies its SHA-256, and treats it as untrusted vendor documentation (§12).

4. Discovery Endpoint & Transport

  • The document MUST be served at /.well-known/agent-signup.json on the Relying Party's canonical HTTPS origin.
  • GET MUST return 200 OK with Content-Type: application/json when the service supports agent-identity-v1.
  • GET MUST return 404 Not Found (or 410 Gone) when the service does not support the protocol. A 401/403 MUST NOT be used to signal non-support.
  • OPTIONS SHOULD return 204 with permissive CORS headers so browser-based inspectors and agent tools can read the document.
  • Responses SHOULD include Cache-Control: public, max-age=60 (maximum recommended 300) and Access-Control-Allow-Origin: *.
  • The document MUST be public and MUST NOT contain secrets, credentials, internal hostnames, or personal data.
  • Redirects SHOULD NOT be used. If used, they MUST remain on the same origin and are limited to three hops.
  • The response body SHOULD be capped at 1 MiB. Implementations MUST enforce a bounded read size when fetching.
  • Agents MUST use HTTPS, except for explicit http://localhost / http://127.0.0.1 development origins.
  • The document is not signed in v1. It is a parameter document, not a trust anchor. Trust comes from TLS, the issuer allowlist, issuer-hosted JWKS, and the broker-signed assertion.

4.1 Error behavior at the discovery endpoint

  • 200 OK — the service supports agent-identity-v1; the body is the discovery document.
  • 404 Not Found / 410 Gone — the service does not support agent-identity-v1. The agent MUST treat the service as unsupported and stop.
  • 5xx or a transport failure — treat as a transient fetch failure. The agent SHOULD NOT retry more than a small bounded number of times and MUST NOT proceed to signup without a valid document.
  • A redirect to a different origin, a document over the size cap, a non-JSON body, or an unparseable body MUST be treated as an invalid document, not as support.

5. Discovery Document Reference (identity-only)

5.1 Required fields

Field Requirement Semantics
protocol REQUIRED Literal "agent-identity-v1". Any other value means the agent MUST treat the service as unsupported — never "best effort".
audience REQUIRED Stable, case-sensitive service identifier. The agent MUST place this exact value in the assertion aud; the RP MUST reject a mismatch.
provider_name REQUIRED Human-readable service name.
issuer_allowlist REQUIRED, non-empty Absolute issuer origins accepted by this RP. Trailing slashes are normalized away. The agent's broker iss MUST match one entry exactly. No wildcards.
required_scopes REQUIRED, unique Scopes the RP requires. For brokered signup this MUST include tos:accept. It MAY include others such as signup:free_tier.
terms.version REQUIRED ToS version. The RP MUST change it whenever the text changes materially (§6).
terms.url REQUIRED Absolute HTTPS URL (localhost development exception) where a human can read the same agreement.
terms.sha256 REQUIRED sha256:<64 lowercase hex> over the exact UTF-8 bytes of terms.text (§6).
terms.text REQUIRED The complete ToS text the agent is accepting, not a summary. Makes the hash unambiguous.
endpoints.agent_signup REQUIRED Absolute HTTPS URL accepting POST with Authorization: Bearer <assertion>. The only endpoint in the protocol.

5.2 Optional fields

Field Requirement Semantics
spec OPTIONAL Absolute URL of the versioned specification. Placeholder canonical URL: https://<canonical-spec-host>/agent-identity/v1/agent-signup (final hosting URL undecided — §22).
provider_url OPTIONAL Canonical service URL.
contact OPTIONAL Security/abuse/legal contact (email or URL).
updated_at OPTIONAL ISO 8601 timestamp for human/debuggability. Not a freshness signal for ToS.
extensions OPTIONAL Non-capability vendor metadata keyed by reverse-DNS namespace (for example dev.vaporcloud). It MUST NOT be used to advertise provisioning, catalogs, SKUs, or resource endpoints.
rp_attestation OPTIONAL A compact JWS issued by the RP's broker (Contract G, §14), at most 16384 characters: three unpadded base64url segments separated by .. Presence changes no other field. Absence is conformant and means unverified. A malformed value is a conformance error; a structurally valid JWS whose header, claims, or signature fail is a verification error, not a conformance error.

5.3 Forbidden fields (vendor capability surface)

The following MUST NOT appear in a conformant agent-signup.json document, at any nesting depth, including under extensions:

  • instance_catalog, SKUs, pricing tiers, free-tier quotas, or product specs;
  • endpoints.provision;
  • endpoints.email_signup;
  • endpoints.verify_otp;
  • endpoints.broker_signup_receipt;
  • any resource endpoint, credential, instance lifecycle, or vendor API usage metadata;
  • any attempt to smuggle the above through extensions.

A document does not become conformant by moving vendor capabilities under extensions. The discovery document is a parameter document, not a capability catalog. A conformant Relying Party MUST be expressible with identity-only support.

5.4 Field rules

  • issuer_allowlist entries MUST be absolute origins with no path, query, fragment, or wildcard, and no duplicate after trailing-slash normalization.
  • required_scopes entries MUST be non-empty strings and unique.
  • URLs advertised for terms.url, endpoints.agent_signup, provider_url, and spec MUST be absolute; protocol-level URLs MUST be HTTPS except the documented localhost development exception.
  • Unknown additive fields MUST be ignored by consumers. Producers SHOULD place non-capability vendor metadata under extensions.<reverse-dns-namespace> so typos remain visible.
  • Consumers MUST NOT interpret any field as a command. All discovery content, especially terms.text, is data (§8).
  • rp_attestation, when present, MUST be a compact JWS string within the 16384-character cap. Its rp.origin MUST equal the origin of the discovery document the verifier actually fetched — not provider_url, not terms.url, and not a redirect target — and its rp.audience MUST equal discovery.audience.
  • When the discovery object was supplied by the caller rather than fetched by the broker, the caller MUST also supply an explicit canonical rp_origin; the verifier uses that declared origin and never infers it from terms.url, provider_url, or the attestation. This path is recorded as agent_supplied_discovery and a require policy MUST reject it (§17).
  • provider_url is a display/context hint and is never an identity binding.

5.5 Minimal conformant example

This example contains no vendor capability fields and is the smallest document a conformant RP may publish (every required field, no optionals):

{
  "protocol": "agent-identity-v1",
  "audience": "vaporcloud",
  "provider_name": "VaporCloud",
  "issuer_allowlist": ["https://id.agentregistrar.dev"],
  "required_scopes": ["tos:accept"],
  "terms": {
    "version": "2026.1",
    "url": "https://vaporcloud.example/terms",
    "sha256": "sha256:<64 lowercase hex characters>",
    "text": "<the complete VaporCloud Terms of Service text>"
  },
  "endpoints": {
    "agent_signup": "https://vaporcloud.example/api/signup/agent"
  }
}

A fuller document may add spec, provider_url, contact, updated_at, and namespaced extensions. It MUST NOT add any field from §5.3.


6. ToS Binding

  • terms.sha256 MUST use the form sha256:<64 lowercase hex characters>.
  • The digest MUST be the SHA-256 of the exact UTF-8 bytes of terms.text, with no newline normalization, no trimming, and no HTML extraction.
  • terms.text MUST be the complete agreement, not a summary.
  • terms.url MUST be an absolute HTTPS URL (localhost development exception) where a human can read the same agreement.
  • terms.version MUST change whenever the text changes materially.
  • The agent MUST recompute the hash from terms.text and compare it with terms.sha256 before calling the Broker, and its Broker MUST verify the accepted text against the hash it records. The agent MUST NOT trust the advertised terms.sha256 alone.
  • The RP MUST reject a signup assertion whose delegation.accepted_rp_tos_hash does not equal the currently advertised terms.sha256 (stale-hash rejection).
  • When the RP changes its terms, it MUST publish the new text and hash before accepting new signups. In-flight assertions for the previous hash MAY be accepted for a bounded grace period, but the discovery document advertises only the current version. The grace period MUST be documented by the RP outside the discovery document (for example in its SKILL.md or support page) and SHOULD NOT exceed 24 hours.
  • The Broker records the accepted URL/hash in tos_acceptances and in the assertion; the RP records the same hash and the assertion jti for traceability.

6.1 Re-acceptance

On tos_hash_mismatch (or whenever terms.sha256 differs from the previously accepted hash), the agent MUST treat it as a hard stop: re-fetch discovery, re-read terms.text, recompute the hash, re-consent through identity_prove, and obtain a fresh assertion. The agent MUST NOT replay the old assertion or reuse the old accepted hash.


7. Assertion & agent_signup Handshake

The assertion format is Contract A and is not redefined here. This section defines how it is presented.

7.1 Handshake

  1. The agent obtains an assertion from its Broker for audience, terms.url, terms.sha256, and the required_scopes, passing the accepted ToS text so the Broker can verify the hash it records. The Broker MUST require the caller to demonstrate knowledge of terms.text and MUST reject a request that does not (the hash check in identity_prove; §10).

  2. The agent sends:

    POST <endpoints.agent_signup>
    Authorization: Bearer <agent-identity-assertion>
    Content-Type: application/json

    The request body is RP-specific and MAY be empty.

  3. The RP verifies at minimum:

    • the JWT signature against the issuer's GET <iss>/.well-known/jwks.json;
    • iss is in issuer_allowlist (exact match);
    • aud equals audience;
    • exp has not passed (allow at most 60 seconds of clock skew);
    • jti is not reused in a conflicting context: a repeated jti for the same agent/account at the same service is not a replay and MUST resolve idempotently rather than fail as assertion_replayed (§7.2);
    • the assertion lifetime is reasonable (recommended maximum 5 minutes);
    • delegation.scopes includes every required_scopes entry;
    • human.verified === true;
    • delegation.accepted_rp_tos_hash === terms.sha256.
  4. On success the RP returns 200/201 with an optional resolution field, provider credentials, and, where supported, a signed signup_token per Contract E (§11) and the optional context pointer. resolution is "created" when the account was created and "resolved" when an existing account was resolved; a resolved outcome MUST NOT be treated as a failure. When the outcome is "resolved", the RP MAY omit provider credentials (show-once — §12.5) but MUST point the agent at its credential recovery path in the vendor guide.

  5. On failure the RP returns a 4xx response with a stable machine-readable error code (§7.2).

7.2 Error envelope and stable codes

The RP MUST return errors as a JSON body with a stable machine-readable code:

{
  "error": "tos_hash_mismatch",
  "error_description": "human-readable, non-normative detail",
  "retryable": false
}
  • error MUST be one of the codes below.
  • error_description SHOULD be present but is not normative and MUST NOT be parsed for control flow.
  • retryable SHOULD be present; when absent, the agent infers retryability from the code.
error code HTTP status Meaning Agent action
invalid_request 400 Malformed request (headers/body). Fix the request; do not retry blindly.
invalid_assertion 401 Assertion failed signature or shape verification. Obtain a new assertion.
issuer_not_allowed 403 iss is not in issuer_allowlist. Do not proceed; the broker is not accepted here.
audience_mismatch 403 aud does not equal audience. Re-discover and mint a correct-audience assertion.
assertion_expired 401 exp has passed beyond allowed skew. Obtain a new assertion.
assertion_replayed 409 jti was already used in a conflicting context (a different agent/account/service). A repeated jti for the same agent/account/service is not a replay and resolves idempotently (§7.2). Obtain a new assertion; do not reuse.
tos_hash_mismatch 409 Accepted ToS hash is stale or wrong. Hard stop: re-discover, re-read, re-accept, re-assert (§6.1).
insufficient_scope 403 Delegated scopes miss a required_scopes entry. Ask the human to re-delegate; do not proceed.
human_unverified 403 human.verified is not true. Complete human verification; do not proceed.
rate_limited 429 Caller is being throttled. Back off and retry after the advertised delay.

This mapping is fixed for v1. The invariant is that an agent can distinguish "retry", "re-consent ToS", "get a new assertion", and "unsupported" without parsing prose.

Replay vs resolve. The jti check in §7.1 is an account-creation guard, not a duplicate-suppression mechanism. A repeated POST carrying an already-used jti for the same agent/account at the same service MUST resolve idempotently — a success (with resolution: "resolved" where the RP signals it), not assertion_replayed. assertion_replayed is reserved for a jti presented in a conflicting context (a different agent, account, or service). A Broker MAY return the identical previously issued (and still unexpired) assertion for an identical request; acceptance is the durable legal event and assertions are short-lived proofs. An RP MUST NOT treat a fresh assertion that references an existing acceptance as a new consent.

Revocation is enforced by the Broker at assertion-minting time. If a Relying Party has an out-of-band revocation feed, it MAY add an implementation-specific error code, but v1 does not require one.

7.3 Acceptance lifetime

  • v1 imposes no protocol-mandated re-acceptance interval. An acceptance remains the recorded consent for its terms.sha256 until the terms change (a new terms.sha256, requiring fresh acceptance per §6.1) or the human re-consents.
  • A Broker MAY impose its own re-acceptance policy (for example, periodically re-confirming the Master Agent Delegation Agreement). If it does, it SHOULD document that policy. Such a policy does not change the wire behaviour of POST endpoints.agent_signup and does not make an RP responsible for prompting re-acceptance.

8. Agent Consumption Rules (prompt-injection & SSRF)

Agents MUST:

  1. Fetch discovery only when the user explicitly targets that service; MUST NOT follow arbitrary links from another service's discovery document.
  2. Treat the document, especially terms.text, as untrusted data, not instructions. Legal text MUST NEVER be interpreted as agent commands. This is the prompt-injection defense.
  3. Reject the service if the document is missing, malformed, over the size cap, uses an unknown protocol, or does not allow the agent's issuer.
  4. Verify that the agent's delegated scopes cover required_scopes before requesting an assertion.
  5. Fetch the document immediately before signup, or honor its cache lifetime; never reuse a stale ToS hash.
  6. Never send an assertion to an endpoint that was not discovered from the same service origin, unless the user intent explicitly covers a separate API origin.
  7. Never reuse an assertion across audiences, services, or after expiry.
  8. Treat tos_hash_mismatch as a hard stop requiring fresh discovery and re-acceptance.
  9. After signup, if the response or receipt contains a context pointer, fetch the vendor guide, verify its SHA-256, and treat it as untrusted vendor documentation — not broker policy or agent instructions (§12).

8.1 SSRF rules

  • Agents and brokers MUST NOT fetch arbitrary internal addresses derived from a discovery document.
  • http://localhost / http://127.0.0.1 MAY be used only in explicit development mode.
  • All fetches of discovery, JWKS, and context URLs MUST apply a timeout, a bounded response size, and a redirect limit; redirects MUST stay on the same origin.
  • Fetching a discovered URL MUST NOT cause a side effect. Probing advertised endpoints, if done at all, MUST be non-destructive.

9. Relying Party Publication Rules

RPs MUST:

  1. Publish the document for as long as they claim agent-identity-v1 support.
  2. Keep terms.text, terms.sha256, and terms.url mutually consistent and atomically updated.
  3. Verify assertions rather than merely parsing them; never trust an unsigned or unverified JWT.
  4. Resolve issuer keys only from the issuer's canonical /.well-known/jwks.json; never accept a jku/x5u header pointing at an arbitrary host.
  5. Reject unknown issuers, wrong audiences, expired assertions, replayed jtis, insufficient scopes, unverified humans, and stale ToS hashes, using the §7.2 codes.
  6. Record the assertion jti, the accepted ToS version/hash, and attribution for audit.
  7. Return stable machine-readable errors and rate-limit abusive callers.
  8. Provide a contact for abuse/security reports and use Contract D.2 (§11.5) to report misbehaving agents to the Broker.
  9. If the RP has vendor capabilities to document, publish a post-signup context guide and return its url + sha256 on successful signup (§12). The RP MUST NOT advertise those capabilities in discovery or the receipt.

9.1 Account identity keys

An RP that resolves signups idempotently needs a stable account key. This section recommends one; it does not add a protocol field.

  • Recommended keys. Key accounts on (iss, human.id) for human-scoped accounts and on (iss, agent.id) for agent-scoped accounts. When a service treats one account per human regardless of which agent acted, (iss, human.id) is the right key; when each agent gets its own account, use (iss, agent.id).
  • Emails are discouraged as keys. Keying on human.email or agent.email is NOT RECOMMENDED. Email addresses are mutable and are not stable identifiers; use them only as a legacy fallback for accounts that predate attribution.
  • Namespace by issuer. Whenever more than one issuer is trusted (issuer_allowlist has more than one entry), the key MUST be namespaced by the assertion's iss so that identities from different Brokers cannot collide.
  • human.verified is email verification, not KYC. The flag means the human owner's email address was verified. It is not a know-your-customer or identity-document check, and an RP MUST NOT represent it as one.

10. Issuer / JWKS Assumptions

Because issuer_allowlist points agents and Relying Parties at issuers, the issuer-side assumptions are part of this specification:

  • Every issuer origin listed by an RP MUST publish GET <issuer>/.well-known/jwks.json as a public JWKS for assertion verification.
  • The JWKS endpoint MUST use HTTPS (localhost development exception), return application/json, and be safe to cache.
  • Assertions MUST follow Contract A: Ed25519 (EdDSA) signatures with a stable kid, a single string aud, and iss, jti, iat, exp, agent, human, and delegation claims.
  • The Broker MUST NOT mint an assertion when the human is unverified, the Master ToS is unsigned, the agent is revoked, or the requested scopes exceed the delegation.
  • The Broker MUST require the caller to demonstrate knowledge of the accepted ToS text (the hash check in identity_prove), MUST reject a request that does not, and MUST record the acceptance before returning an assertion. Knowledge of terms.text is a precondition of consent, not merely an integrity check on the document.

Structured issuer objects and hosted JWKS discovery beyond the fixed path are not part of v1 (§22). They may be added as an additive extension later if a real need appears.


11. Contract E Identity Facts & the context Pointer

The signed signup_token (Contract E) remains the proof that a signup occurred. In v1.2 it records identity/attribution, an opaque account reference, and the RP key/attestation that signed it. It is signed by the RP, not by the broker, with the key named in service.key_id (§11.4).

This section also defines the two non-discovery contracts that carry the same RP identity: authenticated abuse reporting (Contract D.2, §11.5) and authenticated receipt ingestion (Contract F, §11.6).

11.1 Kept claims

  • iss, sub, aud, jti, iat
  • assertion_jti — the jti of the assertion presented at signup
  • signed_up_at
  • service.audience, service.origin, service.provider_name, service.provider_url, service.account_id, service.attestation_jti, service.key_id
  • agent.id, agent.name, agent.email
  • human.id, human.name, human.email, human.verified
  • tos.version, tos.url, tos.sha256, optional tos.master_tos_version
  • optional deprecated exp

No exp in v1.2. A receipt is a historical fact, not an access token. A legacy exp is ignored and never causes rejection (§11.4). iss is the RP origin; aud is the human broker issuer; sub is the agent id; jti is srp_ + a 26-character lowercase Crockford base32 ULID. service.origin MUST equal iss and the attestation rp.origin; service.audience MUST equal the attestation rp.audience; service.attestation_jti names the attestation that contained service.key_id, and that key MUST be in keys[] with receipt in its purposes. The JWS header kid MUST equal service.key_id.

11.2 Removed claims

  • service.instance_sku
  • service.endpoint_url

11.3 Added: the context pointer

An optional top-level context object:

Field Requirement Semantics
context.url REQUIRED when context is present Absolute HTTPS URL (localhost development exception) to the vendor's agent-facing integration guide (such as SKILL.md).
context.sha256 REQUIRED when context.url is present sha256:<64 lowercase hex> over the exact UTF-8 bytes of that guide.

Because context is meaningless without a pointer, context.url is required whenever the object is present, and sha256 is required with it: a URL without a hash cannot be pinned, and a hash without a URL cannot be fetched. The receipt JSON Schema enforces the same pair; an empty context object is not a way to omit the pointer — omit context entirely instead.

Domain constraint (normative authority). context.url MUST stay on the vendor's registrable domain, and the authority for that domain is the signup endpoint's origin (endpoints.agent_signup), not provider_url. When provider_url is present it MUST share the signup endpoint's registrable domain. The broker compares context.url against the signup-endpoint registrable domain and MUST reject a pointer on any other domain. Because the signed receipt carries service.provider_url but not the signup endpoint, a receipt that omits service.provider_url cannot establish the domain; the broker MUST then fail closed by rejecting the pointer rather than skipping the check.

No free-form vendor metadata object exists in v1. Do not add resource endpoints, SKUs, pricing, credentials, or API usage details under any other claim. The context pointer is deliberate: it lets the vendor document provisioning, SKUs, API usage, and errors without turning the signed receipt into a resource inventory.

11.4 Contract E v1.2 — signing, validity, and retention

  • The receipt is a compact JWS with header {"alg":"EdDSA","typ":"agent-signup-receipt+jwt","kid":"<rp key id>"}. The typ is unchanged from v1.1; v1.2 changes the claims, not the typ.
  • The signing key is the RP key named in service.key_id. It MUST appear in the attestation keys[] identified by service.attestation_jti with receipt in purposes. There is no broker-key fallback: a new verified proof requires a key inside a valid attestation.
  • A receipt is valid when both iat and signed_up_at fall inside the attestation's [iat, exp] window. No ±60s extension applies to that historical comparison; clock skew applies only to current-time comparisons.
  • The RP MUST retain the attestation that was current when it signed the receipt and submit that exact JWS with the receipt. It MUST NOT substitute its current attestation: an RP that has renewed has a new jti, and the receipt still names the old one. A broker that cannot retrieve the named attestation and has no stored snapshot returns attestation_unavailable (§11.6) instead of substituting the newer attestation.
  • Receipts are idempotent forever on jti. The same jti with a different body is a conflict (409 signup_conflict); the same body returns the existing row with deduplicated: true.
  • service.provider_url stays OPTIONAL and is not an identity binding. context keeps its v1.1 shape and domain rule (§11.3) and is not part of the attestation.

11.5 Contract D.2 — authenticated abuse reporting

POST /api/broker/report-abuse accepts:

{ "rp_attestation": "<compact JWS>", "report": "<compact JWS>" }

The report header is {"alg":"EdDSA","typ":"agent-rp-abuse+jwt","kid":"<rp key>"}. Claims are REQUIRED: iss (RP origin), aud (the human broker issuer, A), jti (abr_ + 26-character lowercase Crockford base32 ULID), iat, exp (exp - iat exactly 300 seconds), agent_id, and reason (1–500 characters); evidence is OPTIONAL and carries OPTIONAL assertion_jti and signup_token_jti.

Rules, in order:

  1. Apply the full Contract G verification (§14) to rp_attestation, including the trusted RP-broker check before any JWKS fetch. For this inbound abuse path the trust list is the broker's configured RP-broker list only: a human's distrust MUST NOT suppress abuse evidence, and D.2 has no human session.
  2. Require typ: "agent-rp-abuse+jwt", alg: "EdDSA", and exactly one kid. The kid MUST be in attestation.keys[] with abuse in purposes; verify the report signature with that key.
  3. Require iss equal to the attestation rp.origin and aud equal to A's issuer.
  4. Require the attestation to be current at report time with the standard 60-second skew (iat - 60s <= now <= exp + 60s). The report's own five-minute freshness bound is never extended: accept only when now is not before iat - 60s and never after exp.
  5. Idempotent on jti.

A report is verified only when it is linked to a signup row whose proof_source is rp_callback or rp_signed and whose attestation was verified. A link to agent_self_reported or legacy_broker_signed, or no link at all, is unverified: the broker opens a case and does not suspend the agent. Only a verified, receipt-linked report may request protective suspension pending operator review.

Success is 202 {"case_id":"<uuid>","report_jti":"abr_...","verification_status":"verified"|"unverified","deduplicated":false}; a duplicate returns 200 with the same case_id. An unauthenticated legacy post is 401 {"error":"unauthorized","error_description":"...","code":"unauthenticated_report"}. Response bodies never include key material or internal paths.

The human path — a person reporting a bad RP — is broker-local and not required for RP conformance: POST /api/broker/report-provider takes the RP origin, audience, optional attestation jti, reason, optional evidence, and optional distrust_broker/block_provider flags, and returns 202 {"case_id":"...","status":"open"}. It updates the human's trust/policy, never the protocol attestation.

11.6 Contract F — authenticated receipt ingestion

POST /api/broker/signup-receipt accepts:

{ "signup_token": "<compact Contract E receipt JWS>", "rp_attestation": "<compact JWS>" }

Both fields are REQUIRED. signup_token is the compact Contract E receipt defined in §11.1/§11.4; the legacy field name is retained and there is no separate receipt field. The rp_attestation in the body MUST be the exact attestation named by the receipt's service.attestation_jti, not the RP's current discovery attestation.

Order: parse both compact JWS envelopes → resolve assertion_jti to the acceptance and its human (DB lookup only; the receipt is not trusted yet) → build the effective RP-broker trust list from BROKER_TRUSTED_RP_BROKERS plus that human's trust and minus that human's distrust (distrust wins) → check rp_attestation.iss against it before any JWKS fetch → verify the attestation signature and claims (Contract G steps 1–3 and 5–6) → parse and schema-check the receipt → check the receipt iat and signed_up_at against the attestation [iat, exp] window using the receipt's signing time, not ingestion time → check the receipt typ/alg/kid, where header kid MUST equal service.key_id → verify the receipt signature with that key → check iss/aud/origin/audience/attestation_jti → verify the receipt sub equals the acceptance agent id and derive agent/human from the trusted acceptance row, never from the receipt → record an event_kind:"signup" verification snapshot and proof_source:"rp_callback".

The attestation is historical for Contract F: an attestation that has expired since the signup is still the correct key material for a receipt whose iat/signed_up_at fall inside its [iat, exp] window. Do not apply the current-time exp + 60s check to the receipt's attestation. Do apply the current-time check to a D.2 abuse report (a current event).

There is no broker-key fallback, no legacy path for a new verified proof, and no untrusted broker. A receipt whose iat or signed_up_at is outside the attestation window fails with 401 code:"attestation_not_valid_at_signing".

The success body preserves the existing Contract F response fields (ok, signup_id, signup_token_jti, assertion_jti, agent_id, service_audience, provider_account_id, human_email_shared, context_url, context_sha256) and adds proof_source:"rp_callback" and deduplicated. A duplicate returns the existing row with deduplicated:true and does not drop the existing fields.

Tier 2 (identity_record_signup) carries the same signup_token and the exact rp_attestation as an optional companion field; if the agent omits it, the broker fetches discovery at service.origin and requires the presented attestation's jti to equal service.attestation_jti. It never substitutes a newer attestation. Self-report is the separate agent_self_reported path and has no attestation.

HTTP error code Meaning
400 invalid_request invalid_request Missing/malformed body or non-JWS strings
401 invalid_attestation invalid_attestation Contract G parse/signature/claims/origin/audience/binding failure; not historical expiry
401 invalid_attestation rp_broker_untrusted rp_attestation.iss not in the effective RP-broker list (broker default + acceptance human's trust − distrust)
401 invalid_signup_token invalid_signup_token Receipt parse/signature/claims failure; legacy sanitized body preserved
401 attestation_not_valid_at_signing attestation_not_valid_at_signing Receipt iat/signed_up_at outside the attestation [iat, exp] window
401 attestation_unavailable attestation_unavailable The exact attestation is no longer retrievable and the broker has no stored snapshot
409 signup_conflict signup_conflict Same receipt jti with a different body
429 rate_limited rate_limited Bounded ingestion rate exceeded
503 service_unavailable rp_broker_unavailable Transient trusted-broker metadata/JWKS outage; retryable, no write

The response body always uses {error, error_description, code} and error_description never includes key material or internal paths. Broker JWKS retention matters here: the broker MUST keep a retired attestation-signing public key in its JWKS for at least 30 days after the last attestation signed with that key expires, so a receipt presented after expiry and after a broker-key rotation can still be verified (§16). After that window, an unseen old receipt is attestation_unavailable rather than silently trusted.


12. Post-Signup Context Handoff

12.1 Delivery

  • The vendor supplies context.url and context.sha256 in its POST /api/signup/agent response and in the signed signup_token.
  • The broker persists the context and exposes it to the agent after signup (Contract F response and the identity_record_signup MCP result) and to the human (portal link).
  • Discovery does not advertise a post-signup context field in v1. The signed receipt and signup response are the authoritative handoff.

12.2 Integrity

  • The agent MUST fetch the guide after signup and recompute the SHA-256 over the exact bytes it received, comparing it with context.sha256. On mismatch, the agent MUST NOT act on the guide.
  • The guide SHOULD be size-capped (1 MiB recommended) and served with a text/Markdown content type.

12.3 Trust

  • The guide is untrusted vendor documentation — not broker policy and not agent instructions. It is subject to the same prompt-injection rule as terms.text (§8): content is data.
  • The broker vouches for who signed up and what ToS was accepted, not for the guide's contents.
  • The URL MUST stay on the vendor's registrable domain. The normative authority is the signup endpoint's origin (endpoints.agent_signup); provider_url, when present, MUST share that same registrable domain. The broker compares the pointer against the signup-endpoint registrable domain and MUST reject a pointer on any other domain. A cross-domain, missing-authority (no service.provider_url in the signed receipt), or malformed pointer MUST be rejected by the broker and MUST NOT be followed by the agent.
  • The broker validates the pointer statically and MUST NOT fetch it during receipt ingestion, so the receipt path cannot become an SSRF surface.

12.4 Staleness

  • The agent SHOULD re-fetch the guide when it begins a new provisioning task rather than caching it indefinitely, and MUST re-verify the hash on each fetch.
  • The vendor SHOULD version or date the guide and update context.sha256 whenever the served bytes change. A stale guide is a vendor-operations concern, not a broker failure: the portal shows "No post-signup guide provided" rather than inventing a fallback resource view.

12.5 Credential handoff & recovery

Provider credentials (for example a vendor API key) are shown once, at the signup handoff. The protocol draws a firm line between the broker-mediated identity event and the vendor's own credential lifecycle:

  • Show-once handoff. When an RP creates an account it returns the provider credential in the signup response. Provider credentials are vendor scope (§2.2) and are not carried in the signed receipt, the discovery document, or broker storage.
  • Agents MUST persist at handoff. An agent MUST persist provider credentials to a secure store at the moment of handoff, and MUST NOT rely on the conversation, context window, or transcript as storage.
  • The Broker MUST NOT store provider credentials. The Broker records the identity/attribution proof (signup_token) and the optional context pointer; it MUST NOT store, proxy, or re-issue provider credentials.
  • Do not re-run signup for recovery. An agent MUST NOT re-run signup as a credential-recovery mechanism. A repeated signup on an existing account resolves the account and does not re-issue credentials.
  • RPs SHOULD document recovery. A vendor SHOULD document a credential recovery/rotation path in its guide (context.url), reached only after signup. Because the recovery path is vendor capability, it stays out of discovery and the receipt, exactly like provisioning.
  • Resolve does not mint credentials or mail. A resolve of an existing account MUST NOT mint new provider credentials and MUST NOT send duplicate welcome/legal mail; a fresh signup_token MAY be issued so a dropped-response retry can still record the receipt.

13. Versioning, Extension & Deprecation Policy

  • protocol carries the major version only: agent-identity-v1.
  • Additive identity fields are allowed within v1. Consumers MUST ignore unknown fields.
  • Vendor catalogs, provisioning endpoints, resource metadata, and API-usage documentation are not additive protocol fields; they belong in the post-signup guide.
  • Producers SHOULD namespace non-capability vendor metadata under extensions.<reverse-dns>.
  • A breaking change MUST use a new protocol value and a new discovery path (for example agent-signup-v2.json). The v1 path MUST NOT silently change semantics.
  • The public spec URL SHOULD be versioned; the optional spec field lets agents and humans find the exact document.
  • A future version SHOULD be dual-published during a migration window. The changelog (§1.2) records additive v1 changes.

14. Contract G — RP Attestation

The RP attestation is a public, broker-signed statement that binds a relying party's canonical origin, its audience, its provider identity, and its public signing keys. Any agent or broker can verify it locally against the issuing broker's JWKS. It is published as the OPTIONAL rp_attestation field of the discovery document (§5.2, §5.4).

The attestation is public and copyable. Only the origin + audience binding prevents replay on another service. It MUST NOT carry a terms hash, scopes, endpoints, provider_url, account ids, prices, or private material. It is identity, not a capability catalog and not a safety rating.

Header:

{ "alg": "EdDSA", "typ": "agent-rp-attestation+jwt", "kid": "<broker kid>" }

14.1 Claims

Every claim is REQUIRED unless the Rule says OPTIONAL.

Claim Rule
iss Broker B issuer origin: lowercase, no path, query, or fragment.
sub, rp.id Provider UUID; the two MUST be equal.
aud Literal agent-identity-v1 (protocol label, never the human broker issuer).
jti ^rpa_[0-9a-hjkmnp-tv-z]{26}$ (lowercase Crockford base32 ULID, no i/l/o/u), unique per issuance.
iat, exp Integer seconds; exp - iat = 86400 exactly; the issuer MUST NOT widen it.
rp.origin Canonical origin: lowercase scheme and host, default port omitted, no trailing slash, path, query, or fragment.
rp.audience Exactly discovery.audience; ^\S{1,256}$.
rp.name 1–128 characters.
rp.legal_name OPTIONAL at domain, REQUIRED at entity; 1–256 characters.
rp.contact.email Single email address.
rp.contact.name OPTIONAL; 1–128 characters.
rp.verification.level domain or entity (§17.1).
rp.verification.method https or dns: the method that verified the origin.
rp.verification.origin_verified_at RFC 3339 UTC.
rp.verification.entity_reviewed_at null at domain; non-null at entity. It records when the operator reviewed the entity and has no ordering requirement against origin_verified_at.
keys[] 1–8 entries, unique kid. Every active key plus every retiring key whose retire_after is in the future. Registration rejects a ninth simultaneous key with 409 key_limit_reached; issuance never silently omits a key.
keys[].kid ^[A-Za-z0-9._-]{1,64}$.
keys[].kty, keys[].crv, keys[].alg OKP, Ed25519, EdDSA.
keys[].x base64url 32-byte public key, ^[A-Za-z0-9_-]{43}$.
keys[].purposes Exactly ["receipt","abuse"] in v1.2. The array is retained so a future split is additive; a v1.2 producer MUST NOT emit a subset.

Cross-field rules JSON Schema cannot express are normative here and enforced by the validator: exp - iat = 86400, rp.id = sub, unique keys[].kid, legal_name at entity, entity_reviewed_at null at domain.

Origins in a signed identity artifact are HTTPS-only. iss and rp.origin are canonical https:// origins; the localhost development exception (§5.4) applies to discovery, terms, and endpoint URLs and to origin-challenge fetches, never to a broker-signed attestation. The published JSON Schema and the Zod schemas enforce the same rule.

14.2 Verification

Verification is normative and ordered:

  1. Parse the compact JWS; require typ, alg: EdDSA, and exactly one kid.
  2. iss MUST be in the verifier's trusted RP-broker list. Reject an untrusted iss before any JWKS or status fetch; the verifier never contacts an untrusted broker.
  3. Verify against B's JWKS (jwks_uri from §16, else <iss>/.well-known/jwks.json), selecting the key by kid. Never try all keys.
  4. Check iat/exp with 60 seconds of skew. jti MUST match the frozen pattern.
  5. rp.origin MUST equal the origin of the discovery document actually fetched.
  6. rp.audience MUST equal discovery.audience.
  7. Record rp.verification.level. In v1.2 the level is informational for policy (the matrix is level-agnostic) and is displayed in the ledger. A future min_level policy is additive.
  8. The status endpoint (§16) MAY be consulted in enforced mode; otherwise exp is the bound.

A verifier MUST NOT treat a failed step 5 or 6 as "unverified". unverified means no attestation was presented (§18).

14.3 Agent-supplied discovery

The ordinary identity_signup path is broker-fetched: the broker fetches discovery itself and uses that fetched origin. When a caller supplies a discovery object instead (an advanced identity_prove path), the caller MUST also supply an explicit canonical rp_origin; the verifier uses that declared origin and never infers it from terms.url, provider_url, or the attestation itself. The path is recorded as agent_supplied_discovery and a require policy MUST reject it with discovery_source_not_allowed (§17.3).


15. Contract H — Provider Registration and Status API

Contract H is the administrative API a relying party uses with its RP broker to register origins, prove key possession, and obtain or renew an attestation. It is never agent-facing and never appears in discovery. Contract H has no public JSON Schema: its bodies are the normative tables below, and provider portals/CI implement them.

Routes are absolute; shorthand such as POST /origins/:id/challenge is relative to /api/provider/v1. Provider API keys use Authorization: Bearer <key>. The key format is an implementation detail, but the key is distinct from the RP signing key, hashed at rest, and scoped.

Route Auth (scope) Result
GET /api/provider/v1/account API key (account:read) 200 account, status, plan, entitlements
POST /api/provider/v1/origins human session 201 origin, status:"pending"
POST /api/provider/v1/origins/:id/challenge human session 201 challenge
POST /api/provider/v1/origins/:id/verify human session 200 origin, status:"verified"
POST /api/provider/v1/keys/challenge API key (keys:write) 201 PoP challenge
POST /api/provider/v1/keys API key (keys:write) 201 key, status:"active"
POST /api/provider/v1/keys/:kid/retire API key (keys:write) 200 key, status:"retiring"
POST /api/provider/v1/keys/:kid/revoke API key (keys:write) 200 key, status:"revoked"
POST /api/provider/v1/attestation API key (attestation:write) 201 attestation
GET /api/provider/v1/attestation API key (attestation:read) 200 attestation
POST /api/provider/v1/api-keys human session 201 key + show-once secret
POST /api/provider/v1/api-keys/:id/revoke human session 200 revoked key

There is no key-listing endpoint in v1.2, so no keys:read scope is issued. Only the scopes used by the routes above exist. If a key-listing route is added later, it adds both the route and the scope together.

15.1 Origin and audience challenge

POST /origins/:id/challenge body {"method":"https"|"dns"}. The HTTPS response carries challenge_id, method, file_url, file_content, and expires_at; the DNS response carries challenge_id, method, record_name (_agent-signup.<host>), record_type:"TXT", record_value (agent-signup-verify=<token>), and expires_at.

  • Token grammar [A-Za-z0-9_-]{32,128}. Tokens are single use, with a 30-minute TTL.
  • The HTTPS file holds exactly the token plus at most one trailing LF, served as text/plain.
  • DNS resolves one TXT value exactly equal to agent-signup-verify=<token>.
  • Fetch rules: HTTPS only (localhost development exception), no cross-origin redirect, 5-second timeout, 64 KiB cap, public IP addresses only.
  • Limits: 5/hour/origin and 20/day/account, answered with 429 and Retry-After.
  • Audience is globally unique across the broker's provider origins. A revoked provider's audience is not reclaimed automatically. A conflict is 409 code:"audience_conflict" and goes to operator review/cleanup.

15.2 Key proof of possession

POST /keys/challenge body {"kid":"rp-key-1"} returns 201 {"challenge":"<opaque>","expires_at":"...","aud":"<B issuer>"}. The challenge TTL is 10 minutes, single use, bound to the provider and kid.

POST /keys body {"proof":"<compact JWS>","public_jwk":{...},"purposes":["receipt","abuse"]}. The PoP header is {"alg":"EdDSA","typ":"agent-rp-key-pop+jwt","kid":"rp-key-1"} and the claims are:

{ "iss": "https://vaporcloud.example", "aud": "https://agentregistrar.dev",
  "sub": "<provider UUID>", "jti": "<uuidv4>",
  "iat": 1760000000, "exp": 1760000600, "nonce": "<challenge>",
  "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "<43 chars>", "kid": "rp-key-1" },
  "purposes": ["receipt", "abuse"] }

exp - iat = 600. The nonce MUST be the unconsumed challenge; iss MUST be one of the provider's verified origins; sub MUST equal the authenticated provider; jwk MUST equal public_jwk; public_jwk.kid, the challenge kid, the PoP header kid, and jwk.kid MUST all be the same value; the body purposes MUST equal the PoP purposes; and the JWS MUST verify with that jwk. The challenge is consumed on success or on the fifth failure.

Keys are provider-scoped and appear in every attestation for every origin of that provider. Retire sets status:"retiring" and retire_after = now + 30 days; the key stays in new attestations until then and resolves historical receipts from the stored snapshot. Revoke is immediate.

15.3 Attestation issuance and renewal

POST /api/provider/v1/attestation body {"origin":"https://vaporcloud.example","audience":"vaporcloud"} returns 201:

{ "jti": "rpa_01j...", "origin": "https://vaporcloud.example", "audience": "vaporcloud",
  "verification_level": "domain", "issued_at": "2026-10-24T00:00:00.000Z",
  "expires_at": "2026-10-25T00:00:00.000Z", "renewed_from_jti": null,
  "attestation": "<compact JWS>" }

Issuance requires provider status:"active", plan:"verified" with a live subscription (the verified entitlement), origin status:"verified", and at least one active key. A free provider is conformant but cannot issue an attestation; it receives 403 code:"provider_not_eligible" with a plan/upgrade next step. A new jti is minted per issuance; exp = iat + 86400; renewed_from_jti is null on a first issuance and the previous jti on a renewal; there is no minimum renewal interval; the limit is 24 issuances/day/origin. GET ?origin=&audience= returns the latest attestation by (issued_at desc, created_at desc, id desc), or 404 code:"attestation_not_found".

The normal renewal target is 12 hours against a 24-hour expiry: one missed cycle is tolerable and expiry is the primary revocation bound.

15.4 Account lifecycle

A new provider account is pending. The broker promotes it to active when its first origin becomes verified (domain level, automated). The free plan may be active but has no attestation entitlement. verified plans are set with a live subscription. An entity review is recorded before a level:"entity" attestation may be issued. Suspend, revoke, and restore are operator actions. GET /api/provider/v1/account remains readable in pending, suspended, and revoked so the provider can see its state; provider_suspended and provider_not_eligible apply to mutations and issuance, not that read.

15.5 Errors

Errors use the OAuth-style body in §18.5 with a stable code: 400 invalid_request / invalid_jwk / purpose_mismatch; 401 invalid_api_key; 403 insufficient_provider_scope / provider_suspended / provider_not_eligible; 404 not_found / key_not_found / attestation_not_found; 409 audience_conflict / origin_conflict / key_exists / key_limit_reached / last_active_key / key_not_active / no_active_key; 410 challenge_expired / challenge_consumed; 422 challenge_failed / pop_invalid; 429 rate_limited; 500 internal_error. Key and attestation route additions are additive and keep the frozen meanings of provider_*, pop_invalid, and rate_limited.


16. Contract I — Broker Metadata and Attestation Status

16.1 Broker metadata

GET /.well-known/agent-broker.json, Content-Type: application/json, HTTPS, no authentication. All six fields are REQUIRED:

Field Rule
issuer Origin of the document itself; MUST equal the iss of every attestation it signs.
jwks_uri Absolute HTTPS URL, same origin as issuer.
attestation_status_uri Absolute HTTPS URL of the status endpoint.
abuse_endpoint Absolute HTTPS URL accepting Contract D.2 (§11.5).
protocol_versions Non-empty, unique, MUST contain agent-identity-v1.
contact Email address or absolute HTTPS URL.
  • issuer differing from the document origin invalidates the document. issuer is a canonical HTTPS origin, exactly like an attestation iss (§14.1); the localhost development exception does not apply to metadata.
  • Clients cache metadata for at most 1 hour and re-fetch on an unknown kid. This is a client courtesy, not a protocol claim. The attestation status cache stays at 5 minutes.
  • A client that verifies an attestation resolves JWKS as follows: it MAY use the metadata jwks_uri only when the fetched document's issuer equals the trusted attestation iss and jwks_uri is HTTPS and same-origin with it; otherwise it MUST fall back to <issuer>/.well-known/jwks.json. A cross-origin or invalid jwks_uri MUST NOT be followed. Without metadata, clients fall back to <issuer>/.well-known/jwks.json and <issuer>/api/broker/report-abuse, where <issuer> is the assertion iss (Contract A) or the attestation iss (Contract G) being verified. The fallback is a client rule, not a second format.
  • Cross-origin attestation_status_uri and abuse_endpoint are allowed but MUST pass the same SSRF rules as §19.5; same-origin is RECOMMENDED.
  • Broker JWKS retention. B MUST keep a retired attestation-signing public key in its JWKS for at least 30 days after the last attestation signed with that key expires. This lets a broker verify a receipt presented after the attestation expired and after a broker-key rotation. A stores the verified attestation/key snapshot on first ingestion; after the retention window, an unseen old receipt is attestation_unavailable rather than silently trusted. Retention is a broker rule, not a client cache bound.

broker-metadata.schema.json is the producer schema. jwks_uri same-origin with issuer is a cross-field rule enforced by the validator.

16.2 Attestation status

GET <attestation_status_uri>?jti=rpa_... is public. The body is:

{ "attestation_jti": "rpa_01j...", "status_jwt": "<compact JWS>" }

The plaintext field is named attestation_jti so it cannot be confused with the status JWS's own jti (a UUID). The JWS header is {"alg":"EdDSA","typ":"agent-rp-attestation-status+jwt","kid":"<broker kid>"} and the claims are:

{ "iss": "https://agentregistrar.dev", "aud": "agent-identity-v1",
  "jti": "<uuidv4>", "sub": "rpa_01j...",
  "iat": 1760000000, "exp": 1760000300,
  "status": "active", "status_changed_at": "2026-10-24T00:00:00.000Z" }
  • sub is the queried attestation jti.
  • exp - iat is exactly 300 seconds.
  • status is one of active, suspended, revoked.
  • status_changed_at MUST NOT be after iat.
  • The plaintext attestation_jti is advisory. Clients MUST verify the JWS and use sub, and MUST check typ, alg, exactly one kid, the B signature, iss equal to the metadata issuer, aud equal to agent-identity-v1, exp/iat with 60 seconds of skew, and sub equal to the queried attestation_jti.
  • suspended maps to RpVerificationStatus.suspended. revoked maps to invalid with reason:"attestation_revoked" because the frozen enum has no revoked (§18). The status response itself carries only status and status_changed_at.
  • Producer and verifier modes apply to the status JWS too: the published JSON Schema and AttestationStatusClaimsSchema are closed producer mode, while a verifier validates every known claim and ignores unknown additive claims (parseAttestationStatusVerifier).

Caching is Cache-Control: public, max-age=300, must-revalidate; clients MUST NOT use an older cached answer than 300 seconds except to report unavailable. An unknown jti is 404 with {"error":"not_found","error_description":"...","code":"attestation_not_found"} and Cache-Control: no-store. Responses MUST NOT include provider name, contact, or account data.

Local verification does not require the status endpoint: expiry is the primary bound. The status endpoint is opt-in defense-in-depth for enforced mode and operator actions.


17. Trust Model, Assurance Levels, and Policy

The trust model rests on three separate statements:

  1. Assertion verification is permissionless. Anyone can verify a Contract A assertion against the human broker's public JWKS — no account, SDK, shared secret, or broker call.
  2. Attestation verification is local. An agent or broker verifies a Contract G attestation against the RP broker's cached JWKS. There is no per-signup A→B call and no broker in the RP's signup path.
  3. Trust is policy, not cryptography. A valid attestation says "this broker vouches for this origin/audience at this time". Whether that broker is trusted, and what to do about an untrusted or missing attestation, is a local policy decision.

Trust is not transitive: trusting Broker B to attest an RP does not make B's other statements authoritative, and it says nothing about the RP's safety, uptime, solvency, or conduct. "Verified" is a verification event, not a badge and not a safety guarantee.

17.1 Assurance levels

Level Meaning
domain Automated control of the origin via an HTTPS well-known file or a DNS TXT record (§15.1).
entity Operator-reviewed legal-entity evidence: legal name, registry/VAT id, official contact.

There is no enhanced level in v1.2. Neither level asserts safety, uptime, solvency, or conduct. Both levels are verified for policy purposes; the level is recorded and displayed, and a future min_level policy is additive.

17.2 Resolution order

Resolution is normative and follows this order:

  1. Normalize the requested service to an origin.
  2. Fetch and validate discovery (SSRF-safe, bounded).
  3. Validate protocol, terms, scopes, and issuer allowlist.
  4. Verify rp_attestation if present (§14.2).
  5. Resolve trust: the broker default plus the human's override.
  6. Resolve policy: the human's rule for (rp_origin, rp_audience), else warn.
  7. Compute RpVerificationStatus (§18).
  8. Apply the policy matrix (§17.3).
  9. Mint and record the verification event.
  10. Return the prepared signup request and next steps.

17.3 Policy matrix

RpPolicy is allow, warn, require, or block. Policy is never a verification status.

Policy verified unverified attested_untrusted_broker invalid / expired unavailable suspended
allow proceed proceed proceed proceed proceed proceed
warn proceed proceed + warning proceed + warning proceed + warning proceed + warning proceed + warning
require proceed rp_unverified rp_broker_untrusted rp_attestation_invalid / rp_attestation_expired rp_broker_unavailable rp_suspended
block rp_blocked rp_blocked rp_blocked rp_blocked rp_blocked rp_blocked
  • require is always broker-enforced. A require policy that is only advisory would be a lie.
  • allow and warn MAY set enforced:true: the broker still verifies the attestation at mint time and records the verification row (and returns the warning), but the policy still proceeds. enforced:true is not a hidden hard gate for allow/warn; only require refuses.
  • require MUST reject source:"agent_supplied_discovery" with discovery_source_not_allowed and accept only a broker-fetched discovery document.
  • The default policy is warn. The trusted RP-broker list is the broker default plus the human's trust override minus the human's distrust override; distrust wins. There is no per-agent list in v1.2: agents inherit their human's policy.

This specification defines statuses, not MCP tool names or JSON-RPC envelopes.


18. Status and Error Taxonomy

18.1 Verification status

RpVerificationStatus has exactly these values, and no synonyms:

verified, unverified, attested_untrusted_broker, invalid, expired, unavailable, suspended.

Condition Status Validator MCP code
Valid attestation, trusted broker, policy accepts verified verified —
No attestation unverified unverified rp_unverified under require
Signature/claims/origin/audience failure invalid invalid rp_attestation_invalid
Past exp + skew expired expired rp_attestation_expired
iss not trusted attested_untrusted_broker attested_untrusted_broker rp_broker_untrusted
JWKS or status fetch failure unavailable unavailable rp_broker_unavailable
Status suspended, or local provider suspended suspended n/a (no status call) rp_suspended

unverified means "no attestation presented". An expired, invalid, or untrusted attestation is never downgraded to unverified. blocked is policy, not a verification result. The validator never emits suspended because it never calls the status endpoint.

18.2 Proof sources

proof_source has exactly four values: rp_callback (Tier 1, Contract F), rp_signed (Tier 2 with an RP-signed receipt), agent_self_reported (no RP signature), and legacy_broker_signed (pre-v1.2 rows, not verified). rp_authenticated and agent_reported MUST NOT be written for new rows. Only rp_callback and rp_signed, with a verified attestation, can give an abuse report standing.

18.3 Verification source

RpVerificationSource has exactly two values, and no synonyms:

  • broker_fetch — the broker fetched the discovery document itself (the ordinary identity_signup path and Contract F). The fetched document origin is authoritative.
  • agent_supplied_discovery — the caller supplied a discovery object plus an explicit canonical rp_origin (the advanced identity_prove path; §5.4, §14.3). The declared origin is used and is never inferred from terms.url, provider_url, or the attestation.

require is broker-enforced and MUST reject agent_supplied_discovery with discovery_source_not_allowed; only a broker-fetched discovery document is acceptable (§17.2, §17.3). allow and warn may accept either source, and a same-audience require row on another origin also refuses a request with no source. RpVerificationSource is recorded with the rp_verifications event; it is not a verification status and never replaces proof_source.

18.4 Attestation status values

AttestationStatusValue is active, suspended, or revoked. It describes the attestation at a point in time, not the RP's policy. A registry verification.status uses the same values and only active earns a badge.

18.5 HTTP error body

Every new route and every path where a client branches uses the OAuth-style body:

{
  "error": "invalid_request",
  "error_description": "Human-readable, non-secret explanation",
  "code": "stable_machine_code"
}

code is additive to existing OAuth bodies. Existing routes that do not yet emit code keep working; new routes always emit it. error_description never includes key material or internal paths.


19. Verification Rules

19.1 Two independent results

Document conformance and attestation verification are separate outcomes:

  • A malformed rp_attestation field (not three base64url segments, over 16384 characters, or not a string) is a conformance error with no verification verdict: the validator reports attestation.status: not_checked (reason field_malformed), not invalid, and exits 1.
  • A structurally valid JWS whose header, claims, or signature fail is a verification error (invalid), not a conformance error.
  • An expired attestation is a verification warning; a bad signature, origin mismatch, or audience mismatch is a verification failure.
  • A broker/JWKS fetch failure is unavailable — a warning by default, never a conformance failure.

Document conformance never requires a live RP broker. Only explicit attestation verification (§19.2) contacts one.

19.2 Validator behavior

The validator (validator.md) keeps its conformance job and adds an independent attestation result:

Flag Behavior
--verify-attestation Parse and verify rp_attestation if present; opt-in issuer JWKS fetch. An absent field is unverified + warning, exit unchanged.
--require-attestation Implies --verify-attestation; a non-verified result fails the run.
--trusted-rp-broker <issuer> Repeatable. Flags override the env fallback; if any flag is present, the env list is ignored. Env fallback AGENT_IDENTITY_TRUSTED_RP_BROKERS (comma-separated). An empty effective list trusts nothing.
--jwks <path-or-url> Pin exactly one issuer's JWKS for offline runs; never bypasses signature checks and never merges issuers.
--at <rfc3339|epoch> Freeze the clock for deterministic fixture runs; not for production use.

Exit 0 means the document was fetched, parsed, and conformant and no required check failed. Exit 1 means a conformance error (including a body that is not valid JSON, or a malformed rp_attestation field) or an attestation failure under --require-attestation. Exit 2 means the run could not obtain what it needed (document read/fetch failure, or JWKS unavailable under --require-attestation) and is never a conformance verdict: the conformance verdict is not evaluated and human output prints DOCUMENT: NOT CHECKED. An unverified, expired, or invalid attestation stays exit 0 without --require-attestation. A run that explicitly requests verification warns when the optional field is absent (exit unchanged).

The validator never emits suspended: that status comes from the broker or the status endpoint.

19.3 Trust before fetch

The trusted-issuer check happens before any JWKS or status fetch in Contract G, Contract F, and Contract D.2. A verifier never contacts an untrusted broker, and Contract F/D.2 apply the full Contract G verification, not just a signature check.

19.4 Caching and key handling

  • JWKS are cached per iss and never merged. jwks_uri is same-origin with issuer and is used only when the metadata document declares the expected issuer; otherwise the fixed <iss>/.well-known/jwks.json path is used (§16.1).
  • --jwks applies to exactly one issuer.
  • Metadata is cached at most 1 hour; status responses at most 5 minutes; the attestation exp is the primary bound.
  • A retired broker signing key stays in the JWKS for at least 30 days after the last attestation it signed expires (§16.1).

19.5 SSRF and bounded fetch

Every broker and origin fetch — discovery, JWKS, metadata, status, origin challenge — is HTTPS only (localhost development exception), public IP addresses only, no cross-origin redirect, 5-second timeout, 64 KiB cap, and never follows a URL from a response body implicitly. A fetched URL never causes a side effect.

19.6 Algorithm and key confusion

Reject alg != EdDSA, a typ mismatch, a missing kid, and any key not selected by kid. Never try all JWKS keys, never accept none or HMAC, and never accept a jku/x5u header pointing at an arbitrary host.

19.7 Historical vs current

Contract E and Contract F compare the receipt's signing time to the attestation window; clock skew applies only to current-time comparisons. Contract D.2 and status responses are current events with fixed 300-second bounds. A verifier MUST NOT substitute a newer attestation for the one a receipt names.

19.8 Fail closed

require and --require-attestation fail on unavailable. warn is advisory and never claims to be a control. A missing origin for the attestation binding makes verification unavailable; it never silently produces verified.


20. Security Considerations

  • Transport: HTTPS required; localhost development exception only.
  • Issuer spoofing: exact iss allowlist matching and issuer-origin JWKS only.
  • Audience confusion: a single, exact aud, stable per service.
  • ToS substitution: the agent recomputes the hash from terms.text; the RP rejects stale or mismatched hashes.
  • Replay vs resolve: the jti guard is an account-creation guard; a repeated identical request for the same agent/account/service resolves idempotently, and assertion_replayed is reserved for conflicting reuse. Acceptance is the durable legal event; assertions are short-lived proofs (§7.2, §7.3).
  • Credential handoff & recovery: provider credentials are show-once; agents persist them at the handoff moment, the Broker never stores them, and recovery is a separate vendor rotation path — never a signup re-run (§12.5).
  • Prompt injection: discovery content, legal text, and the vendor guide are data, never instructions.
  • Post-signup context: the URL stays on the vendor's registrable domain, uses HTTPS, is hash-pinned, and is size-capped; the broker validates it statically and never fetches it during receipt ingestion.
  • SSRF: agents and brokers must not fetch arbitrary internal addresses from a discovery document; localhost is allowed only in explicit development mode.
  • Discovery tampering: TLS plus the signed assertion; the discovery document itself is not a trust anchor.
  • DoS: response size limits, timeouts, caching, and rate limiting.
  • Privacy: no human PII or secrets in the public discovery document.
  • Cache poisoning: short cache lifetimes and revalidation before signup.
  • RP attestation replay: the attestation is public and copyable; the origin + audience binding is the control, never secrecy. An attestation from another origin or for another audience is invalid, not unverified.
  • Trust before contact: an untrusted RP-broker iss is rejected before any JWKS or status fetch, so a malicious document cannot steer a verifier at a third-party host. JWKS are cached per iss and never merged; --jwks pins exactly one issuer.
  • RP key compromise and rotation: receipts remain verifiable across rotation because the broker retains retired attestation-signing keys for at least 30 days after the last attestation they signed expires, and the verifier checks the receipt against the attestation it names, not the current one.
  • No broker in the signup path: local attestation verification is the default. Enforced mode verifies at minting time; there is no per-signup A→B call and no tracking oracle.
  • Verification is identity, not safety: neither assurance level (domain, entity) asserts safety, uptime, solvency, or conduct. warn is advisory; require is a hard gate; block never proceeds.
  • Fixture credentials: the checked-in fixture signing seed is a warning-marked test vector. It is not a credential, is never referenced by broker configuration, and signs nothing outside fixtures/. No production or non-fixture private key is committed.

21. Conformance Checklist & Minimal Conformant Example

21.1 Relying Party conformance checklist

  • GET /.well-known/agent-signup.json returns 200 with Content-Type: application/json.
  • GET returns 404/410 (never 401/403) when the service does not support the protocol.
  • protocol is exactly agent-identity-v1.
  • audience, provider_name, a non-empty issuer_allowlist, and unique required_scopes (including tos:accept) are present.
  • terms.text is the complete ToS text; terms.sha256 is sha256:<64 lowercase hex> over its exact UTF-8 bytes; terms.url and terms.version are present and consistent.
  • endpoints.agent_signup is the only endpoint and is an absolute HTTPS URL.
  • No §5.3 forbidden vendor field appears anywhere, including under extensions.
  • CORS and cache headers follow §4; the body is within the 1 MiB cap.
  • Assertions are verified against issuer JWKS with every check in §7.1.
  • Stable §7.2 error codes are returned.
  • A context pointer, if the vendor has capabilities to document, is on the vendor's registrable domain, HTTPS, and hash-pinned; it is returned in the signup response and receipt but not in discovery.
  • signup_token (Contract E) contains identity facts, an opaque service.account_id, ToS facts, and optional context — and no instance_sku / endpoint_url.
  • A repeated signup for an existing account, keyed as recommended in §9.1, resolves idempotently: it returns resolution: "resolved", does not re-issue provider credentials, and sends no duplicate welcome/legal mail (SHOULD; §7.2, §12.5).
  • The vendor guide documents a credential recovery/rotation path reached after signup, and that path is not advertised in discovery or the receipt (SHOULD; §12.5).
  • rp_attestation, when published, is a compact JWS within the size bound; its rp.origin equals the fetched document origin and rp.audience equals audience (§5.4, §14).
  • A document without rp_attestation is still conformant and is reported as unverified, never invalid (§1.3, §18).
  • If the RP issues receipts, they are signed by a key in the attestation named by service.attestation_jti with the receipt purpose, and header.kid equals service.key_id (§11.4).
  • The RP publishes /.well-known/agent-broker.json only if it is also an RP broker; a relying party needs broker metadata only when it verifies peers or accepts abuse reports (§16).
  • An RP that verifies peer attestations checks the trusted-broker list before fetching JWKS and binds rp.origin to the fetched document origin (§14.2, §19.3).
  • The attestation is renewed on the 12-hour target against the 24-hour expiry, and a receipt's attestation is retained rather than replaced by the current one (§15.3, §11.4).

21.2 Minimal conformant document

The §5.5 document is the minimal conformant example. It contains only identity fields and no vendor capability fields. This repository is the canonical location of this specification; the spec field may be omitted until a rendered copy exists.


22. Open Questions (non-blocking)

These items do not block v1. They are recorded so later revisions can address them deliberately. The v1.1.0 revision resolved the replay-versus-resolve and credential-handoff questions (see §7.2 and §12.5); canonical hosting remains open.

Question v1 position
Is a repeated signup with an already-used jti an error? Resolved in v1.1.0: a repeated request for the same agent/account/service resolves idempotently; assertion_replayed is reserved for a conflicting context (§7.2).
How long does a ToS acceptance last? Resolved in v1.1.0: no protocol-mandated re-acceptance interval; an acceptance lasts until the terms change or the human re-consents. A Broker MAY impose its own documented policy (§7.3).
How are provider credentials handed off and recovered? Resolved in v1.1.0: credentials are show-once at signup and never stored by the Broker; recovery is a vendor rotation path documented in the guide, never a signup re-run (§12.5).
How does an agent verify a relying party? Resolved in v1.2.0: an OPTIONAL broker-issued attestation (Contract G) is verified locally against the RP broker's JWKS (§14).
Where is RP verification recorded? Resolved in v1.2.0: in the rp_verifications ledger, not as columns on immutable consent and not as a Contract A claim (§17.2, §18).
How does a client find the RP broker's JWKS, status, and abuse endpoints? Resolved in v1.2.0: /.well-known/agent-broker.json (Contract I), with fixed-path fallbacks (§16.1).
How is revocation bounded without a per-signup call? Resolved in v1.2.0: 24-hour expiry with a 12-hour renewal target and 60-second skew; the signed status endpoint is opt-in defense-in-depth with a 5-minute cache (§14.2, §16.2).
Does a signup receipt expire? Resolved in v1.2.0: no. A receipt is a historical fact valid inside the attestation window at signing time; a legacy exp is ignored (§11.1, §11.4).
What happens to an abuse report with no linked receipt? Resolved in v1.2.0: it is unverified, creates a case, and does not suspend the agent; only a verified, receipt-linked report can request protective suspension (§11.5).
How does the registry distinguish conformance from verification? Resolved in v1.2.0: an OPTIONAL verification object in the registry with broker, level, attestation jti, checked_at, and status; only active earns a badge (registry/README.md).
IANA registration of the well-known URI. Evaluate after v1 stabilizes; do not block v1.
Final canonical public hosting URL for this specification. This repository is the canonical location of the specification until a rendered copy exists in the documentation site. The optional spec field points at a versioned copy and stays OPTIONAL; the placeholder https://<canonical-spec-host>/agent-identity/v1/agent-signup is illustrative only.
A future alias such as agent-identity.json. Keep agent-signup.json for v1; consider an alias in v2.
Structured issuers metadata beyond issuer_allowlist. Not in v1; a fixed <issuer>/.well-known/jwks.json path is required. Add structured metadata later only if a real need appears.
Signing the discovery document. Not in v1; TLS + issuer JWKS + signed assertions are the trust path.
A Broker-side well-known discovery document. Not in v1; the Broker is reached through MCP and publishes OAuth metadata + JWKS.
Handling multiple ToS versions during rollout. Advertise only the current version; allow a bounded, RP-defined grace period for in-flight assertions, documented by the RP outside the discovery document (for example SKILL.md or a support page), SHOULD NOT exceed 24 hours.

23. Relationship to Other Contracts

Contract Relationship
Contract A — Agent Identity JWT assertion Referenced, not redefined. This spec defines discovery and presentation.
Contract D.1 — Relying Party discovery This document is the normative definition; the historical reference implementation's documentation summarizes it.
Contract D.2 — Abuse reporting Authenticated RP abuse report JWS signed by an RP key in the attestation (§11.5); unauthenticated legacy posts are rejected.
Contract E — signup receipt RP-signed v1.2 receipt: identity facts, service.attestation_jti/service.key_id, optional context, no exp (§11.1, §11.4).
Contract F — broker signup receipt callback Authenticated receipt ingestion profile: {signup_token, rp_attestation} verified in historical mode (§11.6); not agent-facing discovery.
Contract G — RP attestation New broker-signed RP identity published as the optional rp_attestation field (§14).
Contract H — provider registration and status API New administrative API: origins, keys, attestation issuance/renewal (§15); no public JSON Schema.
Contract I — broker metadata and attestation status New public metadata document and signed status response (§16).

The scope boundary between this identity protocol and vendor responsibilities is defined in scope-boundaries.md. Any proposed new protocol field must pass that document's review checklist before it is added here.

sections

type to search.

up/down navigateenter selectesc close