> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Daemon Auth

> How the Mirage daemon authenticates HTTP clients in local, token, and JWT modes.

## What It Does

The daemon serves a local HTTP API on `127.0.0.1:8765`. Every request except `/v1/health` must present a bearer token. Which token is accepted is decided by `MIRAGE_AUTH_MODE`:

| Mode              | When to use                                             | What the daemon accepts                                                   |
| ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------- |
| `local` (default) | One user, one machine. Zero config.                     | A random token the CLI mints into `~/.mirage/auth_token` (mode 0o600).    |
| `token`           | Shared daemon, operator-issued PAT.                     | The exact string in `MIRAGE_AUTH_TOKEN`.                                  |
| `jwt`             | Multi-tenant, external issuer (Clerk, Auth0, your own). | Any RS256-signed JWT that verifies against `MIRAGE_JWT_PUBKEY` / `_FILE`. |

`/v1/health` is always reachable without a token so load balancers and process supervisors can probe it.

## Local Mode (Default)

You usually do nothing. The first time the CLI spawns the daemon it writes a random 32-byte token to `~/.mirage/auth_token` at mode 0o600 and uses it on every subsequent request.

```bash theme={null}
mirage workspace create workspace.yaml --id demo
cat ~/.mirage/auth_token   # the token the daemon expects
```

Probe it directly:

```bash theme={null}
TOKEN=$(cat ~/.mirage/auth_token)
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8765/v1/workspaces   # 200
curl                                          http://127.0.0.1:8765/v1/workspaces   # 401
curl                                          http://127.0.0.1:8765/v1/health       # 200
```

## Token Mode (Operator PAT)

For a daemon you run yourself (Docker, systemd, a shared dev box), pin one token across all clients.

```bash theme={null}
export MIRAGE_AUTH_MODE=token
export MIRAGE_AUTH_TOKEN=<your-pat>

# Python
python -m uvicorn mirage.server.daemon:app --host 127.0.0.1 --port 8765

# TypeScript
node typescript/packages/cli/dist/bin/daemon.js
```

Clients pass the same value:

```bash theme={null}
curl -H "Authorization: Bearer $MIRAGE_AUTH_TOKEN" http://127.0.0.1:8765/v1/workspaces
```

Both server and CLI use a constant-time compare, so timing leakage is not a concern.

## JWT Mode (External Issuer)

Hand the daemon a public key and it will accept any non-expired JWT signed by the matching private key. Verification is networkless: no JWKS fetch, no callback to the issuer.

```bash theme={null}
export MIRAGE_AUTH_MODE=jwt
export MIRAGE_JWT_PUBKEY_FILE=/etc/mirage/issuer-pub.pem
export MIRAGE_JWT_ALG=RS256

# Optional hardening
export MIRAGE_JWT_ISSUER=https://your-issuer.example
export MIRAGE_JWT_AUDIENCE=mirage-daemon
export MIRAGE_JWT_AUTHORIZED_PARTIES=https://app.example,https://cli.example
export MIRAGE_JWT_CLOCK_SKEW_SECONDS=5
```

Hard rules the daemon enforces:

* `alg` is pinned to `MIRAGE_JWT_ALG`. A token signed with a different algorithm is rejected, which defeats alg-confusion attacks.
* `alg=none` is always rejected.
* `exp` is mandatory.
* `typ`, if present, must be `JWT`.
* Opaque (non-three-segment) values in `Authorization: Bearer` are rejected before key work, so probing is cheap.

## Environment Reference

| Variable                        | Modes        | Purpose                                        |
| ------------------------------- | ------------ | ---------------------------------------------- |
| `MIRAGE_AUTH_MODE`              | all          | `local` (default), `token`, or `jwt`.          |
| `MIRAGE_AUTH_TOKEN`             | local, token | Local-mode override; required in `token` mode. |
| `MIRAGE_JWT_PUBKEY`             | jwt          | PEM string of the public key.                  |
| `MIRAGE_JWT_PUBKEY_FILE`        | jwt          | Path to a PEM file (alternative to inline).    |
| `MIRAGE_JWT_ALG`                | jwt          | Signing algorithm to pin, e.g. `RS256`.        |
| `MIRAGE_JWT_ISSUER`             | jwt          | Required `iss` claim.                          |
| `MIRAGE_JWT_AUDIENCE`           | jwt          | Required `aud` claim.                          |
| `MIRAGE_JWT_AUTHORIZED_PARTIES` | jwt          | Comma-separated allow-list for `azp`.          |
| `MIRAGE_JWT_CLOCK_SKEW_SECONDS` | jwt          | Default `5`.                                   |

## Where to Go Next

* [CLI](/home/cli) walks the daily Workspace flow that uses local-mode automatically.
* [Architecture](/home/architecture) shows where the auth middleware sits in the request path.
