# Authentication

Log in through your browser, use an existing API key, and configure credentials safely for CI or agents.

Use browser login on your workstation. Use an environment variable for CI and agents so credentials do not depend on a machine's saved configuration.

## First command

Authorize the CLI in your browser, then check the credential:

```bash
zernio auth:login
zernio auth:check --pretty
```

Login saves an API key to `~/.zernio/config.json`. A successful check prints JSON containing `success: true` and `message: "API key is valid"`, together with user data.

## Commands

| Task | Command | Effect |
|---|---|---|
| Authorize a workstation | `zernio auth:login` | Browser authorization; saves a key locally |
| Label the authorized device | `zernio auth:login --device-name "workstation"` | Browser authorization with a device label |
| Save an existing key | `zernio auth:set --key "$ZERNIO_API_KEY"` | Writes local configuration; does not validate the key |
| Check the resolved credential | `zernio auth:check` | API read; returns user data |

Create existing keys in the [dashboard](https://zernio.com/dashboard/api-keys). Treat the local configuration file as a secret; do not commit or copy it into a container image.

## Non-interactive authentication

Inject `ZERNIO_API_KEY` from your CI or agent secret manager, then run a read-only command:

```bash
: "${ZERNIO_API_KEY:?Set ZERNIO_API_KEY through your secret manager}"
zernio accounts:list --pretty
```

No browser or `auth:set` is needed. Do not print the variable, enable shell tracing, or put a literal key in command history.

Use a read-only, profile-scoped key for reporting. Grant write access only for tasks that need it. `auth:check` reads team users, so a restricted key can fail that check despite allowing its intended resource.

## Configuration precedence

Each value resolves independently in this order:

| Setting | First | Legacy fallback | File fallback |
|---|---|---|---|
| API key | `ZERNIO_API_KEY` | `LATE_API_KEY` | `apiKey` |
| API base URL | `ZERNIO_API_URL` | `LATE_API_URL` | `baseUrl` |

The CLI selects `~/.zernio/config.json` when it contains an API key. Otherwise, it reads `~/.late/config.json`. A legacy environment variable still overrides a value saved in the newer file.

The default API base URL is `https://zernio.com/api`, without `/v1`. For a custom server, set `ZERNIO_API_URL` or save `--url` with `auth:set`. Never send production credentials to an untrusted host.

## How it behaves

- `auth:login` prints browser instructions and progress to stderr as text; the success result is JSON on stdout.
- `auth:set` saves the key but makes no validation request. Verify with a read allowed by that key.
- Changing the saved key has no effect while an API-key environment variable overrides it.
- On `401`, inspect the message. A platform credential can also fail; use `accounts:health` before assuming your CLI key needs replacement.
- On `403`, check permissions and profile scope before rotating a key.

## Related

- [CLI](/cli): installation and updates
- [Options and output](/cli/options): errors and output streams
- [CLI for agents](/cli/agents): non-interactive execution
- [API keys](/api-keys/create-api-key): scopes and permissions