zespan auth gives the CLI a credential that belongs to you rather than to a
project. Control-plane commands — zespan policy pull, test, plan, apply,
zespan link, zespan projects list — check your role in the organization, so
they need a person’s session. A project API key cannot supply one.
Ingest and the data plane are unchanged.
/v1/ingest, guardrail checks,
the SDKs, and zespan doctor still authenticate with
ZESPAN_API_KEY. Signing in adds a second credential; it does not replace the
first. See the two credentials.Quick start
The CLI prints the second-factor line to everyone, because it cannot know which
account will approve. If your account has no second factor configured, the
approval screen simply asks you to confirm instead — see Approving a
terminal.
How the login works
zespan auth login is an OAuth 2.0 device authorization grant
(RFC 8628) — the same shape as
signing a TV into a streaming service. The terminal never sees your password.
1
The CLI asks for a code
It requests a short user code from the API, sending this machine’s public key
and — when it can determine them — its operating system, OS version, and CLI
version.
2
You approve in the browser
The CLI prints the code and opens the dashboard’s
/device screen with the
code pre-filled. The code is valid for 10 minutes.3
You confirm the request
Check what the screen says about the terminal that asked, then authorize it.
If your account has a second factor, you re-enter it here even though you are
already signed in. See Approving a terminal.
4
The CLI receives a token
It polls every 5 seconds until you approve, then writes the token to
~/.zespan/credentials.json and caches who you turned out to be so later
commands can name you without a network call.Approving a terminal
What the approval screen asks of you depends on whether your account has two-factor authentication turned on.
The requirement on an enrolled account is enforced by the API as well as by the
page, so a request that skips the screen entirely is refused too. The proof is
bound to the exact browser session that supplied it and is good for five
minutes, so verifying in one browser cannot authorize a terminal from another.
A device grant is phishable by construction: someone can start
zespan auth login on their own machine and read the eight-character code to you
over the phone. Two things on the approval screen exist to make that attempt
fail.
The terminal that asked
On both approval paths, before you authorize, the screen shows what the server observed about the machine that made the request:
None of it comes from the link you followed, and none of it is editable. If it
does not describe the machine in front of you, cancel. An older CLI that reported
nothing about itself says exactly that, rather than showing a blank.
The warning
The screen states, before you authorize, that the code should already be on screen in a terminal in front of you — and that if someone read it to you, you should cancel.Machines are remembered
The first login on a machine mints an Ed25519 keypair at~/.zespan/device-key.pem
(with the public half beside it) and sends the public key with the grant. The API
fingerprints it and records the machine, so signing in again from the same
machine keeps the same device identity rather than registering a new one
every time.
That identity is what makes the audit trail readable: a policy apply records the
user, the session, and the device it came from, not just a name. See
Attribution.
zespan auth logout removes the credential but keeps the device key, so
signing back in reuses the same device.
Abandoned logins are cleaned up
A login you start and never finish leaves a pending grant on the server, holding the code, the machine’s public key, and the address the request came from. Two behaviours bound that, and both are worth knowing before you script anything aroundzespan auth login.
Starting a login is rate-limited. Requesting a code is capped at 30 per 5
minutes per IP address. That is generous enough for a whole team behind one
office address onboarding together, and it applies only to starting a login —
the CLI’s polling for the result is deliberately not counted, since one login
polls roughly 120 times.
Pending grants are swept. A code is valid for 10 minutes; a grant that has
expired is deleted 15 minutes later by a sweep that runs every 15 minutes,
so nothing survives beyond roughly 40 minutes from the moment it was requested.
The grace period is not an oversight — it is what lets a slow client be told its
code expired rather than getting a bare error.
Files on disk
The credential and key files are written with owner-only permissions (
0600)
and re-tightened on every write, so an older CLI that left the file world-readable
is fixed the next time you sign in. Windows has no POSIX mode to set; there the
files rely on your user profile’s permissions.
A token is tied to the host it was minted against. If you sign in to
https://api.zespan.com and then run a command with --api-url pointing
somewhere else, the CLI refuses rather than sending your session token to a host
it was not issued for.Commands
zespan auth login
--no-browser. The CLI prints the URL and the code instead of trying
to open anything, and you approve them from any other machine:
--no-browser, a login started where there is no terminal to open a
browser from is refused immediately, before a code is minted — rather than
hanging for ten minutes on a code nobody can see.
zespan auth status
zespan auth logout
~/.zespan/credentials.json from this machine. The device key stays, so
the next login is recognised as the same machine. Running it when nothing is
stored says so rather than pretending to have done something.
Self-hosted deployments
The API tells the CLI where to send you by building the approval URL fromNEXT_PUBLIC_APP_URL — the same variable that already builds invitation and
two-factor links. Set it to your dashboard’s public origin; see Environment
variables. The approval screen is the web app’s
/device route.
If you need to override it for a single login — a tunnel, a preview deployment —
pass --app-url (or set ZESPAN_APP_URL):
X-Forwarded-For. See Client IP behind a
proxy.
When a login fails
The login was denied in the browser
The login was denied in the browser
Someone pressed Cancel this request. Nothing was granted. If it was not
you, that is the control working — do not approve a code you did not start.
The login code expired before it was approved
The login code expired before it was approved
The grant lives for 10 minutes. Run
zespan auth login again for a fresh
code.This device was revoked, so it can no longer sign in
This device was revoked, so it can no longer sign in
A revoked machine cannot re-register itself by signing in again. Remove the
revocation, or delete
~/.zespan/device-key.pem to sign in as a new device —
the CLI prints the exact path.Your saved sign-in is no longer valid
Your saved sign-in is no longer valid
The stored token was rejected. Run
zespan auth login to sign in again from
this machine.Signed in, but could not read your account details just now
Signed in, but could not read your account details just now
The token was minted and written — the login succeeded. Only the follow-up
read of your account details did not answer. Run
zespan auth status once the
network is back.Next steps
zespan link
Choose the project this directory belongs to, and list what you can reach.
zespan policy
The commands that need this sign-in, and what an apply records about you.
CLI overview
The two credentials, configuration precedence, and install options.
API keys
The project key the SDKs and
zespan doctor still use.Client IP behind a proxy
What self-hosters must configure so the approval screen’s IP row is real.

