Style guide

How the site looks, and why

Every swatch, specimen and component below is the real thing, rendered from the same tokens the site uses. Use the theme button in the header to see it in light and dark.

Color

Values shown are read from this page as rendered, in the current theme.

Surfaces

--bg

The page.

--bg-raised

Cards, panels and code blocks: anything that sits on the page.

--bg-sunken

Wells, hovered rows, the footer: anything set into the page.

--bg-inset

The deepest step. Rare.

Text and lines

--fg

Body text and headings.

--fg-muted

Ledes, captions, secondary text.

--fg-subtle

The lightest text allowed. Counts, placeholders, fine print.

--border

Hairlines between rows and around cards.

--border-strong

Control outlines and rules that need to be seen.

Accent

The accent marks interaction: something you can follow, press, or are currently on. It is separate from the five hues so that a link never reads as a role.

--accent

Links, focus rings, the primary button, the current item.

--accent-soft

The highlight behind a targeted entry.

--on-accent

Text on an accent fill.

The five hues

Every diagram on the site assigns one hue to each kind of principal, and keeps that assignment everywhere. Each hue has a solid value for text and outlines and a soft value for fills.

Human--red--red-soft

Also: danger, “no,” cautions.

Agent / client--blue--blue-soft

Also: working group drafts.

Authorization--violet--violet-soft

Also: projects and platforms.

Resource--green--green-soft

Also: success, “yes,” published standards.

Infrastructure--amber--amber-soft

Also: “partly,” individual drafts, out-of-band messages.

Contrast

Each cell is measured live. Text needs 4.5 to 1 to pass WCAG AA.

Text tokenon --bgon --bg-raisedon --bg-sunkenon its own soft fill
--fgAaAaAa–
--fg-mutedAaAaAa–
--fg-subtleAaAaAa–
--accentAaAaAaAa
--redAaAaAaAa
--blueAaAaAaAa
--violetAaAaAaAa
--greenAaAaAaAa
--amberAaAaAaAa
--on-accentAa on --accent

The same pairs are checked in both themes by npm run check:contrast, which fails if any text pair drops below 4.5 to 1.

Type

Four roles, filled by two families.

Display--font-display

Who’s on first?

Body--font-body

Exchange, never forward.

Label--font-label

HUMAN PRESENT · 14 STEPS

Mono--font-mono

grant_type=client_credentials

Scale

h1

Who is acting?

h2

Delegation or impersonation

h3

The agent redeems the code directly

h4

Token Exchange

lede

A field guide to every way an AI agent can prove what it is and what it may do.

body

A service that needs to call another gets a new token for that hop. Passing along the one it received is how a token meant for one place ends up everywhere.

label

Flows / Human present

code

aud, act.sub, cnf.jkt

Shape

Three radii, and three widths that every page shares.

--radius

Cards, buttons, code blocks.

--radius-pill

Chips and segmented choices.

--radius-round

Step numbers and markers.

TokenValueUse
--wide72remThe widest a page gets. Diagrams and grids use all of it.
--prose44remRunning text. About 70 characters a line.
--gutter1rem to 2.5remSide padding, growing with the viewport.

Components

The building blocks, with what each is for.

Buttons

One primary action per view. Everything else is a plain button, and tertiary actions are ghosts. Labels say what happens: “Explore the flows,” never “Click here.”

Chips

Short facts about the thing they sit on. A neutral chip states a property. A toned chip states a category, and takes its hue from the table above, so the same color always means the same thing.

14 stepsHuman presentHuman reachableNo human
RFCFinal specWG draftIndividual draftProject / platformGuidance

Callouts

For the one thing on a page a reader must not miss. If a page has three, none of them is a callout. Red is for a risk, amber for a caveat.

Reading status correctly

A working group draft has been adopted, and can still change in incompatible ways.

The trap in the middle

An unattended agent holding a person’s long-lived token has a human’s authority and no human watching.

Roles

Five kinds of participant appear in every diagram. The class role-human,role-agent and so on sets --role and --role-soft for anything inside it.

HumanAgent / clientAuthorizationResourceInfrastructure

Tables

For comparing the same facts across several things. Column heads are labels; the first column names the row. Wide tables scroll inside their own frame, never the page.

FlowHumanToken subjectMaturity
Authorization code + PKCEpresentUserStable standards
Backchannel approval (CIBA)reachableUser, for one actionStable standards
Workload identity federationabsentAgent, as a workloadStable standards

Code

Inline code names a real identifier: a claim, a parameter, a header. Blocks show what is on the wire. Examples are abbreviated for reading and say so.

