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

# Authentication and Profiles

> Store a credential, see which one a start would use, and run several workers on one machine

The worker authenticates with an API key, and it refers to that key by name. A **profile** — a name, an API
key, and the API URL the key belongs to — is kept in `~/.blobhub/credentials.yaml`, **the same file
[blobhub-cli](/cli/auth) reads and writes**. One `login` on a machine serves both tools.

Four commands cover it: `login` stores a profile, `whoami` reports which credential a `start` would pick up
and where its state lives, and `profile ls` lists what is stored. `start` takes the same selection flags as
the rest.

<Note>
  Removing a credential and moving the default belong to `blobhub` — `blobhub logout` and `blobhub profile use`.
  The worker deliberately ships no second writer for verbs the CLI already owns. Until blobhub-cli is published,
  editing `~/.blobhub/credentials.yaml` by hand is the supported way to remove a profile on a worker-only
  install; the file is a `default:` key and a `profiles:` map, nothing more.
</Note>

## Why a profile is more than a credential here

For the CLI a profile is *only* a credential: every invocation is short-lived and keeps nothing. The worker is
different. Everything it persists — which sessions it watches, how far it has read them, the identity it is
pinned to, its single-instance lock — is meaningful only under one identity on one deployment. So a profile
here also names a **state tree**, and each profile gets its own.

That is what lets one machine run several workers at once:

```bash theme={null}
blobhub-worker start --profile prod &
blobhub-worker start --profile staging &
```

Each has its own `config.yaml`, its own cursors, its own logs, and its own lock. A *second* worker on the same
profile is still refused with `WORKER_ALREADY_RUNNING`, and the message names the profile.

### Which tree a run uses

A state tree is named after **wherever its key came from**, not after the profile name that happened to
resolve:

| The key came from                | The tree is                          |
| -------------------------------- | ------------------------------------ |
| a stored profile                 | `~/.blobhub-worker/profiles/<name>/` |
| `--api-key` or `BLOBHUB_API_KEY` | `~/.blobhub-worker/keys/<digest>/`   |

The digest is a one-way hash of the key, so the key itself is never a path, never logged and never printed.

This rule exists to make one situation unrepresentable. With `default: prod` stored and a *different*
`BLOBHUB_API_KEY` exported, the ladder below resolves the *name* `prod` and a *key* from the environment — and
a tree named after `prod` would put two identities into one set of cursors and one lock. Naming by origin
instead means two credentials can never share a tree.

Two consequences follow. The same key supplied by two different routes gets two trees, which surfaces loudly
as `INVALID_CONFIG` naming a `config.yaml` that is not there rather than as silent misbehaviour. And
`--profile` alongside a supplied key runs in a *key* tree — the named profile then contributes only its URL,
and the worker says so in its log.

