Watch
0
0
Fork
You've already forked hyperhive
0

agents keep their github token in /run, fetched on every start, not in the state dir

This commit is contained in:
atlas 2026-11-15 02:04:04 +01:00 • committed by mara
commit 4bd02ea14d
4 changed files with 310 additions and 78 deletions

View file

@ -32,7 +32,7 @@ or the token fetch.
swarm-wide switch is the intended control.)
github.com only. The token **value** never touches nix — the agent writes it
to `<state>/github-token` at runtime (see [Provisioning](#provisioning)).
to a file under `/run` at runtime (see [Provisioning](#provisioning)).
## Provisioning
@ -44,13 +44,19 @@ no hive writes it. One token per agent: swarm-controller refuses to link
another until you delete the stored one, and no route hands it back.
The agent's `hive-agent-github-token` unit reads that path under the
agent's own store certificate and writes `<state>/github-token` (`0600`,
owned by the agent), on boot and every two minutes, replacing the file only
when the token changed. It needs a store identity
agent's own store certificate and writes `/run/hive-agent-github-token/token`
(`0400`, owned by the agent), on every start and every two minutes, replacing
the file only when the token changed. It needs a store identity
(`services.hyperhive.agent.bao.addr`); an agent without one gets no token.
The unit never deletes the file: a `github-token` already in place stays
when the store holds none or doesn't answer. Where the token lives and who
reads it: [credentials-reference.md](../../4-implementation/swarm/credentials-reference.md).
`/run` starts empty on every start, so the unit fetches the token anew each
time: an agent whose token you removed from the store has none after its next
start. When the
store doesn't answer at start, the agent boots and runs without a GitHub
token (`gh` and `git push` run unauthenticated, the unit logs one warning),
and the unit asks again until the store answers. Nothing reads the
`<state>/github-token` file an earlier version kept, and nothing deletes it;
it stays on disk, unused. Where the token lives and who reads it: [credentials-reference.md](../../4-implementation/swarm/credentials-reference.md).
## Security

View file

@ -17,9 +17,10 @@ When enabled, the container gets:
Host-scoped, so it never touches the forge (`localhost:3000`) or any
other remote.
Both scripts read the token from `<state>/github-token` **at invocation
time**, so a PAT written (or rotated) mid-session takes effect immediately
— no container rebuild or restart. Until the file exists, `gh` / `git push`
Both scripts read the token from `/run/hive-agent-github-token/token`
(`services.hyperhive.agent.github.tokenFile`) **at invocation
time**, so a PAT fetched (or rotated) mid-session takes effect immediately
— no container rebuild or restart. They never read the state dir. Until the file exists, `gh` / `git push`
fail unauthenticated. The build bakes the token path into the scripts
(rather than reading it from an env var), because claude's Bash tool runs in a
minimal environment that wouldn't carry one.
@ -42,12 +43,13 @@ rather than merely switched off. it's also why this isn't a cargo
feature — a feature would unify across the workspace and cost every
crate its build cache.
At runtime the poller needs the PAT above. No PAT, no polling: the unit
At runtime the poller needs the PAT above, at the path in
`HIVE_GITHUB_TOKEN_FILE` (the same `/run` file). No PAT, no polling: the unit
logs why and exits 0, which is why it's `Restart = on-failure` and
never `always` — a clean exit on a PAT-less agent must not become a
restart loop. The poller waits about ten minutes for the PAT to appear
before it gives up. A path unit (`hive-github-notify.path`) starts the poller
again when `<state>/github-token` changes, so a PAT that lands later still
again when the token file changes, so a PAT that lands later still
enables polling. Once polling, it re-reads the file on every
tick, so it picks up a rotated PAT on the next tick without a restart.

View file

@ -33,7 +33,7 @@ the rest of the cell says how.
| `swarm/agents/<agent>/forge-token` | `swarm-controller`, at agent creation and every 5 minutes | the agent | ✅ when missing or no longer matching the forge | ✅ every 10 minutes |
| `swarm/agents/<agent>/forge/<label>` | `swarm-controller`, when you link an external forge account in the swarm UI | the agent | ❌ your token; delete it and link the label again | ✅ every 2 minutes |
| `swarm/agents/<agent>/gitlab/<label>` | `swarm-controller`, when you link a GitLab account through the API | the agent | ❌ your token; delete it and link the label again | ✅ every 2 minutes; a deleted account leaves the agent's `glab` configuration on the next run |
| `swarm/agents/<agent>/github-token` | `swarm-controller`, when you link a GitHub account in the swarm UI | the agent | ❌ your token; delete it and link again | ✅ every 2 minutes |
| `swarm/agents/<agent>/github-token` | `swarm-controller`, when you link a GitHub account in the swarm UI | the agent, which keeps it in `/run` only; the `<state>/github-token` copy of an earlier version is no longer read | ❌ your token; delete it and link again | ✅ every 2 minutes |
| `swarm/agents/<agent>/backend-env` | `hive-agent-backend-env`, in the agent's container, from the agent's `backendEnvironmentFile` when the leaf holds nothing (field `env`, the file's exact text); or `swarm-controller`, when you set the API-key environment in the swarm UI | the agent | ❌ nothing replaces it without you; a save in the swarm UI overwrites it, including text an agent wrote into an empty leaf; the agent's file stays as it was | ❌ the file in `/run` refreshes every 2 minutes, and the harness and the subagent daemon load it at their next start |
| `swarm/agents/<agent>/claude-credentials` | `swarm-claude-login`, when you log the agent in from its card; one version per login | the agent, while it waits for a login | ❌ log in again | ✅ every 30 seconds while waiting |
| `swarm/hives/<hive>/forge/token` | `swarm-controller`, every 5 minutes | `swarm-controller` and the hive | ✅ when missing or no longer matching the forge | ✅ read on every forge call |

View file

@ -952,10 +952,10 @@ a link dialog closes.
- **link a github account** — `PUT /api/hives/{hive}/agents/{agent}/github-account`
with a personal access token, stored at `swarm/agents/<agent>/github-token`.
One token per agent. The agent's
`hive-agent-github-token` unit fetches it into `<state>/github-token`,
the file its `gh` wrapper, git credential helper and GitHub notification
poller read. A `github-token` already in place stays when the store holds
none. What the token needs and how the agent uses it:
`hive-agent-github-token` unit fetches it into
`/run/hive-agent-github-token/token` at every start, the file its `gh`
wrapper, git credential helper and GitHub notification poller read. A token
removed from the store stops working at the agent's next start. What the token needs and how the agent uses it:
[GitHub accounts](../../2-handbook/external-accounts/github.md).
- **set the API-key environment** — `PUT /api/hives/{hive}/agents/{agent}/backend-env`
with the text of a systemd `EnvironmentFile` (`NAME=VALUE` lines, at most