the spec
overview
the welcome mat is the signup pattern for services built for AI agents. agents autonomously discover and register using cryptographic identity, signed consent, and proof-of-possession authentication. it follows a Trust on First Use (TOFU) model — the same trust pattern behind SSH — where both sides accept the other's identity on first contact, then verify cryptographically on every subsequent request.
a service publishes a markdown file at /.well-known/welcome.md describing its requirements and enrollment flow. agents fetch this file, generate a cryptographic identity, sign the terms of service, and register — no human intervention required.
authentication is built on DPoP (RFC 9449). agents prove key possession on every request via signed DPoP proofs in HTTP headers. the access token is a self-signed JWT encoding the agent's consent to the service's current terms. when terms change, existing tokens become invalid — agents must re-consent to continue.
the welcome.md file
location
the file MUST be served at /.well-known/welcome.md over HTTPS with content type text/markdown or text/plain.
format
plain markdown with the following required and optional sections. the file is both human-readable documentation and the machine-readable discovery endpoint — agents parse the markdown directly.
required sections
| service name | the H1 heading. identifies the service. |
| description | a brief paragraph explaining what the service does and what agents can do here. |
| requirements | protocol version, supported DPoP algorithms, minimum key sizes. |
| endpoints | URLs for terms of service (GET) and signup (POST), plus any service-specific API endpoints. |
| enrollment flow | step-by-step instructions with request/response examples for the complete signup process. |
optional sections
| signup requirements | service-specific fields required during registration (handle, subject, etc.). |
| ref policy | declares whether ref is accepted, ignored, or required; describes any service-specific semantics for the URL's fragment, query, or path. |
| rate limits | request rate limits, cooldown periods, or throttling policies. |
| pricing | cost information, free tier limits, or payment requirements. |
| usage policies | acceptable use, content policies, or behavioral expectations. |
| terms of service | inline ToS text or a link to the full terms. the ToS endpoint (GET) returns the exact text for signing, but including it here gives agents context before starting enrollment. |
example
# example service a platform for AI agents to share and discover resources. ## requirements - protocol: welcome mat v1 (DPoP) - dpop algorithms: RS256 - minimum key size: 4096 (RSA) ## endpoints - terms: GET https://example.com/tos - signup: POST https://example.com/api/signup ## signup requirements - handle: required ## enrollment flow ### 1. get terms GET /tos — no authentication needed: GET /tos HTTP/1.1 Host: example.com response: the ToS text as text/plain. ### 2. sign up POST /api/signup HTTP/1.1 Host: example.com DPoP: <proof JWT> Content-Type: application/json { "tos_signature": "base64url-encoded-signature", "access_token": "eyJ0eXAiOiJ3bStqd3QiLC...", "handle": "your-chosen-handle" } response: { "access_token": "eyJ0eXAiOiJ3bStqd3QiLC...", "token_type": "DPoP", "handle": "your-chosen-handle" }
enrollment flow
the enrollment flow covers steps 1–5: discovery, identity generation, terms retrieval, consent, and registration. these steps stand alone — a service that uses a non-HTTP protocol for ongoing communication (WebSockets, TCP, gRPC, etc.) can complete enrollment over HTTP and issue protocol-native credentials in the signup response. step 6 (authenticated requests) describes the standard HTTP path using DPoP headers.
agent service
| |
| GET /.well-known/welcome.md |
|--------------------------------------------->|
| markdown (endpoints, algorithms, |
| signup requirements) |
|<---------------------------------------------|
| |
| GET /tos |
|--------------------------------------------->|
| terms of service text |
|<---------------------------------------------|
| |
| sign ToS text with private key |
| generate self-signed access token JWT |
| { tos_hash, aud, cnf, iat, jti } |
| |
| POST /signup |
| DPoP: <proof, no ath> |
| Body: { tos_signature, access_token, ref?, |
| ...service-specific fields } |
|--------------------------------------------->|
| { access_token, token_type: "DPoP", |
| ...service-specific response } |
|<---------------------------------------------|
| |
| --- enrolled --- |
| |
| POST /api/action |
| Authorization: DPoP <access_token> |
| DPoP: <proof, with ath> |
| Body: { business data } |
|--------------------------------------------->|
| { ok } |
|<---------------------------------------------|
1. discovery
the agent fetches GET /.well-known/welcome.md from the service. this returns the welcome mat file containing requirements, endpoints, and enrollment instructions.
agents SHOULD check for /.well-known/welcome.md on any service they want to interact with. the presence of this file indicates the service supports agent-initiated signup.
2. identity generation
the agent generates a keypair using one of the algorithms listed in the service's requirements section. the public key becomes the agent's identity on this service. the private key MUST be stored securely and never transmitted.
agents MAY reuse the same keypair across multiple services (portable identity) or generate a unique keypair per service (isolated identity).
3. terms retrieval
GET /tos HTTP/1.1 Host: example.com
the agent fetches the terms of service from the URL specified in the welcome.md endpoints section. this is a plain GET request — no authentication required. the terms are a public document.
the response body is the canonical text the agent must sign. services SHOULD serve terms as text/plain or text/markdown for unambiguous agent consumption.
4. consent and access token generation
the agent performs two operations locally:
sign the ToS text — a direct signature over the ToS text bytes using the agent's private key and the same algorithm as the DPoP proof. the signature is base64url-encoded.
generate a self-signed access token — a JWT signed with the agent's private key:
HEADER: {"typ": "wm+jwt", "alg": "RS256"}
PAYLOAD: {
"jti": "<unique identifier>",
"tos_hash": "<base64url-encoded SHA-256 of the ToS text>",
"aud": "<service origin, e.g. https://example.com>",
"cnf": {"jkt": "<JWK SHA-256 Thumbprint per RFC 7638>"},
"iat": <unix timestamp>
}
the tos_hash binds the access token to the specific terms the agent consented to. aud prevents cross-service token confusion. cnf.jkt is an explicit key binding per RFC 9449 section 6. this binding is what enables automatic re-consent — when terms change, existing tokens become invalid.
this pair — the ToS signature plus the self-signed access token — is cryptographic proof of consent. it proves the holder of this specific private key agreed to this specific text, with the consent embedded in the token itself.
5. registration
POST /api/signup HTTP/1.1
Host: example.com
DPoP: <proof JWT, no ath>
Content-Type: application/json
{
"tos_signature": "base64url-encoded-signature-of-tos-text",
"access_token": "eyJ0eXAiOiJ3bStqd3QiLC...",
"handle": "chosen-handle",
"ref": "https://example.com/#inv_01HX7T9Z8K3MQR2"
}
the agent sends a DPoP proof, the ToS signature, the self-signed access token, an optional ref field (the verbatim entry URL the agent was handed — see registration reference below), and any service-specific fields to the signup endpoint.
the server validates the DPoP proof, verifies the ToS signature against the current ToS text, validates the access token (signature, tos_hash, aud, cnf.jkt), and creates the account. the server returns the approved access token:
{
"access_token": "eyJ0eXAiOiJ3bStqd3QiLC...",
"token_type": "DPoP",
"handle": "chosen-handle"
}
the server MAY return the agent's self-signed access token unchanged, or MAY return a different server-issued access token. the agent MUST use whichever access token the server returns. services MAY return a token_type value other than "DPoP" when the issued credential is intended for a non-HTTP protocol.
when a server issues its own token, the token SHOULD include a cnf.jkt claim preserving the JWK Thumbprint from enrollment. this maintains cryptographic identity continuity — the same key that proved possession during enrollment can prove possession in the target protocol. services MAY include additional protocol-specific claims (audience, endpoint, roles, etc.) in the issued token.
6. authenticated requests
POST /api/action HTTP/1.1
Host: example.com
Authorization: DPoP <access_token>
DPoP: <proof JWT with ath>
Content-Type: application/json
{"business": "data"}
after enrollment, API requests use standard DPoP authentication. the access token goes in the Authorization header. the DPoP proof includes an ath claim — the base64url-encoded SHA-256 hash of the access token string — binding each proof to the specific access token per RFC 9449 section 4.2.
request bodies contain only business data. authentication is entirely in HTTP headers.
registration reference
agents are typically given an entry URL out-of-band — "sign up here: <url>" — that points at the service. that URL MAY carry per-agent context the service wants to see at enrollment: an invite code, a referral attribution, a role grant, an account-linking token, a campaign tag. the welcome.md file is static and cacheable, so it cannot carry per-agent context; the entry URL can.
the ref field on the signup body carries the full entry URL the agent was handed, verbatim, including any fragment.
agent behavior
if the agent was given an entry URL for registration, it SHOULD include that URL verbatim as ref in the signup body:
{
"tos_signature": "base64url-encoded-signature-of-tos-text",
"access_token": "eyJ0eXAiOiJ3bStqd3QiLC...",
"ref": "https://example.com/?campaign=launch#inv_01HX7T9Z8K3MQR2"
}
the URL MUST be included exactly as the agent received it. fragments — which HTTP clients normally strip before sending a request — MUST be preserved. agents that did not arrive via a specific entry URL (hardcoded discovery, public listing, broadcast) MAY omit ref.
service behavior
the service MAY parse ref and use any part — origin, path, query, fragment — to link the enrolling agent with prior context. services SHOULD NOT require ref unless they explicitly advertise a referral-gated or invite-gated policy in their welcome.md.
the welcome.md MAY declare known ref semantics in a signup requirements or usage policies section — for example, "the fragment of ref MUST be an invite token issued by this service."
why a URL, not an opaque token
the full URL preserves every linking signal the service might want and lets each service interpret whichever parts it cares about:
- fragment — the privacy-preserving carrier for per-agent secrets (invite codes, referral keys).
- query — non-secret context (campaign, source, version).
- path — landing surface (which invite page, which team, which deployment).
- origin — which welcome.md the agent was directed at, when a service runs more than one.
services that only care about the fragment can extract it; services that want the full picture have it.
privacy and log surface
the discovery fetch is always GET /.well-known/welcome.md against the service origin — no part of the entry URL beyond the origin appears in the discovery request, regardless of which path, query, or fragment the entry URL carried. fragments specifically are stripped by HTTP clients before any request leaves the agent (RFC 3986 section 3.5). all of that information reaches the service only at signup, when the agent presents ref.
this separates two phases cleanly:
- discovery — public, cacheable, no per-agent context in server logs, CDN logs, or
Refererheaders. - enrollment — the agent consciously presents
refalongside ToS consent.
bearer semantics
ref is a bearer reference — whoever holds the entry URL can present it. single-use enforcement, expiration, allowlists, and rate-limiting are the service's responsibility, not the protocol's. services that issue invite URLs SHOULD treat them with the same care as any bearer token.
integrity
the DPoP proof does not sign the request body, so ref is not cryptographically bound to the agent's key at the protocol layer. TLS covers transit. services that need tamper-evident binding MAY echo a derived value into a server-issued access token claim per RFC 9449 section 6.
re-consent
on a ToS-change re-consent (see ToS-gated validity), the account already exists and ref applies only to initial enrollment. agents SHOULD omit ref on re-consent; services SHOULD ignore it if present.
access token
self-signed (stateless)
when the server returns the agent's self-signed access token unchanged, no server signing key is needed. on subsequent requests, the server:
- validates the DPoP proof per RFC 9449 section 4.3
- verifies the access token signature using the JWK from the DPoP proof — the same key MUST sign both
- checks
audmatches the service's origin - checks
cnf.jktmatches the JWK Thumbprint of the key in the DPoP proof - checks
tos_hashin the access token matches SHA-256 of the server's current ToS text - checks
athin the DPoP proof matches SHA-256 of the access token string - processes the request
no stored state is required beyond the current ToS text. services MAY store additional account data (profiles, service-specific state) while still using stateless authentication.
server-issued (stateful)
a server MAY replace the agent's self-signed token with its own at registration time. server-issued tokens follow standard RFC 9449 token binding — the token contains a cnf.jkt claim (JWK SHA-256 Thumbprint) binding it to the agent's key.
ToS-gated validity
the tos_hash claim in the access token creates an automatic re-consent mechanism:
- on each request, the server computes SHA-256 of the current ToS text and compares it to
tos_hashin the access token - if they match: the token is valid — the agent consented to the current terms
- if they don't match: the ToS has changed — the server rejects the request:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{"error": "tos_changed"}
the agent re-consents by repeating the enrollment flow from step 3 (terms retrieval). if the agent's key is already registered, the signup endpoint treats the request as a re-consent — validate the new ToS signature and return a new access token without creating a duplicate account.
no token revocation infrastructure is needed. updating the ToS text invalidates all existing tokens.
cryptographic requirements
| DPoP proof format | JWT with typ: dpop+jwt per RFC 9449 |
| access token format | JWT with typ: wm+jwt |
| algorithm | RS256 (RSASSA-PKCS1-v1_5 with SHA-256) |
| minimum key size | 4096 bits (RSA) |
| key encoding | JWK (in DPoP proof header) |
| JWK thumbprint | SHA-256, per RFC 7638 |
| hash encoding | base64url (no padding) |
implementation guide
for services
a minimum welcome mat implementation requires:
- a
/.well-known/welcome.mdfile — the discovery endpoint, declaring supported DPoP algorithms, minimum key sizes, endpoints, and signup requirements. - a terms document — any URL serving the ToS text, referenced from the welcome.md. a plain
GETendpoint; no authentication required. - a signup endpoint — validates the DPoP proof, verifies the ToS signature, validates the proposed access token, optionally processes the
reffield (the verbatim entry URL the agent was handed — see registration reference), and returns the approved access token.
DPoP proof validation
follow RFC 9449 section 4.3:
typisdpop+jwtalgis a supported algorithmjwkcontains a valid public key meeting size requirements- signature is valid
htmmatches the HTTP methodhtumatches the request URL (without query/fragment)iatis recent- when an access token is present:
athmatches SHA-256 of the access token string
JWK thumbprint
the JWK thumbprint (RFC 7638) is used as the canonical account identifier. for RSA keys, the canonical JSON is {"e":"...","kty":"RSA","n":"..."} (alphabetical order, no whitespace), then SHA-256 and base64url-encode.
re-consent
when the ToS text changes, all existing access tokens become invalid (their tos_hash won't match). the signup endpoint SHOULD accept re-registration from agents whose keys are already known — validate the new ToS signature and return a new access token without creating a duplicate account.
for agents
to sign up for a service with a welcome mat:
- fetch
/.well-known/welcome.md— read requirements, endpoints, and signup fields - generate a keypair matching the service's algorithm requirements
GETthe terms URL from the welcome.md — read the ToS text- sign the ToS text with your private key (UTF-8 bytes, same JWA algorithm as your DPoP proofs)
- generate a self-signed access token JWT:
typ=wm+jwt,tos_hash= base64url(SHA-256(ToS text)),aud= service origin,cnf.jkt= your JWK Thumbprint, plusjtiandiat POSTto the signup endpoint with DPoP proof,tos_signature,access_token,ref(your verbatim entry URL, if any — see registration reference), and any service-specific fields from the welcome.md- store the returned
access_token— use it in theAuthorization: DPoP <token>header on all subsequent requests - on 401 with
"error": "tos_changed", re-consent by repeating steps 3–7
security considerations
- trust on first use (TOFU). the welcome mat follows the TOFU model — the same trust pattern behind SSH host key verification. both sides accept the other's identity on first contact: the agent trusts the service's welcome.md, and the service trusts the agent's self-generated key. after enrollment, identity is verified cryptographically on every request. this eliminates the need for certificate authorities or pre-shared credentials, at the cost of vulnerability during the initial exchange. see also RFC 7435 (Opportunistic Security).
- TLS required. all endpoints MUST be served over HTTPS.
- no private key transmission. private keys never leave the agent. only public keys (in DPoP proof JWK headers) and signatures are transmitted.
- DPoP replay protection. DPoP proofs are bound to a specific HTTP method and URL. see RFC 9449 for replay mitigation strategies including
jtitracking and server-provided nonces. - self-signed access tokens. self-signed tokens allow agents to mint new tokens without going through the signup flow. this is a property of the self-signed model — the access token is a capability assertion verified by DPoP proof-of-possession, not a server-issued credential. services requiring enforceable re-consent or individual token revocation SHOULD issue server-signed tokens.
- key reuse and correlation. agents that reuse keys across services can be correlated via JWK Thumbprint. agents needing unlinkability should use unique keys per service.
refis a bearer reference. the entry URL is held by whoever was given it; single-use, expiration, and allowlist enforcement are service responsibilities.refis not signed by the DPoP proof; services needing tamper-evidence MAY bind a derived value into a server-issued access token claim.
future extensions
these are acknowledged directions for future versions of the spec.
- key rotation — a mechanism for agents to rotate keys while maintaining account continuity.
- delegation chains — a way for agents to declare who they're acting on behalf of (e.g., a principal agent delegating to sub-agents).
- IANA registration — formal registration of
/.well-known/welcome.mdper RFC 8615. - protocol bridging — welcome mat enrollment as the identity foundation for non-HTTP protocols, with
cnf.jktas the cross-protocol continuity mechanism. see guides/non-http-protocols.md for the current pattern.