The resource parameter asks for a token that is valid for one API only, and the API checksaud.

POST /token HTTP/1.1
Host: auth.example
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&resource=https://erp.example/api

Comparison

Yes, no and partly are stated in words as well as color, so the answer survives a grayscale print and a color-blind reader.

How is authority passed down the chain?
What the downstream API can tell
Who is the user?No
What software is calling?Partly
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?No
Can one user or one agent be cut off?No

The API sees one caller: whoever holds the key. Every user and every agent run looks the same, the key works from anywhere, and revoking it cuts off everyone at once. Still the most common setup in the wild.

What the downstream API can tell
Who is the user?Yes
What software is calling?No
Was this credential meant for me?No
Is a stolen copy useless elsewhere?No
Can one user or one agent be cut off?Partly

The user is visible, but the API is accepting a token that was issued for a different service. Every hop that touched the token can replay it wherever it is accepted. This is the confused-deputy setup, and the MCP specification prohibits it.

What the downstream API can tell
Who is the user?Yes
What software is calling?No
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?Partly
Can one user or one agent be cut off?Partly

Each hop gets a fresh token for the right audience. But the API believes the user is calling directly. Logs show Alice doing things at 3 a.m. that Alice never did, and policy cannot treat software differently from a person.

Token exchange →
What the downstream API can tell
Who is the user?Yes
What software is calling?Yes
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?Partly
Can one user or one agent be cut off?Yes

The token names both the user and the chain of software acting for them. The API can apply the user’s permissions and the agent’s limits together, and either can be revoked on its own. A stolen token is still usable until it expires, but only at this one API.

Token exchange →
What the downstream API can tell
Who is the user?Yes
What software is calling?Yes
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?Yes
Can one user or one agent be cut off?Yes

As above, and each token is bound to a key held by the caller. Copying a token out of a log or a model’s context gets an attacker nothing. This is where the standards are heading; few deployments are here yet.

Sender-constrained tokens →

Diagrams

Sequence diagrams are drawn at build time from data, so they take the tokens like everything else.

One column per participant, colored by role.

Line style is meaning. Solid for a direct call, dashed for a response, dotted for out of band, and the human hue for anything that passes through a person’s browser.

Labels stay short. Two lines at most; the panel carries the explanation.

Sequence diagram3 participants, 7 steps
  • Direct call
  • Response
  • Local action

Drag to see every party and step. Tap a step to jump to its explanation.

1Sign a client assertion2Token requestwith assertion3Verify assertion, apply policy4Access token (sub = agent)5API call with Bearer token6Validate; authorize the client7200 OK

  1. 1AgentLocal action

    The agent creates a signed proof of its identity

    Instead of sending a shared secret, the agent signs a short-lived JWT with its private key. Issuer and subject are both the client ID; the audience is the authorization server; jti makes it single-use. The private key never leaves the agent.

    {
      "iss": "invoice-agent",
      "sub": "invoice-agent",
      "aud": "https://auth.example",
      "jti": "b1f7c2a0-5d1e-4c8e-9f3a-0d6b8e1a2c44",
      "iat": 1794268800,
      "exp": 1794268860
    }

    Who holds what after this step

    Agent
    newprivate key

    Parties not listed hold none of this flow’s credentials at this point.

  2. 2Agent to Authorization serverDirect call

    The agent requests a token

    One direct call. The resource parameter asks for a token that is valid for one API only.

    POST /token HTTP/1.1
    Host: auth.example
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=client_credentials
    &scope=invoices.read invoices.reconcile
    &resource=https://erp.example/api
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion=eyJhbGciOiJFUzI1NiJ9…

    Who holds what after this step

    Agent
    private key

    Parties not listed hold none of this flow’s credentials at this point.

  3. 3Authorization serverLocal action

    The authorization server authenticates the client

    It verifies the signature against the public key registered for this client, checks audience, expiry and replay, then decides which of the requested scopes this client is allowed. There is no user to ask, so policy is the only gate.

    Who holds what after this step

    Agent
    private key

    Parties not listed hold none of this flow’s credentials at this point.

  4. 4Authorization server to AgentResponse

    The server issues an access token

    The subject of the token is the client itself. Nothing in it refers to a human.

    {
      "iss": "https://auth.example",
      "sub": "invoice-agent",
      "client_id": "invoice-agent",
      "aud": "https://erp.example/api",
      "scope": "invoices.read invoices.reconcile",
      "exp": 1794269400
    }

    Who holds what after this step

    Agent
    private keynewaccess token

    Parties not listed hold none of this flow’s credentials at this point.

  5. 5Agent to APIDirect call

    The agent calls the API

    An ordinary bearer request.

    Who holds what after this step

    Agent
    private keyaccess token

    Parties not listed hold none of this flow’s credentials at this point.

  6. 6APILocal action

    The API authorizes a service, not a person

    The API must treat this as a machine caller. Row-level rules that depend on “the current user” have nothing to bind to. If the agent is doing something for a particular user, that fact has to arrive some other way, and a request parameter the agent filled in is not evidence.

    Who holds what after this step

    Agent
    private keyaccess token

    Parties not listed hold none of this flow’s credentials at this point.

  7. 7API to AgentResponse

    The API responds

    The audit log records the agent’s client ID. If several replicas, runs or sub-agents share that ID, the log cannot tell them apart. Workload identity systems such as SPIFFE address this by giving each workload its own identity.

    Who holds what after this step

    Agent
    private keyaccess token

    Parties not listed hold none of this flow’s credentials at this point.

