# Claude login from the swarm UI An operator logs an agent in to claude from the agent's card in the swarm UI. No hive is in the path: the login runs in the swarm's `swarm-claude-login` service, the credentials go to the agent's own leaf in the secret store, and the agent's harness installs them from there. Where it runs and how it's enabled: [`services.md`](services.md#claude-login-minter). The service runs the `claude-code` that `services.hyperhive.c0re.claudeCodePackage` pins for agents, and its container's own nixpkgs' where the hive pins none. ## What happens on a login 1. The agent publishes `needs_login` in its turn-state header, and its card shows **login needed** with a **log in** button. 2. **log in** opens a dialog, which calls `POST /api/agents//claude-login`. swarm-controller relays the start as a request on the queue subject `$SWARM.claude-login.request`. 3. The minter runs `claude auth login` with `HOME` and `CLAUDE_CONFIG_DIR` in a fresh scratch directory, over plain stdio pipes, and answers with the first `https://` URL it prints. The dialog shows that URL. 4. The operator opens the URL, signs in to the agent's own claude account, and pastes the code the page shows into the dialog, which posts it to `POST /api/agents//claude-login/code`. 5. The minter writes the code to claude's stdin and waits for it to exit. It stores the `.credentials.json` claude wrote at `swarm/agents//claude-credentials` (field `value`), as a new version over any stored before, and removes the scratch directory. 6. The agent's harness, while it waits for a login, reads that leaf every 30 seconds under the agent's own certificate. When the stored version is newer than the one it installed last, it writes the file to `~/.claude/.credentials.json`, records the version, and resumes its turn loop as it does after a login from the agent's web UI. A started login waits 15 minutes for its code, then the minter stops it. Starting another login for the same agent replaces the running one. Each step has a minute to finish; a refused code fails the login, and the dialog shows what claude printed. ## Why the harness takes only a newer version `claude` rotates the refresh token in `.credentials.json` as it uses the session, and the agent can't write the store. After the first refresh the stored copy is stale. A harness that installed it whenever the bytes differed would replace the agent's live session with a stale copy and log the agent out. The harness therefore compares the store's KV version with the last version it installed (`claude-credentials-version` under the harness dir): - it installs a newer version, that is, a new login; - it leaves an equal or older version alone, even when the agent needs a login again, so it never puts back a session that has gone bad; - it reads nothing while the agent has a session. ## Credentials | Principal | Grant | | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `swarm-claude-login`, in its container | `create`, `update` on `swarm/agents/+/claude-credentials`; `read` on `swarm/services//oidc/client` | | the agent, under its own certificate | `read` on its own `swarm/agents//*`, which covers the leaf | The minter has no read on the leaf and nothing else under an agent. Every hive's policy reads `swarm/agents/*`, so every hive can read every agent's claude session, as it can read the GitHub token. The `operatorsOnly` authelia rule on the swarm domain, not the controller, keeps these routes to operators: the controller checks only that the gateway named a user. Without that rule any account authelia admits could store its own claude session as any agent's, so no agent account may exist before the rule reaches the swarm. The minter refuses a step whose agent name isn't an identifier before it makes a directory or runs `claude`. ## Caveat The installed-version record lives under the harness dir and the credentials file under `~/.claude`. If only the record survives a rebuild, the agent ignores the stored version until the operator logs it in again, which writes a newer one. ## What stays The agent's own web UI login (`/login`) still works, unchanged. It goes away once the operator has confirmed the swarm login works.