Five subcommands and one top-level alias, covering the sessions the CLI holds and the GitLab token your site checkout depends on. This page is flags and outputs; for what a session actually is, where it is stored and how it stays alive, read CLI authentication first.
leed auth login
leed auth login
leed auth login --env staging
leed auth login --company <companyId>
leed auth login --email you@example.com| Flag | Type | Default | Required | Hidden from --help? | What it does |
|---|---|---|---|---|---|
-e, --env <name> | string | the site checkout’s environment, else ENV_NAME | Outside a checkout, unless --company is logged in to exactly one environment | Yes | Which environment to sign in to: production, staging or dev |
--company <company> | string | the site checkout’s company | No | No | Which company to bind the session to, by companyId or by the domain of a company you already hold a session for. Without it and outside a checkout, the approval page lets you pick |
--browser | boolean | true | No | No | Use the browser device-authorization flow. This is the default |
--email [email] | string | — | No | No | Switch to magic-link sign-in. Omit the address to be asked for it |
The default flow is the browser device authorization. The CLI prints a verification code and the approval URL, opens your browser at the approval screen where the machine has a display, and polls until you confirm:
Environment: staging
Your verification code is: AB12-CD34
Approve it here: https://app.leed.ai/…
Opening browser for authorization...
Waiting for authorization...
Authenticated as Ada Lovelace (you@example.com) on staging
Company: example.com (<companyId>)
Credentials saved to /home/you/.config/leed/auth.jsonEverything down to “Waiting for authorization…” is narration on stderr; the closing lines are the command’s result on stdout. Credentials saved to … appears only for a non-production environment, where the path is worth knowing. The approval URL is always printed, before any attempt to open a browser; on a machine with no display — a remote shell, a container, a headless Linux box — no browser is tried and “Opening browser…” does not appear, so open the URL anywhere you are already signed in.
The browser screen you approve on is documented from the CMS side in authorizing devices and clients.
The magic-link mode does not sign you in
--email switches to a magic-link send, and that is all it does:
Check your email and click the sign-in link.
After clicking the link, run: leed auth login --browser --env productionNo session is stored. The email gets you signed in to the CMS in a browser; the CLI still needs the device flow afterwards to obtain its own session. This is the single most common misunderstanding of this command — plan on running leed auth login twice.
--email takes an optional value, so a bare --email prompts for the address rather than failing. With nobody at the terminal it refuses instead of blocking on a stdin that will never answer.
leed login
leed loginThe top-level alias for leed auth login. Identical flags, identical behavior, identical output. The only difference is the name it reports in its --json document: login rather than auth.login, so a consumer can match the command line it actually ran.
leed auth logout
leed auth logout
leed auth logout --env staging
leed auth logout --company example.com
leed auth logout --all-companies| Flag | Type | Default | Required | Hidden from --help? | What it does |
|---|---|---|---|---|---|
-e, --env <name> | string | the site checkout’s environment, else ENV_NAME | Outside a checkout, unless --company is logged in to exactly one environment | Yes | Which environment to sign out of |
--company <company> | string | the site checkout’s company | Outside a checkout, whenever company sessions are stored | No | Which stored company to sign out of, by companyId or domain |
--all-companies | boolean | off | No | No | Sign out of every company stored for the environment |
It asks the CMS to revoke the session, then deletes the stored session for that company. A revoke the CMS receives ends the session at once, together with any token it replaced in the last minute. The revoke is best effort and the deletion is not: a laptop that cannot reach the CMS still has to be able to drop its cached token, so a refusal or a network failure is reported as a warning and the session goes anyway. When that happens the session may remain live on the server until it expires — up to 90 days after it was last renewed — and re-running the command from a connected machine will still revoke it. The company’s raw-content token is kept — other checkouts, CI runners and teammates share it, and only leed auth reset discards it.
With nothing stored it prints Not logged in to <env>. and reports success — the goal, no session on this machine, already held. Otherwise it prints Logged out of <env> (<company>).
It ends only that company’s session in that environment, unless you pass --all-companies. A machine signed in to both production and staging stays signed in to the other one.
leed auth status
leed auth statusNo flags of its own. What it reports depends on where you run it.
- Anywhere else
- Inside a site checkout
Credentials: /home/you/.config/leed/auth.json
dev environment:
✗ example.com (<companyId>): you@example.com (expired Aug 3, 2026)
staging environment:
✓ example.com (<companyId>): you@example.com (expires Sep 9, 2026)
✓ other.example (<companyId>): you@example.com (expires Sep 8, 2026)Every session this machine holds, grouped by environment, one row per company. A session from an older CLI whose company could not be resolved shows as (unassigned legacy session). The Credentials: path line appears only when a non-production environment is in play — that is exactly when knowing which file to look in matters.
With nothing stored at all, the whole output is:
Not authenticated. Run: leed auth login Credentials: /home/you/.config/leed/auth.json
staging environment:
✓ example.com (<companyId>): you@example.com (expires Sep 9, 2026)
GitLab tokens for example.com (staging):
⚠ raw-content: expires in 4 days (Sep 6, 2026)The extra row is read live from the CMS, with a five-second timeout, and only appears here — the command has to be inside a site checkout to know which company to ask about. It is the token that lets git talk to your raw-content repository, and it is the reason “logged in” and “can git fetch” are different states.
Inside a site directory, status first renews that site’s session through the same path every other command uses, so its row shows the expiry the CMS has just set — about 90 days out for a session in use. That renewal needs the CMS: when it cannot be reached, expect a wait of about ten seconds before anything prints.
Symbols mean the same thing in both places, except that session rows never show a warning — a session is either live or it is not.
| Symbol | Meaning | Threshold |
|---|---|---|
✓ | Valid | More than 7 days remaining |
⚠ | Expiring soon — rotate | 7 days or fewer remaining (the GitLab token row only) |
✗ | Expired and already failing | Past its expiry |
? | Unknown | No expiry recorded, or one this CLI cannot parse |
The token lookup can end four other ways, each replacing the token row with a single line:
| Line | What happened | What to run |
|---|---|---|
Session expired. Run: leed auth login --env <env> | The CMS rejected the stored session (401) | leed auth login --env <env> |
No developer access for this organization | The account is signed in but not permitted (403) | Ask an administrator — see billing and developer access |
Could not reach <cms url> (<detail>) | The CMS did not answer | Check connectivity, then retry |
(no tokens stored yet — run 'leed auth rotate' or rerun the installer) | Neither token has ever been issued | leed auth rotate |
A separate line, GitLab tokens for <domain> (<env>): not authenticated for this env, means the site targets an environment you hold no session for — sign in to that environment before the tokens can even be asked about.
Where leed auth default went
It is gone, together with the default company it set. The CLI no longer picks a company or an environment you did not name: inside a site checkout the site’s own company is used, and outside one you pass --company — and -e when that company is logged in to more than one environment. A command that cannot tell which company you mean refuses with “No company selected for <env>.”, lists the companies you are logged in to, and hints the command with --company <companyId> added. A default recorded in auth.json by an older CLI is read and ignored.
leed auth rotate
leed auth rotate
leed auth rotate --yes| Flag | Type | Default | Required | Hidden from --help? | What it does |
|---|---|---|---|---|---|
-y, --yes | boolean | false | No | No | Confirm without being asked. Required for any unattended run — under --json, or with no interactive terminal |
Reissues this site’s GitLab raw-content token and stores it in ~/.config/leed/auth.json, filed under the site’s company. The checkout’s origin URL is reconciled to the credential-free remote; the token is never written into it. Your CMS login session is not revoked — you stay signed in. Like every command that calls the CMS, rotate renews the session on the way in, so a session past half-life comes out of it with a fresh 90-day token.
It must be run from inside a site checkout. Anywhere else it stops with “This command must be run from inside a Leed site checkout.” and the hint to cd into the folder holding identity.json — leed site list shows where each of your sites is.
Because the previous token stops working the moment the new one is issued, it asks first:
Rotate this site's GitLab token? The current token stops working, and ~/.config/leed/auth.json is rewritten.A successful run prints a short summary:
✓ GitLab credentials rotated
Git repo: /home/you/leed/example-com/raw-content
Expires: 11/30/2026
Your CMS login session was not touched. No re-authentication needed.Two failures are worth recognizing. A 401 is “Session expired or invalid. Run: leed auth login --env …”. A 403 is “You don’t have developer access to this organization.” — you are signed in and not permitted, so re-authenticating will not help; an administrator has to grant the developer override. And if the token was issued but could not be stored, or the checkout’s remote could not be reconciled, the command fails with external_command_failed and points at re-running leed auth rotate once the file can be written; if no session is stored for the site’s company, it tells you to run leed auth login for that company and then leed auth rotate.
The raw-content token is stored per company, so one rotation covers every checkout of that company on this machine. If you develop for several companies, run it in a site of each.
leed auth reset
leed auth reset
leed auth reset --all-sessions
leed auth reset --all-companies| Flag | Type | Default | Required | Hidden from --help? | What it does |
|---|---|---|---|---|---|
--all-sessions | boolean | off | No | No | Also revoke your sessions on other devices, rather than this one alone |
--all-companies | boolean | off | No | No | Also sign out of every other company stored for this environment. Their raw-content tokens are kept, because only this site’s company was rotated |
-y, --yes | boolean | false | No | No | Confirm without being asked. Required for any unattended run |
The order of operations is fixed, and it explains the failure modes:
- Confirm — before the site context, the credentials or the network, so a refusal leaves everything exactly as it found it.
- Rotate the GitLab token, while the session is still valid. Revoking first would make this step fail with a 401.
- Reconcile the checkout — point the raw-content checkout’s
originat the credential-free remote. The rotated raw-content token is deliberately not stored: it would be filed in the very record the next step deletes. - Revoke and sign out — the other devices’ sessions when
--all-sessionswas given, then this one, then every other company’s with--all-companies— and delete the local credentials for this site’s company regardless of whether the server was reachable.
A remote that could not be reconciled in step 3 does not abort step 4. You asked for your credentials to stop working, and that has to happen even when a local file could not be updated. The run still succeeds, with a warning on stderr. Either way the site is left with no raw-content token, so the recovery is always the same two steps: leed auth login, then leed auth rotate.
✓ Credentials reset
Git repo: /home/you/leed/example-com/raw-content
CMS login: revoked (all devices)
You will need to re-authenticate. Run: leed auth login --env staging
Then run 'leed auth rotate' to issue and store this site's content-repository token.Revoking other sessions is the same operation the CMS offers on sessions, connected accounts and API tokens.
Choosing between rotate and reset
leed auth rotate | leed auth reset | |
|---|---|---|
GitLab raw-content token (auth.json) | Reissued and stored | Reissued, not stored — run login then rotate |
| CMS login session | Untouched | Revoked |
| Sessions on other devices | Untouched | Revoked with --all-sessions |
| Other companies’ sessions | Untouched | Revoked with --all-companies |
| Local credentials | Untouched | Deleted for this site’s company |
| Re-login required afterwards | No | Yes |
Rotate for hygiene, for a token leed auth status shows expiring, and when git fetch or leed site push starts failing with an authorization error. Reset for a lost or compromised machine, or a credential you believe has leaked.
JSON output
The five commands report as auth.login, auth.logout, auth.status, auth.rotate and auth.reset; the alias reports as login. Envelope, warnings and the full code list are on machine-readable output.
{
"environment": "staging",
"customer": "example-com",
"rawContentRepo": "/home/you/leed/example-com/raw-content",
"tokensExpireAt": "2026-11-30T00:00:00.000Z",
"tokensExpireAtLabel": "11/30/2026",
"cmsSessionPreserved": true
}These commands can produce not_authenticated, auth_expired, permission_denied, not_a_site_repo, invalid_arguments, network_error and external_command_failed. invalid_arguments is the refusal for an environment or company nobody named, and its hint is the same command with -e <env> or --company <companyId> added. login refuses under --json with interactive_input_required; rotate and reset refuse with confirmation_required unless --yes is given. auth status never fails for a state it can describe — a rejected session, a missing developer override and an unreachable CMS are all successful status reports carrying the reason.
Notes worth carrying
The credentials file holds a refreshToken field that is always empty and reserved. It is not a working refresh token; the CLI refreshes by round-tripping its access token instead.
The raw-content token no longer lives in the checkout. It is stored in ~/.config/leed/auth.json, handed to git per invocation, and served to the git you run yourself by the leed __credential helper — so copying a site folder no longer copies a live credential. A checkout cloned by an older CLI, with the token embedded in its origin URL, is migrated on the next leed site command.
leed auth is one of the CLI’s command groups; the whole tree with its global options is at CLI command reference. The file these commands rewrite is cataloged on files and environment, and a token that has quietly expired shows up first as an authentication failure in the git workflow.