> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.ekanos.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Ekanos CLI authentication flow, credential storage, and host resolution.

The CLI uses the **OAuth 2.0 device authorization flow** to authenticate against an Ekanos deployment. Your credentials are stored locally in a mode-restricted file keyed by host.

## Quick start

```bash theme={null}
# Authenticate against your Ekanos host
ekanos login --host https://your-ekanos-host

# Verify you're logged in
ekanos whoami

# See what sources you can publish to
ekanos sources

# When done, sign out
ekanos logout
```

No browser port binding, no loopback redirect needed — the flow works over SSH, in containers, and from CI.

## Login flow

1. Run `ekanos login --host <url>`
2. The CLI prints a short code and a browser URL to **stderr**
3. Open the URL in any browser on any device
4. Approve the request
5. The CLI polls and confirms when approved

The device code and URL go to stderr so they're visible even when stdout is piped. There is nothing to configure on your side: the CLI asks the host for its login configuration, so one binary works against any deployment.

If the code expires before you approve it, `login` exits 4 with `The login code expired before it was approved.` — run `ekanos login` again.

## Host resolution

`logout`, `whoami`, `status`, `sources`, `use`, `publish`, and `upgrade` resolve the host in this order:

1. **`--host` flag**
2. **`EKANOS_HOST` environment variable**
3. **`host` field in `ekanos.json`** (the project's deployment)
4. **Sole stored host** (if your credentials file holds exactly one)

The first source that has an answer wins. When the host comes from `ekanos.json` or the sole stored login, the CLI names it on stderr.

**`login` is special:** it resolves from `--host`, else `EKANOS_HOST`, else the sole stored host — **never** from `ekanos.json`. Login is the one verb that *creates* credentials, and `ekanos.json` is committed to git; if login honored it, anyone could point your device flow at their own tenant just by editing a file.

### Example: single-deployment workflow

```bash theme={null}
# Log in once with your Ekanos host
ekanos login --host https://your-ekanos-host

# Later verbs need no --host: the sole stored host is used
ekanos whoami
ekanos publish --source my-source   # also saves host and source to ekanos.json
```

### Example: multi-deployment workflow

```bash theme={null}
# Log in to both deployments
ekanos login --host https://dev.your-ekanos-host
ekanos login --host https://prod.your-ekanos-host

# With two stored hosts, be explicit (or set EKANOS_HOST)
ekanos publish --host https://prod.your-ekanos-host
```

`ekanos use` and a successful `ekanos publish` write `host` to `ekanos.json` only when it has no `host` yet; an existing value is never overwritten by a one-off `--host`. After that, verbs in the project resolve the host from `ekanos.json`.

### No host specified, multiple stored

```
$ ekanos whoami
Error: No Ekanos host given.
Hint: You are logged in to 2 hosts (https://dev.your-ekanos-host, https://prod.your-ekanos-host) — pass "--host <url>" to choose one, or set EKANOS_HOST.
```

This is a usage error (exit 2). The CLI never picks a host silently.

***

## Credential storage

Credentials are stored in `~/.ekanos/credentials.json` with mode **0600**, inside a `~/.ekanos` directory forced to **0700**. Writes go to a temporary file that is renamed into place.

### File structure

```json theme={null}
{
  "version": 1,
  "hosts": {
    "https://your-ekanos-host": {
      "auth0RefreshToken": "...",
      "accessToken": "...",
      "expiresAt": "...",
      "userId": "...",
      "email": "you@example.com",
      "updatedAt": "..."
    }
  }
}
```

Each entry is keyed by the normalized host origin (scheme and host, lowercased, no trailing slash). `accessToken` is the short-lived Ekanos JWT; `auth0RefreshToken` is the rotating refresh token.

A file that is not valid JSON or not a recognized credentials file is a `precondition_failed` error (exit 9) rather than a silent logout. It holds only credentials, so deleting it and running `ekanos login` again is safe.

### Relocation

Set `EKANOS_HOME` to use a different home directory (useful in containers, CI):

```bash theme={null}
export EKANOS_HOME=/var/lib/ekanos
ekanos login --host https://your-ekanos-host
```

Credentials are then stored in `$EKANOS_HOME/.ekanos/credentials.json`.

### Permissions

If the file or directory is group- or world-accessible, the CLI tightens the permissions and **warns on stderr**, for example:

```
ekanos: /home/you/.ekanos/credentials.json was readable by other users (0644); tightening it to 0600. Rotate your session with "ekanos logout && ekanos login" if this machine is shared.
```

The CLI does **not** refuse to run. If the machine is shared, assume the token was exposed and re-authenticate.

### No token in output

Tokens are never printed — not by `whoami`, not in error messages, not in `--json` envelopes, not at any log level.

***

## Idempotence

### Login

Running `ekanos login` with a session that still works verifies it against the server and exits 0 without opening a browser:

```
$ ekanos login --host https://your-ekanos-host
login: already authenticated to https://your-ekanos-host as you@example.com.
```

In JSON mode, `data.status` is `already-authenticated` (versus `authenticated` after a fresh login). Use `--force` to start a fresh login regardless:

```bash theme={null}
ekanos login --force --host https://your-ekanos-host
```

### Logout

Logging out twice exits 0 both times:

```
$ ekanos logout
logout: signed out of https://your-ekanos-host and revoked the session.

$ ekanos logout
logout: no stored session for https://your-ekanos-host.
```

In JSON mode `data` is `{ host, cleared, revoked }`; the second run reports `cleared: false`. `--all` signs out of every stored host and reports per-host `revoked` results.

Revocation is attempted but never blocks the local delete: try to revoke, always delete, report both.

***

## Rotation safety

Auth0 rotates the refresh token the moment the server's exchange succeeds, so from that instant the replacement is the only copy in existence — and `/api/auth/cli-refresh` can still fail afterwards. It therefore returns the rotated token on its error responses too, and the CLI persists it the moment it is seen, before surfacing any error.

### Single process: rotate, then fail

1. The CLI refreshes the session
2. The refresh token rotates and is saved immediately
3. A later step fails
4. Retry the command — the saved token is used

A transient blip costs you a retry, not your session. If the host renewed the session but could not verify the identity, the CLI exits 8 with a hint saying you do not need to log in again.

### Two processes: concurrent rotation

Two CLIs sharing one credentials file both present the same token. Auth0 honors the first; the second gets a clean exit 4 (`auth_required`). There is no file lock — re-run `ekanos login`.

***

## Exit codes

* **`0`** — Success
* **`2`** — No host could be resolved
* **`4`** — Auth required: no session, an expired one that could not be renewed, or a revoked one. Run `ekanos login`
* **`8`** — Network: the host was unreachable, or the refresh rotated but could not complete. Retry; do not log in again
* **`9`** — Precondition: an unreadable credentials file, an invalid `--host` URL, or a host without CLI login configured

***

## Troubleshooting

### Not logged in to a host

```
Error: Not logged in to https://your-ekanos-host.
Hint: Run "ekanos login --host https://your-ekanos-host".
```

You haven't authenticated against that host yet. Exit 4.

**Fix:** Run `ekanos login --host <url>`.

### Several hosts stored and none chosen

See [No host specified, multiple stored](#no-host-specified-multiple-stored).

**Fix:** Use `--host` or set `EKANOS_HOST`:

```bash theme={null}
ekanos whoami --host https://prod.your-ekanos-host

# Or
export EKANOS_HOST=https://prod.your-ekanos-host
ekanos whoami
```

### Revocation failed during logout

```
ekanos: the local credentials were removed, but the refresh token could not be revoked at Auth0. It stays valid until it expires — revoke the session in Auth0 if this machine is compromised.
```

The local delete succeeded; only revocation did not. `logout` still exits 0 and reports `revoked: false`.

**Fix:** Nothing urgent — the token is off your machine. If the machine is compromised, ask your Ekanos operator to revoke the session.

### Session expired and could not be renewed

```
Error: The stored session for https://your-ekanos-host is no longer valid.
```

The refresh token expired, was revoked, or was already spent by another process sharing the credentials file. Exit 4. A related message is `The session for https://your-ekanos-host could not be renewed.`

**Fix:** Re-authenticate:

```bash theme={null}
ekanos login --host https://your-ekanos-host
```

### Plaintext HTTP warning

```
ekanos: http://your-ekanos-host is plaintext HTTP. Your credentials will cross the network unencrypted. Use https:// unless this is a local host.
```

`http://` is allowed (local development needs it), but a non-loopback plaintext host gets this warning. Use `https://`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.