Annotated tokens

User access token

Authorization server → agent → API

What an agent holds after a person signs in and consents. The JWT profile (RFC 9068) is one common format; opaque tokens checked by introspection carry the same information.

{
"iss": "https://auth.example",
Bookkeeping

The authorization server that issued the token. The API must check it against the issuer it trusts; an agent that talks to many servers must check it against the one it meant to use.

"sub": "user:alice",
Who is this for?

The subject: whose authority this token carries. Here, a human. APIs apply this user’s permissions.

"client_id": "https://agent.example/client.json",
What software is acting?

The OAuth client the token was issued to. This is usually the closest thing to “which agent” in a plain access token, and it identifies an application, not an individual agent run. With Client ID Metadata Documents the value is a URL the server can fetch to learn about the client.

"aud": "https://calendar.example/api",
Where is it valid?

The audience: the one API that should accept this token. Agents talk to many servers, so audience restriction (requested with the resource parameter) is what stops a token given to one server from being replayed at another.

"scope": "calendar.read",
What may it do?

What the user consented to. Coarse by design. For anything transaction-shaped, authorization_details (RFC 9396) is more precise.

"exp": 1794269400,
For how long?

Expiry. Minutes, not days. A leaked short-lived token is a small incident.

"iat": 1794268800,
For how long?

When the token was issued.

"jti": "8f1c9a52-…"
Bookkeeping

A unique identifier for this token, used for replay detection, revocation lists and correlating audit logs.

}

Delegated token

Authorization server → tool → downstream API

The result of a token exchange. The user is still the subject; the act claim names who is acting for them. Nested act claims record the chain.

{
"iss": "https://auth.example",
Bookkeeping

Issuer.

"sub": "user:alice",
Who is this for?

Still Alice. Delegation keeps the original subject so that downstream permissions are hers.

"aud": "https://warehouse.example/api",
Where is it valid?

A new audience. The token the tool received was addressed to the tool; this one is addressed to the next hop.

"scope": "sales.read",
What may it do?

Narrower than the original grant. Each exchange is a chance to drop privileges the next hop does not need.

"exp": 1794269100,
For how long?

Shorter-lived than the token it was exchanged for.

"act": {
What software is acting?

The actor claim from RFC 8693. Its presence is the difference between delegation (“the tool, acting for Alice”) and impersonation (“Alice”). Without it the API cannot tell software from the person.

"sub": "https://tools.example/mcp",
What software is acting?

The current actor: the party presenting this token.

"act": { "sub": "research-agent" }
What software is acting?

A prior actor, nested. RFC 8693 says only the outermost actor is relevant to access control; nested actors are history, useful for audit. Several current drafts would make this chain richer for agents.

}
}
  • Who is this for?
  • What software is acting?
  • Where is it valid?
  • What may it do?
  • How is possession proven?
  • For how long?

Voice

How the site talks.

Say who does what

“The agent redeems the code.” Not “the code is redeemed.” Auth is about who acts; passive sentences hide exactly that.

State it, then stop

One clear sentence beats a hedge. Where something is uncertain, say what is uncertain and why, once, in the place it applies.

Name a thing once

Pick the term, define it in the glossary, and keep using it. A “grant” in one paragraph is not a “permission” in the next.

Status is a fact

“Working group draft, revision 11” is a status. “Emerging standard” is an opinion. Report what a source says and link to it.

The joke is the frame, not the filling

Who, What and I Don’t Know organize the ideas. They do not appear in every heading. A reference page earns trust by being plain.

Cautions come with a way out

Every “watch out” says what to do instead. A warning with no alternative is just worry.