A successful signup can return two identity artifacts: an RP-signed signup
receipt (Contract E) and an optional context pointer to the vendor's
post-signup guide. The receipt is proof of who signed up, for whom, under which
terms, and when. The context pointer is an opaque, hash-pinned handoff to vendor
documentation; it is not a capability catalog.
The normative rules are spec section 11 and section 12. The relying-party steps are in the Implementation guide.
The signup response
On success the relying party returns 200 or 201 with:
- provider credentials, when an account was created;
- an optional
resolutionfield:"created"or"resolved"; - optionally, an RP-signed
signup_tokenper Contract E; - optionally, a
contextpointer.
resolution is optional. An absent resolution is conformant. "resolved" is
not a failure: the account already existed and was resolved. On "resolved" the
relying party may omit provider credentials (show-once) but must point the agent
at the credential recovery path in the guide.
Contract E claims
The v1.2 receipt is a compact JWS with header {"alg":"EdDSA", "typ":"agent-signup-receipt+jwt","kid":"<rp key id>"}. The typ is unchanged; the
claims changed.
| Claim | Type | Meaning |
|---|---|---|
iss |
string | The RP origin. It must equal service.origin and the attestation rp.origin. |
sub |
string | The agent id. |
aud |
string | The human broker issuer. |
iat, signed_up_at |
integer (Unix seconds) | Signing time and signup time. Both must fall inside the named attestation's window. |
jti |
string (srp_...) |
Receipt id. The broker records it as signup_token_jti. |
assertion_jti |
string | The jti of the assertion presented at signup. |
service.audience |
string | The relying party's audience; must equal the attestation rp.audience. |
service.origin |
string | The RP canonical origin; must equal iss. |
service.provider_name |
string | The relying party's provider_name. |
service.provider_url |
string, optional | The relying party's canonical URL. It is not an identity binding. |
service.account_id |
string | Opaque vendor account reference. It is not a resource inventory. |
service.attestation_jti |
string | The attestation that contained service.key_id. |
service.key_id |
string | The RP key that signed the receipt; must appear in the attestation keys[] with receipt in its purposes, and must equal the JWS header kid. |
agent.id, agent.name, agent.email |
string | The agent sub-identity. |
human.id, human.name, human.email, human.verified |
string / boolean | The verified human owner. The broker derives these from the trusted acceptance, not from the receipt body. |
tos.version, tos.url, tos.sha256 |
string | The accepted relying party terms. |
tos.master_tos_version |
string, optional | The Master Terms version the human accepted. |
context |
object, optional | The hash-pinned post-signup guide. See below. |
No exp. A v1.2 receipt has no expiry. A legacy receipt may carry exp;
verifiers ignore it and never reject a receipt because it passed. The removed
vendor claims service.instance_sku and service.endpoint_url stay removed. No
free-form vendor metadata object exists.
Signing and validity
- The signing key is the RP key named in
service.key_id. It must appear in the attestation identified byservice.attestation_jti. - There is no broker-key fallback. A new verified proof requires an RP key inside a valid attestation.
- The receipt is valid when both
iatandsigned_up_atfall inside that attestation's[iat, exp]window. This is a historical comparison; the 60 second skew applies only to current-time checks. - The RP must retain the exact attestation that was current when it signed and
submit that JWS to Contract F. After renewal the current attestation has a new
jti; the receipt still names the old one. - A broker that cannot retrieve the named attestation and has no snapshot returns
attestation_unavailableinstead of substituting a newer attestation. - Receipts are idempotent forever on
jti. Samejtiwith a different body is409 signup_conflict; the same body returns the existing row withdeduplicated: true.
Contract F and proof sources
Contract F (POST /api/broker/signup-receipt) accepts exactly:
{
"signup_token": "<compact Contract E receipt JWS>",
"rp_attestation": "<the exact attestation named by service.attestation_jti>"
}Both fields are required. The broker checks the effective RP-broker trust list
before any JWKS fetch, verifies the historical attestation and the receipt
signature, and records an event_kind:"signup" verification snapshot with
proof_source:"rp_callback". It never uses a broker key and never substitutes the
current attestation.
identity_record_signup (Tier 2) carries the same receipt, optionally with the
exact attestation. If the attestation is omitted, the broker fetches discovery at
service.origin and requires the presented attestation's jti to equal
service.attestation_jti. Tier 2 records rp_signed.
| Proof source | Meaning |
|---|---|
rp_callback |
Tier 1: the RP posted the receipt to Contract F. |
rp_signed |
Tier 2: the agent recorded the same RP-signed receipt. |
agent_self_reported |
No RP signature; weaker and marked. |
legacy_broker_signed |
Pre-v1.2 row; historical and unverified. |
Tier 1 wins. A matching Tier 2 row can be upgraded by Tier 1. A different account
id for the same soft key is flagged conflict for operator review; it is never
silently overwritten. Only rp_callback and rp_signed with a verified
attestation give an abuse report standing.
The context pointer
| Field | Requirement | Meaning |
|---|---|---|
context.url |
Required when context is present |
Absolute URL to the vendor's agent-facing integration guide, such as SKILL.md. HTTPS is required except the localhost development exception. |
context.sha256 |
Required when context.url is present |
sha256: followed by 64 lowercase hex characters, over the exact UTF-8 bytes of the guide. |
The pointer appears in the signup response and in the signed receipt. Discovery
does not advertise a post-signup context field. After the broker records the
receipt, the same two values are returned to the agent as context_url and
context_sha256 on
identity_record_signup, alongside
the handoff reminder.
Domain rule
context.url must stay on the vendor's registrable domain. The authority is the
signup endpoint's origin (endpoints.agent_signup), not provider_url.
- When
provider_urlis present, it must share the signup endpoint's registrable domain. - The broker compares
context.urlagainst the signup endpoint's registrable domain and rejects a pointer on any other domain. - The signed receipt carries
service.provider_urlbut not the signup endpoint. A receipt that omitsservice.provider_urlcannot establish the domain; the broker fails closed and rejects the pointer.
Integrity and trust
- Fetch the guide after signup and recompute SHA-256 over the exact bytes received. On mismatch, do not act on the guide.
- The guide should be size-capped (1 MiB recommended) and served as text or Markdown.
- The guide is untrusted vendor documentation, not broker policy and not agent instructions. The broker vouches for who signed up and what terms were accepted, not for the guide's contents.
- The broker validates the pointer statically and does not fetch it during receipt ingestion, so the receipt path cannot become an SSRF surface.
- Re-fetch the guide when a new provisioning task starts, and re-verify the hash on each fetch.
An illustrative pointer shape:
{
"context": {
"url": "https://example.com/docs/SKILL.md",
"sha256": "sha256:1111111111111111111111111111111111111111111111111111111111111111"
}
}The digest is a placeholder. The relying party computes it over the exact guide bytes.
Credential handoff and recovery
Provider credentials are shown once, at the signup handoff. The protocol draws a firm line between the broker-mediated identity event and the vendor's credential lifecycle:
- Show-once handoff. The relying party returns the provider credential in the signup response. Provider credentials are vendor scope and are not carried in the signed receipt, discovery, or broker storage.
- Agents persist at handoff. Persist provider credentials to a secure store at the moment of handoff. Do not rely on the conversation, context window or transcript as storage.
- The broker does not store provider credentials. It records the identity and attribution proof and the optional context pointer. It does not store, proxy or re-issue provider credentials.
- Do not re-run signup for recovery. A repeated signup on an existing account resolves the account and does not re-issue credentials.
- Relying parties should document recovery. A vendor should document a credential recovery or rotation path in its guide, reached only after signup. Recovery is vendor capability, so it stays out of discovery and the receipt.
- Resolve does not mint credentials or mail. Resolving an existing account must not mint new provider credentials and must not send duplicate welcome or legal mail. A fresh receipt may be issued so a dropped-response retry can still record the proof.
Known limits
- The receipt records identity and attribution only. It is not a resource inventory and must not be extended into one.
- The broker does not verify the guide's contents. It validates the pointer's form and domain, does not dereference the guide, and does not recompute the hash. The agent must fetch the guide and verify the digest before acting.
- A receipt that omits
service.provider_urlcannot prove the context domain, so the broker rejects the pointer rather than skipping the check. - A historical attestation must be retained by the RP or recoverable from the
broker's snapshot. After the broker's key-retention window, an unseen old
receipt is
attestation_unavailablerather than silently trusted. - Credential recovery depends on the vendor publishing a recovery path. The protocol cannot recover a credential the agent failed to persist.