Authentication
The CLI uses the bearer auth mode of the Athena API. Every request
sends Authorization: Bearer <access_token> and X-Auth-Mode: bearer,
which tells the server to return refresh_token in the response body
rather than as an httpOnly cookie. That makes the CLI work the same on
servers, laptops, and CI runners without cookie jar gymnastics.
Two ways to provide a token
Stored login (recommended)
export ATHENA_PASSWORD='<password>'athena login --email you@leanscale.com --password "$ATHENA_PASSWORD"Persists to ~/.athena/credentials.toml (chmod 600). The CLI uses these
credentials for every authenticated command. On a 401 it transparently
refreshes the access token using the stored refresh token; if refresh
fails it exits 3 with code refresh_failed.
athena logoutWipes the local credentials and tries to revoke the refresh token server-side. If the server revoke call fails, the local credentials are still removed (you can’t fix a bad token by keeping it).
Stateless via env var
For CI or one-off scripts:
export ATHENA_API_TOKEN="eyJhbGciOi..."athena agent listWhen ATHENA_API_TOKEN is set the CLI uses it verbatim and skips the
credentials file. Errors point at ATHENA_API_TOKEN rather than athena login so the hint matches your actual setup.
Resolver chain
For every command, the effective values are resolved in this order (first match wins):
| Setting | 1. Flag | 2. Env var | 3. Profile in config.toml |
|---|---|---|---|
| API base URL | --api-url <url> | ATHENA_API_URL | [profile.<name>].api_url |
| Access token | --token <jwt> | ATHENA_API_TOKEN | [profile.<name>] in credentials.toml |
| Profile name | --profile <name> | ATHENA_PROFILE | default_profile in config.toml (built-in fallback "default") |
| Workspace ID | --workspace <slug> | ATHENA_WORKSPACE_ID | [profile.<name>].active_workspace |
Missing required values surface as flat errors:
{ "code": "missing_api_url", "message": "api_url not configured for profile 'default' (set via --api-url, ATHENA_API_URL, or [profile.default].api_url)", "hint": "Set `api_url` in [profile.<name>] or pass --api-url."}Profiles
Use profiles to switch between dev/beta/prod, between workspaces, or between accounts.
default_profile = "dev"
[profile.dev]api_url = "https://athena-dev-api.leanscale.com"active_workspace = "019f58c6-0400-7fa2-a41f-a32abf613c76"
[profile.beta]api_url = "https://athena-beta-api.leanscale.com"
[profile.prod]api_url = "https://athena-api.leanscale.com"Switch on a single command without changing the default profile:
athena --profile prod agent listATHENA_PROFILE=prod athena agent list # sameathena login writes credentials under the current profile. Logging
into beta is:
athena --profile beta login --email you@leanscale.com --password "$ATHENA_PASSWORD"What gets persisted where
| File | Created by | What it holds |
|---|---|---|
~/.athena/config.toml | You (or installer) | default_profile, profile api_url, active_workspace, anything else editable |
~/.athena/credentials.toml | athena login | access_token, refresh_token, expires_at, user_email per profile. chmod 600. Never edit by hand. |
Header validation
Env-derived header values are validated before they hit the wire.
Crafted CRLF or non-ASCII bytes in ATHENA_WORKSPACE_ID or
--idempotency-key exit 2 with invalid_header_value rather than
panicking inside the HTTP layer or smuggling additional headers.