the spec

version 1.1 (draft) · canonical source

for humans: this spec is a minimum profile of DPoP (RFC 9449) optimized for agents to read and implement autonomously. for the full protocol details, refer to the RFC itself.

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).

why RSA-4096? generating an RSA-4096 keypair takes real compute (~0.5-2 seconds). this creates a natural proof-of-work that makes mass account creation expensive without requiring rate limiting infrastructure. verification is fast — the asymmetry benefits the service.

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:

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:

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:

  1. validates the DPoP proof per RFC 9449 section 4.3
  2. verifies the access token signature using the JWK from the DPoP proof — the same key MUST sign both
  3. checks aud matches the service's origin
  4. checks cnf.jkt matches the JWK Thumbprint of the key in the DPoP proof
  5. checks tos_hash in the access token matches SHA-256 of the server's current ToS text
  6. checks ath in the DPoP proof matches SHA-256 of the access token string
  7. 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:

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:

  1. a /.well-known/welcome.md file — the discovery endpoint, declaring supported DPoP algorithms, minimum key sizes, endpoints, and signup requirements.
  2. a terms document — any URL serving the ToS text, referenced from the welcome.md. a plain GET endpoint; no authentication required.
  3. a signup endpoint — validates the DPoP proof, verifies the ToS signature, validates the proposed access token, optionally processes the ref field (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:

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:

  1. fetch /.well-known/welcome.md — read requirements, endpoints, and signup fields
  2. generate a keypair matching the service's algorithm requirements
  3. GET the terms URL from the welcome.md — read the ToS text
  4. sign the ToS text with your private key (UTF-8 bytes, same JWA algorithm as your DPoP proofs)
  5. 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, plus jti and iat
  6. POST to 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
  7. store the returned access_token — use it in the Authorization: DPoP <token> header on all subsequent requests
  8. on 401 with "error": "tos_changed", re-consent by repeating steps 3–7
key = identity. there is no key rotation mechanism in this version of the spec. if you lose your private key, you lose access to the account. agents SHOULD store keys securely.

security considerations

future extensions

these are acknowledged directions for future versions of the spec.