Authentication
Sign in with the CLI.
Authentication is optional. deessejs init <slug> works without an account; the CLI clones a public GitHub repo and exits. You only need auth login if a command hits the DeesseJS API on your behalf (publishing templates, fetching private registry data, leaving reviews, anything not yet public).
The CLI signs in via OAuth 2.0 device authorization grant. There's no password prompt, no copy-paste of API tokens, no headless browser.
deessejs auth login
deessejs auth loginWhat happens:
- The CLI requests a device code from
POST /api/v1/auth/device/codeon the DeesseJS server. The response carries a short user code and a long device code, plus a verification URL. - The CLI spawns your default browser at the verification URL:
https://app.deessejs.com/device?user_code=.... - The CLI polls
POST /api/v1/auth/device/tokenuntil you approve (or deny) in the browser. - On approval, the CLI exchanges the device code for an access token, validates it server-side, and writes
~/.deessejs/auth.jsonwith mode0600. - The CLI echoes the identity you signed in with. The access token itself never prints.
If you aren't signed in to app.deessejs.com when you land on /device, you'll redirect through the normal login flow and bounce back to /device?redirect=... once authenticated.
Flags
| Flag | Effect |
|---|---|
--json | Machine-readable output ({ ok: true, user } on success, { ok: false, code, message, hint } on failure). |
Exit codes
0: successful login.1: generic failure, network error, or server error (network_error).1: device code expired (cli_device_expired).1: you denied the request in the browser (cli_device_denied).
deessejs auth status
deessejs auth statusReads the cached session and validates it against GET /api/v1/auth/get-session with a Bearer header. Three terminal states:
| State | Output | Exit code |
|---|---|---|
No ~/.deessejs/auth.json | Yellow notice: "no active session. Run deesse auth login to authorise this machine." | 0 |
| Token still valid | Green ✓ + identity + fetchedAt timestamp | 0 |
| Token revoked or expired server-side | Yellow notice + the stale identity for context | 0 |
The exit code always reads 0; auth status is a read-only diagnostic and never modifies state.
The token never echoes, only its fetchedAt timestamp and the resolved user identity (email, name, id).
Flags
| Flag | Effect |
|---|---|
--json | Emits { ok: true, user, fetchedAt }, { ok: false, reason: "no_session" }, or { ok: false, reason: "session_invalid", stored: { user, fetchedAt } }. |
deessejs auth logout
deessejs auth logoutTwo-step teardown:
POST /api/v1/auth/sign-outwith the stored bearer token, to invalidate the session server-side.- Delete
~/.deessejs/auth.jsonregardless of the sign-out outcome. A stale file is worse than a missing one, because every subsequent command would silently use a dead token.
If ~/.deessejs/auth.json doesn't exist, auth logout exits 0 silently: there's no session to revoke.
Flags
| Flag | Effect |
|---|---|
--json | { ok: true } on success, { ok: true, reason: "no_session" } if nothing existed to revoke. |
Where the token lives
~/.deessejs/auth.json, mode 0600 on Linux and macOS, or the closest Windows equivalent. The file stays short-lived: each call to a server-hitting command validates the token via auth status semantics, and any 401 invalidates the cache.
The file shape:
{
"access_token": "...",
"user": { "id": "...", "email": "...", "name": "..." },
"fetchedAt": "2026-09-21T10:00:00.000Z"
}The token format stays opaque; treat the entire file as a secret.
Troubleshooting
"The stored token no longer matches an active session on the server."
Run deessejs auth login again. The CLI cached a token that the server no longer recognises (you signed out elsewhere, the session got revoked, or the access token expired). auth status keeps echoing the stale identity until you re-login.
cli_device_denied
You clicked Deny on the /device page, or the page expired before you approved. Re-run deessejs auth login.
cli_device_expired
The 30-minute device-code window elapsed before you approved. The CLI aborts the poll and exits. Re-run deessejs auth login; each invocation requests a fresh code.
Permission errors writing ~/.deessejs/auth.json
The CLI tries fs.open(path, O_WRONLY | O_CREAT, 0o600) on POSIX. If the file already exists with looser permissions, the CLI won't tighten them; fix it manually (chmod 600 ~/.deessejs/auth.json) or delete the file and re-login.
"Logged in as unknown user"
The CLI never falls back to a synthetic identity when the server rejects the bearer token (per ADR-022, "Bug A"). If you see this, it means auth.json got hand-edited or copied from another machine. Delete the file and re-login.
Network errors during polling
Transient network failures inside the device-flow poll window trigger exponential-backoff retries. If the network stays down for the full 30-minute window, the poll exits with cli_device_expired. Re-run auth login once the network returns.