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

# Authentication & profiles

The CLI signs every request with a per-key ES256 JWT (P-256 ECDSA). You generate a keypair locally, register the **public** half with Bron through the UI, and keep the **private** half on disk. The CLI never sees your password and never round-trips the private key over the network.

## Generate a keypair + profile in one step

```bash theme={"system"}
bron config init --key-file ~/.config/bron/keys/me.jwk
```

In an interactive terminal, the CLI does the whole bootstrap end-to-end:

1. Prompts for a profile name (suggesting `default` if the slot is free; otherwise leaves the default empty so a stray Enter can't overwrite the active profile).
2. If `--key-file` points to a **non-existent** path → generates a fresh P-256 ECDSA keypair locally (`crypto/ecdh`), writes the **private JWK** to that path at mode `0600` (only your user can read it). The file contains `kty`, `crv`, `x`, `y`, `d`, `kid` — the `kid` is the random key identifier used to look up the matching public key on the server.
3. If `--key-file` points to an **existing** private JWK → leaves it alone, just re-derives the public half.
4. Prints the **public JWK** (no `d` field) and waits — paste it into Bron Settings → API keys.
5. After you press Enter, calls `GET /workspaces` with the freshly registered key. That single call validates the registration (you'd 401 if the public JWK wasn't accepted yet) and returns the workspace the key is bound to. The CLI saves the resolved `workspaceId` into the profile.

So in the interactive flow you don't have to type the workspace ID at all — the server tells the CLI which workspace the key belongs to. If you already know it (CI / scripts / non-TTY runs), pass it explicitly and the CLI skips the auto-discovery step:

```bash theme={"system"}
bron config init --name default --workspace <workspaceId> --key-file ~/.config/bron/keys/me.jwk
```

In **non-interactive runs** (no TTY on stdin), `--workspace` is required — the auto-discovery prompt has no scripted equivalent, and silently saving an unverified profile would be worse than failing fast.

```json theme={"system"}
{
  "kty": "EC",
  "crv": "P-256",
  "kid": "f3c2…",
  "x": "qO0e…",
  "y": "8j8x…"
}
```

Paste that public JWK into **Settings → API keys** in the Bron UI. Pick a name, role, optional IP whitelist, tick **Input public key (JWK)** and paste the JSON into the **Public key (JWK)** field, then **Generate key**.

<img src="https://mintcdn.com/bron/TUdNBpHBUzrJANOn/images/api-key-creation-modal.png?fit=max&auto=format&n=TUdNBpHBUzrJANOn&q=85&s=db7f7e5e2e95670e0babe3b7207625ae" alt="New API key form in Bron UI, with Input public key (JWK) ticked" style={{ maxWidth: 300, width: '100%', display: 'block' }} width="940" height="1604" data-path="images/api-key-creation-modal.png" />

Bron stores the public key indexed by `kid`; when the CLI signs a request the server looks up the matching public key and verifies the signature.

The private key never leaves your machine. If you lose it, generate a new keypair (point `--key-file` at a fresh path) and revoke the old `kid` in the UI.

### Re-print the public JWK at any time

If you forgot to copy the public JWK earlier, rerun `bron config init` with the same `--name` and `--key-file`. The CLI re-derives the public half from the existing private JWK on disk (no key regeneration), prints it, and walks you through the same auto-discovery flow as the first run — paste the JWK into Bron Settings → API keys, press Enter, the CLI calls `GET /workspaces` to validate and resolves the workspace ID:

```bash theme={"system"}
bron config init --name default --key-file ~/.config/bron/keys/me.jwk
```

If you want to skip the validation round-trip and just reprint the public half (e.g. you already know the registration is fine), pass `--workspace` explicitly — the CLI then skips `GET /workspaces` and writes the profile as-is:

```bash theme={"system"}
bron config init --name default --workspace <workspaceId> --key-file ~/.config/bron/keys/me.jwk
```

## Profiles

A profile binds a key file, workspace, and base URL together so you don't repeat them on every call. Configs live in `~/.config/bron/config.yaml` (override with `BRON_CONFIG`).

```bash theme={"system"}
# Set up the default profile (interactive: workspaceId resolved automatically).
bron config init --key-file ~/.config/bron/keys/me.jwk

# Add a second profile (e.g. for a different workspace).
bron config init --name staging \
  --key-file ~/.config/bron/keys/staging.jwk \
  --base-url https://api.qa.bron.org   # optional; default is production

# Switch the active profile.
bron config use-profile staging

# Modify keys without re-running init.
bron config set workspace=<otherWorkspaceId> proxy=http://proxy:8080

# Inspect.
bron config show              # active profile + config file path (env overrides applied)
bron config show --raw        # unmodified YAML entry
bron config show --output json
bron config list              # all profiles, with the active one annotated
```

### Per-call overrides

Every flag a profile sets has a CLI override:

```bash theme={"system"}
bron --profile staging tx list
bron --workspace <workspaceId> tx list
bron --key-file ~/.config/bron/keys/other.jwk tx list
bron --proxy http://proxy:8080 tx list
```

### Env-var overrides

Useful for CI / containers / one-off scripts:

```bash theme={"system"}
BRON_PROFILE=staging                                 bron tx list
BRON_WORKSPACE_ID=<workspaceId>                      bron tx list
BRON_API_KEY="$(op read 'op://Personal/Bron/jwk')"   bron tx list   # raw JWK bytes; never lands on disk
BRON_API_KEY_FILE=~/.config/bron/keys/other.jwk      bron tx list
BRON_BASE_URL=https://api-staging.bron.org           bron tx list
BRON_PROXY=http://user:pass@proxy:8080               bron tx list
HTTPS_PROXY=http://proxy:8080                        bron tx list   # standard env vars are honored too
BRON_CONFIG=/tmp/cli.yaml                            bron config show
```

`BRON_API_KEY` (raw JWK bytes) wins over `BRON_API_KEY_FILE` and `key_file:` when all are set — pair it with a secret store (1Password, Vault, sops) so the private JWK never lives on disk. The CLI strips the var from its environment after reading, so child processes don't inherit it.

Precedence (highest first): explicit flag → env var → active profile → built-in default.

## Proxy

Outbound from the CLI honors:

* `BRON_PROXY` env var
* standard `HTTPS_PROXY` / `HTTP_PROXY` env vars
* `proxy` field set on the active profile (`bron config set proxy=...`)

If you sit behind a proxy that requires auth, embed the credentials: `http://user:pass@host:8080`. The same proxy is used for both REST and WebSocket transports (the `bron tx subscribe` socket goes through it too).

## Key rotation

1. Generate a new keypair *and* point the active profile at it in one step: `bron config init --key-file ~/.config/bron/keys/me-new.jwk` (the non-existent path triggers a fresh keygen + private file at mode 0600 + prints the public JWK).
2. Register the new public JWK in **Settings → API keys**.
3. Verify with a no-op call: `bron workspace info`
4. Revoke the old `kid` in the UI.

You can have multiple active keys at once — the server matches by `kid`, so old and new keys coexist during a rotation window.

## Permissions

API keys inherit the permissions of the workspace member they belong to. If the user can approve transactions in the UI, the CLI can too — there's no separate "API mode" with reduced privileges. Reduce scope by creating a member with limited permissions and registering the key under that member's account.

## Troubleshooting

| Symptom                                               | Likely cause                                                         |
| ----------------------------------------------------- | -------------------------------------------------------------------- |
| `403 Forbidden` on every call                         | Public key not registered, or the `kid` was revoked                  |
| `401 Unauthorized` immediately after rotation         | Old `kid` still in active profile — `bron config show` to inspect    |
| `dial: x509: certificate signed by unknown authority` | Corporate proxy MITM — set `HTTPS_PROXY` or import the CA            |
| `403` on some endpoints, `200` on others              | Member's role doesn't grant the action — check workspace permissions |

If a request fails with a 4xx / 5xx, the CLI prints the API error envelope plus an `id:` line containing the correlation ID. Quote that ID when reporting issues — it lets us pull the exact ES log line for your call.
