# Credentials and profiles

Source: https://codeherder.com/docs/credentials/

How to sign in with your browser or configure an API key, verify your connection, and save named credential profiles.

The `ch` CLI authenticates in two ways: **browser sign-in** (the easiest path for interactive use) or an **API key** (required for agents, CI, and automated scripts). Either way, you must also tell it which workspace to operate on — or commit a [project defaults file](https://codeherder.com/docs/project-config/) so your whole team gets the same server and workspace automatically.

This page covers the CLI. If your account is on the Enterprise plan and signs people into the web app through your own identity provider, see [Single sign-on (SAML)](https://codeherder.com/docs/sso/) — it’s a separate path and doesn’t change how the CLI authenticates.

| Variable | What it holds | Default |
| --- | --- | --- |
| `CH_TOKEN` | Your API key. Required for the API-key authentication path. | *(none)* |
| `CH_WORKSPACE_ID` | The workspace commands operate on — a name, slug, slug path, or UUID, despite the variable’s name. Most `ch` verbs are workspace-scoped and read this automatically. | *(none — required for workspace commands)* |
| `CH_ADDR` | The API base URL of your CodeHerder server. | `{{api_base}}` |

Neither value comes from a single page. For `CH_WORKSPACE_ID`, list your workspaces — with their IDs, names, and slugs — with `ch workspace list`; any of those forms works, so you don’t have to copy a UUID. For `CH_TOKEN`: browser sign-in (below) caches a credential automatically, with nothing to copy, and an API key you can export yourself comes from a member’s page under **Set up → Members** or `ch member keys create` — see [Minting API keys](https://codeherder.com/docs/members-and-teams/#minting-api-keys).

You can also select the workspace on a per-command basis with `--workspace <ref>`, which overrides `CH_WORKSPACE_ID` for that invocation. A workspace reference can be a name, slug, slug path, or UUID — see [Referring to things on the command line](https://codeherder.com/docs/using-the-cli/#referring-to-things-on-the-command-line) for the rules:

```
ch --workspace acme/platform task list
```

See [Managing workspaces](https://codeherder.com/docs/workspaces/) for the full workspace-selection order and how to find your workspace ID.

## Browser sign-in

Run `ch login` to open your browser and sign in to CodeHerder:

```
ch login
```

This opens your default browser, completes the sign-in flow, and caches your credentials securely. The first time you do this on a given machine, it also asks you to approve the sign-in from that browser tab — approve it once, and every later `ch login` on that machine skips the extra step. Subsequent `ch` commands authenticate automatically — you do not need to set `CH_TOKEN`.

To sign out and remove the cached credentials:

```
ch logout
```

`ch logout` revokes this installation’s credential on the server first, then removes the cached credential from this machine. Pass `--local-only` to skip the server-side revoke and only clear the local cache. Either way, it does not affect any API keys or profile files you have configured. To revoke a machine or API key you no longer have access to, see [My work](https://codeherder.com/docs/my-work/#your-machines).

## API key

For agents, CI pipelines, and any non-interactive scenario, export your API key directly:

```
export CH_TOKEN=<your-api-key>
export CH_WORKSPACE_ID=<your-workspace-id>
```

Add these to your shell profile (`~/.zshrc`, `~/.bashrc`, or equivalent) so they persist across terminal sessions.

### When credentials expire

Some credentials never expire on their own. On a self-hosted server, revoke them by hand.

- **API keys** for people do not expire.
- **CLI sign-in** on a machine renews itself and does not expire.
- **Device tokens** do not expire.

To revoke one credential:

- CLI sign-in on this machine: `ch logout`.
- An API key: `ch member keys delete <hash>`. Find the hash with `ch member keys <memberRef>`.
- A device: `ch device rotate-token <deviceRef>`, or see [My work](https://codeherder.com/docs/my-work/#your-machines).

To revoke everything one person holds, an instance operator runs:

```
ch human revoke-all <humanRef> --yes
```

This calls `POST /v1/instance/humans/{id}/revoke-all`. It revokes every API key, MCP grant, device token and CLI installation of that person. It does not disable their sign-in. Disable the user in your identity provider as well.

### Bootstrapping the first account (self-hosted)

If you’re running a self-hosted CodeHerder server that doesn’t offer [browser sign-in](https://codeherder.com/docs/credentials/#browser-sign-in), use `ch human bootstrap` once to create the first workspace, human account, and API key. Cloud users should use `ch login` or the web sign-up flow instead. See [Self-hosted deployment](https://codeherder.com/docs/self-hosting/) for downloading and starting the server itself. A fresh server admits the first verified caller as its first account. Create it before you expose the server. See [Create the first account](https://codeherder.com/docs/self-hosting/#create-the-first-account).

`ch human bootstrap` is interactive and email-verified — it does not hand you a key in a single step:

1. Run the command with the email you want to sign in with: `ch human bootstrap --email you@example.com --display-name "Your Name" --workspace-name "My Workspace"`
2. The CLI completes a quick automatic verification step on its own — you’ll see a brief “verifying…” message, but there’s nothing for you to do.
3. CodeHerder emails a one-time 6-digit code to the address you passed.
4. Paste that code at the CLI’s prompt.
5. Once the code checks out, the CLI writes your credentials — printed as an env-block for you to save, or written straight to `~/.codeherder/<name>.env` if you passed `--install-as <name>`. The API key is shown only this once, so save it right away.

You need access to the inbox for the `--email` address you passed — the code only goes there. Each code is one-time and expires; if it doesn’t arrive or stops working, just re-run `ch human bootstrap` to request a fresh one.

If a server has turned off self-service signup, `ch human bootstrap` reports that signup is closed on that server. In that case, ask your administrator for an API key instead.

**`ch human verify`** completes the emailed-code step separately from `ch human bootstrap`, non-interactively — for when you already have a verification ID and the emailed code (for example, if the interactive prompt above was interrupted after the code was sent). It takes `--verification-id`, `--code`, and `--email` (all required), plus the same optional `--install-as <name>`:

```
ch human verify --verification-id <id> --code <6-digit> --email you@example.com --install-as me
```

## Verify your connection

Once signed in, confirm the CLI can reach CodeHerder and recognise your identity:

```
ch whoami
```

This prints your display name, member ID, and home workspace, along with the server endpoint it reached and its version. When the server build is stamped, it also shows a short build reference next to the version — see [Confirm which build is live](https://codeherder.com/docs/self-hosting/#confirm-which-build-is-live) for why that matters on a self-hosted server. It’s a quick way to confirm you’re signed in and talking to the right server. It does **not** confirm that `CH_WORKSPACE_ID` (or `--workspace`) resolves to anything: it always reports your home workspace, regardless of what either is set to. To check the workspace you’ve actually set, scope the call explicitly:

```
ch whoami --workspace <ref>
```

This prints your home workspace, the workspace `<ref>` resolves to, and your role there — `none` rather than an error if you’re not a member of it. Pass `$CH_WORKSPACE_ID` as `<ref>` to confirm the variable you exported actually works. If you see an error, double-check that you are signed in (`ch login` or `CH_TOKEN`) and that your workspace is set (`CH_WORKSPACE_ID` or `--workspace`) — see [Troubleshooting sign-in](https://codeherder.com/docs/credentials/#troubleshooting-sign-in) below for the specific messages.

If a [project defaults file](https://codeherder.com/docs/project-config/) supplied `CH_ADDR` or `CH_WORKSPACE_ID` for this command, `ch whoami` also prints a `project config` row naming the file and which keys it set — so a surprising server or workspace is traceable to its source in one command. The row appears only when at least one key was actually applied.

## Profiles

A **profile** is a named credential file at `~/.codeherder/<name>.env`. Profiles let you keep multiple credential sets and switch between them without editing your shell profile. The file is written at mode 0600 so its contents are readable only by your own account.

### Creating a profile

Commands that mint a new API key accept `--install-as <name>` to write the credentials directly to `~/.codeherder/<name>.env` in one step:

```
# Mint a key for an existing member
ch member keys create <memberId> --install-as laptop

# Create an agent and mint its key in one call
ch agent create --display-name Builder --mint-key --install-as builder
```

Minting a key for a person needs the Pro plan; minting one for an agent is unaffected — see [Plans and limits](https://codeherder.com/docs/plans-and-limits/#what-your-plan-unlocks).

`ch human bootstrap` also accepts `--install-as`, but it isn’t a one-step mint — it writes the profile only after you complete an email verification step. See [Bootstrapping the first account](https://codeherder.com/docs/credentials/#bootstrapping-the-first-account-self-hosted) below.

Without `--install-as`, the same commands print the env-block to standard output so you can save it manually:

```
cat > ~/.codeherder/staging.env << 'EOF'
export CH_ADDR={{api_base}}
export CH_TOKEN=ch_...
export CH_WORKSPACE_ID=019e...
EOF
chmod 600 ~/.codeherder/staging.env
```

### Using a profile

There are two ways to activate a profile, and they behave differently with respect to the workspace.

**`source` — loads all variables, including the workspace:**

```
source ~/.codeherder/staging.env
```

`source` runs the file in your current shell, setting every variable it contains — including `CH_WORKSPACE_ID`. Use `source` when you want to switch both identity and workspace at once.

**`--profile` or `CH_PROFILE` — loads credentials, never the workspace:**

```
ch --profile staging task list
```

or, to apply it to every command in a shell session:

```
export CH_PROFILE=staging
```

When `--profile` or `CH_PROFILE` is set, the CLI reads the named file and applies the token (`CH_TOKEN`) and server address (`CH_ADDR`). **A profile never sets the workspace** — that always comes from `CH_WORKSPACE_ID` or a `--workspace` flag, regardless of which profile (if any) is active. Use this form when you want to authenticate as a different user but keep working in the same workspace. To switch workspace at the same time, use `source`, or pass `--workspace` alongside `--profile`.

### Credential precedence

The CLI resolves credentials in this order — later sources override earlier ones:

1. A committed [project defaults file](https://codeherder.com/docs/project-config/) (`.codeherder.env`) — the lowest tier, and the only source that applies before you set anything in your shell. It never carries `CH_TOKEN` or any other credential.
2. Cached browser sign-in from `ch login` (used only if no explicit token is set)
3. `CH_*` environment variables (override the browser sign-in cache)
4. The profile named by `CH_PROFILE` (overrides the env vars above)
5. The profile named by `--profile` (overrides `CH_PROFILE`)
6. Explicit per-command flags (`--token`, `--addr`) — always win

In practice: use `ch login` for day-to-day interactive work; export `CH_TOKEN` or load a profile to authenticate as a specific identity (such as an agent key or a different workspace), and use explicit flags for one-off overrides. Commit a project defaults file only for values every checkout of a repo should share, like which server your team runs.

## Troubleshooting sign-in

**`ch login` doesn’t seem to take effect.** If `CH_TOKEN` is exported in your shell, or a profile is active via `CH_PROFILE` or `--profile`, that credential overrides the browser sign-in cache — silently, with no warning printed at sign-in time. If commands keep authenticating as the wrong identity after `ch login`, check whether `CH_TOKEN` or `CH_PROFILE` is set in your environment and unset whichever one is taking precedence.

**A “no token” error:**

```
no token: set CH_TOKEN, pass --token, or use --profile
```

None of the credential sources — browser sign-in, `CH_TOKEN`, or a profile — resolved to a token. Run `ch login` to sign in with your browser, or set `CH_TOKEN` (and `CH_WORKSPACE_ID`) directly.

**An expired browser sign-in.** If your cached sign-in has expired, the CLI tells you so and asks you to run `ch login` again. You may see the “no token” error above right after it, if nothing else is configured as a fallback. Re-running `ch login` refreshes the cache.

**A self-hosted server without browser sign-in.** Not every CodeHerder server offers the browser sign-in flow. If `ch login` exits saying the server has no browser sign-in configured, use the API-key path instead: follow [Bootstrapping the first account](https://codeherder.com/docs/credentials/#bootstrapping-the-first-account-self-hosted) to create one with `ch human bootstrap`, or paste an existing key into `CH_TOKEN` or a profile file. If the server has closed self-service signup, `ch human bootstrap` tells you so — ask your administrator for a key instead.

**A loose-permissions warning on the sign-in cache.** The CLI stores your browser sign-in at mode 0600 (readable only by your account) under `~/.codeherder/`. If those permissions are ever widened — for example by a manual copy or a backup restore — the CLI refuses to use the cache and warns you, naming the file. `chmod 600` the path it names, or delete the file and run `ch login` again.

**A refusal naming your `.codeherder.env`.** If a repo you’re working in has committed a [project defaults file](https://codeherder.com/docs/project-config/) that sets `CH_ADDR`, and you’re also supplying a token (via `CH_TOKEN`, `--token`, or a profile) without otherwise confirming the server, `ch` refuses rather than send your token somewhere the repo chose without asking you:

```
ch: <path>/.codeherder.env declares CH_ADDR=<addr> — refusing to send your bearer token there without your consent. Export CH_ADDR=<addr> yourself, or pass --addr <addr>, if you mean it.
```

If instead your cached `ch login` credential is for a different server than the one the project file names, `ch` refuses to send that credential to the project’s server:

```
ch: <path>/.codeherder.env declares CH_ADDR=<addr>, but your cached credential (`ch login`) is for a different server (<other addr>) — refusing to send it there. Run `ch login` against <addr>, or pass --token.
```

Either way, do what the message says: run `ch login` against the address the project file names, or pass `--token` explicitly.
