vendo.tokenFor() spends it for
you — this page is what is behind that call.
Where the key lives
On a local door (no key). The key is any opaque string you generate. It lives in your own environment asVENDO_SERVICE_KEY, the composition lists it,
and vendo init --service-key writes both for you — this is what it generates:
lib/vendo.ts
tokenFor is the only place your own code touches
either.
Who the audit says acted
The exchange binds two identities to every call the token makes: the person the token names, and the key that asked. The key rides as a client id of the formsvc:<hash8>, the first eight hex of
the presented key’s digest. The audit row names which key acted without the key
value going anywhere near it.
Your policy sees the same person your own UI sees. A write still parks for
approval, and the approval names the tool and the user, not the agent.
Rotating a key
1
Create the replacement
A new key alongside the one you are retiring — a second entry in
serviceAuth.keys on a local door, or a new key in the console on Cloud.2
Ship it
Update
VENDO_SERVICE_KEY where your backend runs and deploy.3
Retire the old one
Drop it from the list, or delete it in the console. Tokens it already minted
keep working until they expire, which is at most ten minutes.
When the mint refuses
The first row is deliberately one answer for two causes. Anything narrower would
tell whoever is guessing which half of the credential they got right.
The
401 is the one that looks like a key problem and is not. The exchange
never checks the user id, so a token for a stranger mints fine and dies at
principal() on the first real request.
What the broker owns
A broker is an authorization server in front of your door. On Cloud, yourVENDO_API_KEY is the whole configuration — the runtime reads the broker URL
and the federation secret from Cloud and provisions the tenant on first use.
To point at a broker you run yourself, set both values where you deploy. An
explicit override always wins over the Cloud default.
.env
VENDO_MCP_BROKER_URL decides two things at once. Its origin becomes the issuer
your door trusts, and the URL itself becomes the audience every token must
carry. The door then stops serving /authorize, /token and /register, and
verifies each inbound bearer as a JWT the broker signed. Keys come from the
issuer’s own metadata, so ordinary rotation at the broker needs nothing from
you.
VENDO_MCP_FEDERATION_SECRET answers the broker’s login handshake. The broker
owns the sign-in surface and your app owns the users, and that shared secret is
how the two agree on an answer.
VENDO_BASE_URL has to name an HTTPS origin either way — Vendo Cloud refuses to
register a forwarding address that is not https, and init refuses that pair
before it writes.
Who has to reach that origin depends on the path. Signing a person in is a
browser redirect: the broker sends the user’s own browser to your door’s
/federate, and the door reads their session cookie there, so the door has to
be reachable by whoever signs in — public for a public agent’s users. The
backend-agent path reaches nothing inward: tokenFor calls the broker outbound
from your own backend, so a private HTTPS origin your backend and agent can
already reach is enough for it. Everything else about the door is unchanged by
the posture, the client URL included — see the
quickstart for both postures end to end.