Skip to main content
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.
Turning two-factor authentication on is worth it here. It adds the confirmation step to this screen and to your sign-ins, so a terminal cannot be authorized from a session someone walked up to on an unlocked machine — which is the attack this step exists for, and the one a re-entered code actually stops. Turn it on under Settings → Profile → Two-factor authentication. It is not required to use the CLI: the same account can already apply enforcement policy from the dashboard without one, and this path is not the place to invent a stricter rule.
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.
Self-hosters: the IP row is only as trustworthy as your proxy. Zespan resolves the address from X-Forwarded-For, so an edge that appends to a client-supplied value — or an API reachable without going through the edge — lets a caller choose the address a human is shown here. On the row that exists precisely to be corroborated by eye, that is worth an hour of your time. See Client IP behind a proxy.

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 around zespan 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.
Self-hosters: both depend on infrastructure that is easy to leave out. The rate limit needs REDIS_URL configured on the API — without Redis it does not apply at all. The sweep runs in the worker process, and only when WORKER_TYPE is unset or all; if you split workers by type and never run a general one, nothing consumes the sweep queue and expired grants accumulate indefinitely. See Environment variables.

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

On a machine with no browser — an SSH session, a container, a headless build host — pass --no-browser. The CLI prints the URL and the code instead of trying to open anything, and you approve them from any other machine:
Without --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

Prints the resolved identity: the account, the host, where the credential lives, its expiry if the server reported one, the organizations you reach with your role in each, and the project linked in this directory. It still answers when the network is down — the account details fall back to the copy cached at login, and the output says so rather than silently showing stale data as if it were live.

zespan auth logout

Removes ~/.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 from NEXT_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):
One more thing to check before you hand this to a team: the approval screen shows the IP the request came from, and that row means what it says only if your proxy overwrites X-Forwarded-For. See Client IP behind a proxy.

When a login fails

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 grant lives for 10 minutes. Run zespan auth login again for a fresh code.
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.
The stored token was rejected. Run zespan auth login to sign in again from this machine.
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.