Generated from
agentregistrar/specatv1.2.0(tagv1.2.0). Do not edit this page; edit the spec and re-runpnpm sync:spec.
Agent Identity Relying Party Discovery v1 (agent-signup.json)
- Status: Normative specification —
v1.2.0. Maintained inagentregistrar/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_signuphandshake, including stable error codes; - the identity facts of the signup receipt (Contract E) and the optional post-signup
contextpointer; - 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
protocolfield:agent-identity-v1. - This specification revision is
v1.2.0. Minor/patch revisions are additive and do not change theprotocolvalue; breaking changes require a newprotocolvalue and a new discovery path (§13). Additive v1 changes are recorded in the changelog above and must pass thescope-boundaries.mdreview checklist; the process is written down inrelease-notes-v1.0.0.md§3. rp_attestation(§5.2) is OPTIONAL. Av1.0.0orv1.1.0discovery 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
specfield points at a versioned copy and stays OPTIONAL; implementations may omit it, and the placeholderhttps://<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/blockpolicy (§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> ----------------->|- The agent fetches the discovery document only when the user targets that service (§8).
- The agent verifies the document, checks
issuer_allowlistandrequired_scopes, and computes the ToS hash fromterms.text(§6). - The agent calls its Broker's
identity_provewith the accepted ToS URL/hash and text to obtain a signed assertion scoped foraudience(§7). - The agent
POSTs the assertion toendpoints.agent_signup. - The RP verifies the assertion, completes signup, and returns provider credentials plus, where
supported, a signed
signup_token(Contract E) and the optionalcontextpointer. - 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. - 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.jsonon the Relying Party's canonical HTTPS origin. GETMUST return200 OKwithContent-Type: application/jsonwhen the service supportsagent-identity-v1.GETMUST return404 Not Found(or410 Gone) when the service does not support the protocol. A401/403MUST NOT be used to signal non-support.OPTIONSSHOULD return204with permissive CORS headers so browser-based inspectors and agent tools can read the document.- Responses SHOULD include
Cache-Control: public, max-age=60(maximum recommended300) andAccess-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.1development 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 supportsagent-identity-v1; the body is the discovery document.404 Not Found/410 Gone— the service does not supportagent-identity-v1. The agent MUST treat the service as unsupported and stop.5xxor 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_allowlistentries MUST be absolute origins with no path, query, fragment, or wildcard, and no duplicate after trailing-slash normalization.required_scopesentries MUST be non-empty strings and unique.- URLs advertised for
terms.url,endpoints.agent_signup,provider_url, andspecMUST 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. Itsrp.originMUST equal the origin of the discovery document the verifier actually fetched — notprovider_url, notterms.url, and not a redirect target — and itsrp.audienceMUST equaldiscovery.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 fromterms.url,provider_url, or the attestation. This path is recorded asagent_supplied_discoveryand arequirepolicy MUST reject it (§17). provider_urlis 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.sha256MUST use the formsha256:<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.textMUST be the complete agreement, not a summary.terms.urlMUST be an absolute HTTPS URL (localhost development exception) where a human can read the same agreement.terms.versionMUST change whenever the text changes materially.- The agent MUST recompute the hash from
terms.textand compare it withterms.sha256before calling the Broker, and its Broker MUST verify the accepted text against the hash it records. The agent MUST NOT trust the advertisedterms.sha256alone. - The RP MUST reject a signup assertion whose
delegation.accepted_rp_tos_hashdoes not equal the currently advertisedterms.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.mdor support page) and SHOULD NOT exceed 24 hours. - The Broker records the accepted URL/hash in
tos_acceptancesand in the assertion; the RP records the same hash and the assertionjtifor 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
-
The agent obtains an assertion from its Broker for
audience,terms.url,terms.sha256, and therequired_scopes, passing the accepted ToS text so the Broker can verify the hash it records. The Broker MUST require the caller to demonstrate knowledge ofterms.textand MUST reject a request that does not (the hash check inidentity_prove; §10). -
The agent sends:
POST <endpoints.agent_signup> Authorization: Bearer <agent-identity-assertion> Content-Type: application/jsonThe request body is RP-specific and MAY be empty.
-
The RP verifies at minimum:
- the JWT signature against the issuer's
GET <iss>/.well-known/jwks.json; issis inissuer_allowlist(exact match);audequalsaudience;exphas not passed (allow at most 60 seconds of clock skew);jtiis not reused in a conflicting context: a repeatedjtifor the same agent/account at the same service is not a replay and MUST resolve idempotently rather than fail asassertion_replayed(§7.2);- the assertion lifetime is reasonable (recommended maximum 5 minutes);
delegation.scopesincludes everyrequired_scopesentry;human.verified === true;delegation.accepted_rp_tos_hash === terms.sha256.
- the JWT signature against the issuer's
-
On success the RP returns
200/201with an optionalresolutionfield, provider credentials, and, where supported, a signedsignup_tokenper Contract E (§11) and the optionalcontextpointer.resolutionis"created"when the account was created and"resolved"when an existing account was resolved; aresolvedoutcome 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. -
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
}errorMUST be one of the codes below.error_descriptionSHOULD be present but is not normative and MUST NOT be parsed for control flow.retryableSHOULD 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.sha256until the terms change (a newterms.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_signupand does not make an RP responsible for prompting re-acceptance.
8. Agent Consumption Rules (prompt-injection & SSRF)
Agents MUST:
- Fetch discovery only when the user explicitly targets that service; MUST NOT follow arbitrary links from another service's discovery document.
- 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. - 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. - Verify that the agent's delegated scopes cover
required_scopesbefore requesting an assertion. - Fetch the document immediately before signup, or honor its cache lifetime; never reuse a stale ToS hash.
- 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.
- Never reuse an assertion across audiences, services, or after expiry.
- Treat
tos_hash_mismatchas a hard stop requiring fresh discovery and re-acceptance. - After signup, if the response or receipt contains a
contextpointer, 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.1MAY 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:
- Publish the document for as long as they claim
agent-identity-v1support. - Keep
terms.text,terms.sha256, andterms.urlmutually consistent and atomically updated. - Verify assertions rather than merely parsing them; never trust an unsigned or unverified JWT.
- Resolve issuer keys only from the issuer's canonical
/.well-known/jwks.json; never accept ajku/x5uheader pointing at an arbitrary host. - Reject unknown issuers, wrong audiences, expired assertions, replayed
jtis, insufficient scopes, unverified humans, and stale ToS hashes, using the §7.2 codes. - Record the assertion
jti, the accepted ToS version/hash, and attribution for audit. - Return stable machine-readable errors and rate-limit abusive callers.
- Provide a contact for abuse/security reports and use Contract D.2 (§11.5) to report misbehaving agents to the Broker.
- If the RP has vendor capabilities to document, publish a post-signup context guide and return its
url+sha256on 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.emailoragent.emailis 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_allowlisthas more than one entry), the key MUST be namespaced by the assertion'sissso that identities from different Brokers cannot collide. human.verifiedis 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.jsonas 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 stablekid, a single stringaud, andiss,jti,iat,exp,agent,human, anddelegationclaims. - 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 ofterms.textis 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,iatassertion_jti— thejtiof the assertion presented at signupsigned_up_atservice.audience,service.origin,service.provider_name,service.provider_url,service.account_id,service.attestation_jti,service.key_idagent.id,agent.name,agent.emailhuman.id,human.name,human.email,human.verifiedtos.version,tos.url,tos.sha256, optionaltos.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_skuservice.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 attestationkeys[]identified byservice.attestation_jtiwithreceiptinpurposes. There is no broker-key fallback: a new verified proof requires a key inside a valid attestation. - A receipt is valid when both
iatandsigned_up_atfall 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 returnsattestation_unavailable(§11.6) instead of substituting the newer attestation. - Receipts are idempotent forever on
jti. The samejtiwith a different body is a conflict (409 signup_conflict); the same body returns the existing row withdeduplicated: true. service.provider_urlstays OPTIONAL and is not an identity binding.contextkeeps 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:
- 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'sdistrustMUST NOT suppress abuse evidence, and D.2 has no human session. - Require
typ: "agent-rp-abuse+jwt",alg: "EdDSA", and exactly onekid. ThekidMUST be inattestation.keys[]withabuseinpurposes; verify the report signature with that key. - Require
issequal to the attestationrp.originandaudequal to A's issuer. - 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 whennowis not beforeiat - 60sand never afterexp. - 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.urlandcontext.sha256in itsPOST /api/signup/agentresponse and in the signedsignup_token. - The broker persists the context and exposes it to the agent after signup (Contract F response and
the
identity_record_signupMCP 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 (noservice.provider_urlin 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.sha256whenever 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 optionalcontextpointer; 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_tokenMAY be issued so a dropped-response retry can still record the receipt.
13. Versioning, Extension & Deprecation Policy
protocolcarries 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
protocolvalue and a new discovery path (for exampleagent-signup-v2.json). The v1 path MUST NOT silently change semantics. - The public spec URL SHOULD be versioned; the optional
specfield 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:
- Parse the compact JWS; require
typ,alg: EdDSA, and exactly onekid. issMUST be in the verifier's trusted RP-broker list. Reject an untrustedissbefore any JWKS or status fetch; the verifier never contacts an untrusted broker.- Verify against B's JWKS (
jwks_urifrom §16, else<iss>/.well-known/jwks.json), selecting the key bykid. Never try all keys. - Check
iat/expwith 60 seconds of skew.jtiMUST match the frozen pattern. rp.originMUST equal the origin of the discovery document actually fetched.rp.audienceMUST equaldiscovery.audience.- 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 futuremin_levelpolicy is additive. - The status endpoint (§16) MAY be consulted in enforced mode; otherwise
expis 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
429andRetry-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. |
issuerdiffering from the document origin invalidates the document.issueris a canonical HTTPS origin, exactly like an attestationiss(§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_urionly when the fetched document'sissuerequals the trusted attestationissandjwks_uriis HTTPS and same-origin with it; otherwise it MUST fall back to<issuer>/.well-known/jwks.json. A cross-origin or invalidjwks_uriMUST NOT be followed. Without metadata, clients fall back to<issuer>/.well-known/jwks.jsonand<issuer>/api/broker/report-abuse, where<issuer>is the assertioniss(Contract A) or the attestationiss(Contract G) being verified. The fallback is a client rule, not a second format. - Cross-origin
attestation_status_uriandabuse_endpointare 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_unavailablerather 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" }subis the queried attestationjti.exp - iatis exactly 300 seconds.statusis one ofactive,suspended,revoked.status_changed_atMUST NOT be afteriat.- The plaintext
attestation_jtiis advisory. Clients MUST verify the JWS and usesub, and MUST checktyp,alg, exactly onekid, the B signature,issequal to the metadataissuer,audequal toagent-identity-v1,exp/iatwith 60 seconds of skew, andsubequal to the queriedattestation_jti. suspendedmaps toRpVerificationStatus.suspended.revokedmaps toinvalidwithreason:"attestation_revoked"because the frozen enum has norevoked(§18). The status response itself carries onlystatusandstatus_changed_at.- Producer and verifier modes apply to the status JWS too: the published JSON Schema and
AttestationStatusClaimsSchemaare 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:
- 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.
- 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.
- 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:
- Normalize the requested service to an origin.
- Fetch and validate discovery (SSRF-safe, bounded).
- Validate protocol, terms, scopes, and issuer allowlist.
- Verify
rp_attestationif present (§14.2). - Resolve trust: the broker default plus the human's override.
- Resolve policy: the human's rule for
(rp_origin, rp_audience), elsewarn. - Compute
RpVerificationStatus(§18). - Apply the policy matrix (§17.3).
- Mint and record the verification event.
- 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 |
requireis always broker-enforced. Arequirepolicy that is only advisory would be a lie.allowandwarnMAY setenforced: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:trueis not a hidden hard gate forallow/warn; onlyrequirerefuses.requireMUST rejectsource:"agent_supplied_discovery"withdiscovery_source_not_allowedand accept only a broker-fetched discovery document.- The default policy is
warn. The trusted RP-broker list is the broker default plus the human'strustoverride minus the human'sdistrustoverride; 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 ordinaryidentity_signuppath and Contract F). The fetched document origin is authoritative.agent_supplied_discovery— the caller supplied a discovery object plus an explicit canonicalrp_origin(the advancedidentity_provepath; §5.4, §14.3). The declared origin is used and is never inferred fromterms.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_attestationfield (not three base64url segments, over 16384 characters, or not a string) is a conformance error with no verification verdict: the validator reportsattestation.status: not_checked(reasonfield_malformed), notinvalid, and exits1. - 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
issand never merged.jwks_uriis same-origin withissuerand is used only when the metadata document declares the expected issuer; otherwise the fixed<iss>/.well-known/jwks.jsonpath is used (§16.1). --jwksapplies to exactly one issuer.- Metadata is cached at most 1 hour; status responses at most 5 minutes; the attestation
expis 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
issallowlist 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
jtiguard is an account-creation guard; a repeated identical request for the same agent/account/service resolves idempotently, andassertion_replayedis 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, notunverified. - Trust before contact: an untrusted RP-broker
issis rejected before any JWKS or status fetch, so a malicious document cannot steer a verifier at a third-party host. JWKS are cached perissand never merged;--jwkspins 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.warnis advisory;requireis a hard gate;blocknever 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.jsonreturns200withContent-Type: application/json. -
GETreturns404/410(never401/403) when the service does not support the protocol. -
protocolis exactlyagent-identity-v1. -
audience,provider_name, a non-emptyissuer_allowlist, and uniquerequired_scopes(includingtos:accept) are present. -
terms.textis the complete ToS text;terms.sha256issha256:<64 lowercase hex>over its exact UTF-8 bytes;terms.urlandterms.versionare present and consistent. -
endpoints.agent_signupis 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
contextpointer, 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 opaqueservice.account_id, ToS facts, and optionalcontext— and noinstance_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; itsrp.originequals the fetched document origin andrp.audienceequalsaudience(§5.4, §14). - A document without
rp_attestationis 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_jtiwith thereceiptpurpose, andheader.kidequalsservice.key_id(§11.4). - The RP publishes
/.well-known/agent-broker.jsononly 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.originto 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.