API ReferenceCLI Reference
login
Authenticate with the NNO platform
Usage
nno login [options]Options
| Name | Type | Required | Description |
|---|---|---|---|
token | string | No | NNO API token (for CI/non-interactive use) |
iam-url | string | No | Override IAM base URL |
Synopsis
nno login authenticates the Neutrino CLI against the NNO IAM service and stores the resulting credentials in ~/.nno/credentials.json. Two modes are supported:
- Token mode (
--token) — for CI/CD pipelines and other non-interactive environments. The provided token is written to the credential file with a 30-day expiry. An optional IAM lookup populatesemail,platformId,role, andexpiresAtfrom the live session; if IAM is unreachable the token is stored withci-useras a placeholder. - Interactive mode — runs the OAuth 2.0 device-authorization flow (RFC 8628). The CLI requests a device code from IAM, prints a short user code and a verification URL, then polls the token endpoint until the user approves, the code expires, or access is denied.
Examples
CI / non-interactive
export NNO_API_TOKEN=$(op read "op://CI/NNO/api-token")
nno login --token "$NNO_API_TOKEN"Output:
✔ Authenticated via tokenThe credential file is written atomically with 0600 mode. The parent ~/.nno/ directory is created on first login (0700), so subsequent nno whoami calls succeed immediately.
Interactive (device flow)
nno loginOutput when IAM has the device endpoint enabled:
ℹ Open this URL in your browser:
ℹ https://console.app.nno.app/activate
╭────────────────╮
│ Code: ABCD-1234 │
╰────────────────╯
ℹ Waiting for authentication...
✔ Authenticated as [email protected]The CLI polls POST /oauth/device/token at the cadence (interval) returned by the device-code response, handling RFC 8628 error states:
| Error code | Behaviour |
|---|---|
authorization_pending | Continue polling at the current interval |
slow_down | Increase polling interval by 5 seconds |
expired_token | Exit 1 — re-run nno login to get a new code |
access_denied | Exit 1 — user declined in browser |
Point at a staging IAM endpoint
nno login --iam-url https://iam-stg.svc.nno.app --token "$STAGING_TOKEN"Credential Storage
| Path | Owner | Mode |
|---|---|---|
~/.nno/ | current user | 0700 (created on first login) |
~/.nno/credentials.json | current user | 0600 |
Source of truth: packages/cli/src/utils/credentials.ts. The stored schema is:
{
"token": "string",
"refreshToken": "string | null",
"email": "string",
"platformId": "string | null",
"role": "string",
"expiresAt": "ISO 8601 timestamp",
"iamUrl": "string",
"audience": "nno-cli"
}nno logout clears the file in place; nno whoami reads it without modification.
Environment Variables
| Variable | Description |
|---|---|
NNO_IAM_URL | Override the IAM base URL (same as --iam-url). Checked after the flag; falls back to https://iam.svc.nno.app. |
Exit Codes
| Code | Meaning |
|---|---|
0 | Login succeeded (token mode or device flow). |
1 | Login failed — IAM unreachable, device code expired, access denied, or unexpected runtime error. |
Error Scenarios
| Scenario | Output | Remedy |
|---|---|---|
| IAM unreachable (interactive) | Interactive login is unavailable — cannot reach the NNO IAM service at <url> | Use --token or check network |
| Device endpoint not enabled (HTTP non-2xx) | Device login endpoint not available (NNN) | Use --token |
| Device code expired | Code expired. Run: nno login | Re-run nno login |
| Access denied in browser | Access denied. | Re-run and approve in browser |
| Login polling timeout (5 min) | Login timed out. Run: nno login | Re-run nno login |
See Also
nno whoami— show the currently authenticated user, platform, and role.nno logout— clear the local credential file.- Auth Flow — Better Auth, per-platform Workers, and the CLI device-authorization flow.