Run [`whoami`](#blobhub-worker-whoami) to see the resolved path. You need it: a key-derived tree's name cannot
be guessed, and its `config.yaml` has to be authored inside it before the first `start`.

## How a credential is resolved

Three values, each highest-precedence first. This is [blobhub-cli's ladder](/cli/configuration) rung for rung.

1. **Profile name** — `--profile`, else `BLOBHUB_PROFILE`, else the `default:` recorded in the file. A name
   that is not stored is `PROFILE_NOT_FOUND`.
2. **Key** — `--api-key`, else `BLOBHUB_API_KEY`, else the resolved profile's. None of the three is
   `CREDENTIALS_NOT_FOUND`.
3. **URL** — `--api-url`, else `BLOBHUB_API_URL`, else the profile's, else `https://api.blobhub.io/v1`.

A container therefore needs no stored profile at all: `BLOBHUB_API_KEY` on its own is enough.

<Warning>
  One difference from the CLI, on purpose. A profile name that is **provided but empty** is refused rather than
  falling through to the stored default. `--profile "$WORKER_PROFILE"` with the variable unset would otherwise
  start a worker on whatever the default happens to be — and here that selects an entire state tree, not just
  one command's credential. An *unset* `BLOBHUB_PROFILE` still falls through as normal.
</Warning>

`start` announces the profile and tree it resolved on its first log line. That is deliberate: `blobhub profile
use`, typed for a CLI reason, moves which profile a bare `blobhub-worker start` picks up.

## `blobhub-worker login`

Store an API key under a name, and record the identity it belongs to.

```bash theme={null}
blobhub-worker login [--profile <name>] [--api-url <url>]
```

| Flag        | Default                     | Description                       |
| ----------- | --------------------------- | --------------------------------- |
| `--profile` | `default`                   | Name to store this profile under. |
| `--api-url` | `https://api.blobhub.io/v1` | Base API URL to log in against.   |

There is no `--api-key` flag: the key is prompted for without echo, and a flag would put it in your shell
history. The **first** profile stored becomes the file's `default:`; later logins add or replace a profile and
leave the default where it is. Full detail: [`blobhub-worker login`](/worker/cli/login).

## `blobhub-worker whoami`

Show which credential a `start` would use, and where that run's state lives.

```bash theme={null}
blobhub-worker whoami [--profile <name>] [--api-key <key>] [--api-url <url>]
```

It resolves exactly as `start` does, calls `GET /v1/users/me`, and prints five lines:

```text theme={null}
Profile: prod
URL: https://api.blobhub.io/v1
User: Ada Lovelace (<user id>)
State: /home/you/.blobhub-worker/profiles/prod
Running: no
```

A run whose key came from a flag or the environment reports `Profile: key <digest>` — there is no worker
equivalent of the CLI's `Profile: (none)`, because the name is what selects the tree. Every failure a `start`
would hit surfaces here identically, which is the point of running it first.

## `blobhub-worker profile ls`

List stored profiles, annotated with what this machine knows about each.

```bash theme={null}
blobhub-worker profile ls
```

```text theme={null}
* prod (https://api.blobhub.io/v1) [running]
  staging (https://api.blobhub.io/v1) [state]
  ci (https://api.blobhub.io/v1) [no state]
  (key 9f31c0a2b4d67e18) [state]
```

`*` marks the file's default. The bracket says whether this machine has a state tree for that profile and
whether a worker is live in it — which is what `blobhub profile ls` structurally cannot show. Key-derived
trees are listed too, by digest, since they are state trees with no name. No network call is made.

## Upgrading from a pre-profiles worker

Installs older than v0.5.0 kept everything flat under `~/.blobhub-worker/`. The first `login` or `start` after
upgrading migrates automatically and prints what it moved: state into `profiles/default/`, and the credential
into `~/.blobhub/credentials.yaml` as the profile `default`. The old copy of the key is removed, not left
behind.

It refuses rather than overwrites in two cases — a pre-upgrade worker still running (stop it first), and a
`default` profile already in the shared store under a *different* key (`MIGRATION_CONFLICT`; rename yours or
move it aside).

One case migrates but needs a follow-up, and the migration says so: if the shared file's `default:` already
names some *other* profile, it is left alone — it is not the worker's to move — so a bare `blobhub-worker
start` will not pick up the tree just migrated. Use `--profile default`, or move the pointer with `blobhub
profile use`.

## Sharing the file with blobhub-cli

Because there is one store, actions in one tool are visible in the other:

* `blobhub login --profile prod` creates a profile this worker can use immediately. Its first `start` records
  the identity then, rather than requiring a `blobhub-worker login` you have effectively already done.
* `blobhub logout --all` removes the worker's credential too.
* `blobhub profile use` moves which profile a bare `blobhub-worker start` resolves.
* blobhub-cli has **no identity-change guard** on `login` — a CLI switches identities by design. So re-logging
  into a name this worker is pinned to surfaces at its next `start` as `USER_IDENTITY_MISMATCH`, which is the
  right place for it to land: the tree's cursors belong to the old identity.

There is no lock around the file, so two simultaneous logins lose one profile edit. That was already true of
two `blobhub login`s.

## Errors

| Code                               | When                                                                                  |
| ---------------------------------- | ------------------------------------------------------------------------------------- |
| `AUTH_INVALID`                     | The key was empty, or `GET /v1/users/me` rejected it.                                 |
| `PROFILE_NOT_FOUND`                | A named profile is not stored, or the name is not a valid profile name.               |
| `CREDENTIALS_NOT_FOUND`            | No key from flags, environment or the file — or the file is present but malformed.    |
| `INSECURE_CREDENTIALS_PERMISSIONS` | `~/.blobhub/credentials.yaml` is readable by group or other. Refused, never repaired. |
| `USER_IDENTITY_MISMATCH`           | The server's user differs from the one recorded for this profile.                     |
| `MIGRATION_CONFLICT`               | A pre-profiles migration found a different `default` already stored.                  |

Full catalog: [Reference](/worker/reference).

## See also

* [`blobhub-worker login`](/worker/cli/login) — the command in full, including running as a service account.
* [`blobhub-worker start`](/worker/cli/start) — the selection flags and the preflight order.
* [Filesystem layout](/worker/filesystem) — what a state tree contains.
* [blobhub-cli authentication](/cli/auth) — the other half of the shared store.
