drop the config and /applied mounts: an agent reads its config by cloning agent-configs, and the prompt says so
This commit is contained in:
parent
a781addcfe
commit
3c09e14a76
3 changed files with 103 additions and 288 deletions
|
|
@ -100,10 +100,9 @@ state dirs work as for every other agent.
|
|||
- **Storage/mounts** — the `manage_root_agent` permission, which the swarm
|
||||
UI's setup assistant creates ruth with, gets a container
|
||||
`/var/lib/hyperhive/agents` bind-mounted RW at `/agents` (so it can
|
||||
manage any agent's state dir — config isn't authored there, since a
|
||||
real config change is a PR from a clone), plus RO mounts for
|
||||
`/applied` (diff against what's deployed) and `/meta` (the hive's
|
||||
topology and limits files). Any agent holding it gets the same mounts.
|
||||
manage any agent's state dir; config isn't mounted, since a real
|
||||
config change is a PR from a clone), plus an RO mount for
|
||||
`/meta` (the hive's topology and limits files). Any agent holding it gets the same mounts.
|
||||
- **Prompt** — identical for every container but one block (below):
|
||||
`prompts/system.md`
|
||||
carries `<!-- role:agent -->` / `<!-- role:manager -->` marker
|
||||
|
|
|
|||
|
|
@ -24,10 +24,8 @@ No MCP tool exists for config changes: opening the PR IS the request.
|
|||
tracked path, but `agent.nix` is the contract entry point), commits
|
||||
with its own git identity, and pushes a branch + opens a PR with
|
||||
`hive-forge` — the same way it would change any other repo.
|
||||
The bind-mounted `/agents/<name>/config/` is a **copy for reading** a
|
||||
config, not the tree to edit: authoring in place there produces no PR.
|
||||
(it's currently mounted read-write, which is a defect tracked
|
||||
separately, not an authoring path.)
|
||||
The container has no config mount: the clone is how an agent reads its
|
||||
own config as well as how it changes it.
|
||||
Branch protection (the agent isn't on the `main` push allowlist;
|
||||
merge and approvals allowlist = `operators` team; see "Forge config repos"
|
||||
below) makes the agent a write collaborator that **can't merge its own
|
||||
|
|
@ -205,8 +203,8 @@ the swarm builder built the agent from the updated repo (the
|
|||
hive-c0re seeds the applied repo at first spawn from the forge's
|
||||
`agent-configs/<name>` `main`, or from a template when the forge has none yet,
|
||||
and fast-forwards it to each merged config commit that deploys. An agent never
|
||||
edits it: the agent sees it read-only at `/agents/<name>/config`, so it can
|
||||
read what governs it, while a config change is a PR against its forge repo.
|
||||
edits it: it isn't mounted into any container, and a config change is a PR
|
||||
against the agent's forge repo.
|
||||
Older hives also kept a *proposed* clone at `agents/<name>/config`. Nothing
|
||||
reads it any more, and you can delete it.
|
||||
|
||||
|
|
@ -247,22 +245,17 @@ the forge. Branch protection keeps the agent off the `main` push
|
|||
allowlist and allowlists merging to the `operators` team only, so an
|
||||
agent can't fast-forward its own config or self-merge its PR (see
|
||||
[Config changes](#config-changes) above). hive-c0re passes the credential to
|
||||
each `git` call inline, never writing it into
|
||||
`applied/<n>/.git/config`; that repo is RO-bind-mounted into the root
|
||||
agent, and a stored token would leak the hive's forge credential to an
|
||||
agent.
|
||||
each `git` call inline, never writing it into `applied/<n>/.git/config`, so
|
||||
no stored token sits in a repo an agent might one day read.
|
||||
|
||||
The dashboard deep-links into this org — a `config repo` link
|
||||
per container row. See `docs/web-ui/dashboard.md`.
|
||||
|
||||
### Submitting agent's view of config repos
|
||||
|
||||
An agent holding `ManageRootAgent` has every other agent's config repo
|
||||
bind-mounted **read-only** (`hive-c0re/src/lifecycle/host_config.rs`
|
||||
calls `bind_child_agent_dirs` for each entry in
|
||||
`topology::all_agents()`). it's a copy to *read* another agent's
|
||||
current config — not an editing surface. An agent without the
|
||||
permission sees no other agent's config at all.
|
||||
No agent has a config repo mounted, not even one holding
|
||||
`ManageRootAgent`: reading another agent's config means cloning its repo
|
||||
from the forge, subject to the forge's own access rules.
|
||||
|
||||
An agent submits a change to another agent's config the same way it
|
||||
makes any other change: **clone the target agent's config repo from the
|
||||
|
|
@ -276,10 +269,8 @@ the swarm's `agent-permissions` record; see
|
|||
mounts via `set_nspawn_flags`:
|
||||
|
||||
- `/var/lib/hyperhive/agents/<n>/state` → `/agents/<n>/state` (RW) for
|
||||
**every** agent, and `/agents/<n>/config` (RO, the applied repo). The
|
||||
permission means "may manage any agent," so the mounted set is every agent.
|
||||
- `/var/lib/hyperhive/applied/` → `/applied/` (RO) — every agent's
|
||||
authoritative applied repo, including `.git`.
|
||||
**every** agent. The permission means "may manage any agent," so the mounted
|
||||
set is every agent.
|
||||
- `/var/lib/hyperhive/meta/` → `/meta/` (RO) — the hive's plain state
|
||||
(topology, swarm limits).
|
||||
|
||||
|
|
@ -297,18 +288,6 @@ permissions were unset, swarm-controller writes the default plus
|
|||
`manage_root_agent` into her key once at start, so she keeps her mounts;
|
||||
it never touches a key that's already set.
|
||||
|
||||
With the full `/applied` mount, an agent can read what hive-c0re deployed,
|
||||
failed or denied for any agent:
|
||||
|
||||
```sh
|
||||
git -C /applied/<n> log main --oneline
|
||||
git -C /applied/<n> show refs/tags/deployed/0 # the seed commit
|
||||
git -C /applied/<n> show refs/tags/denied/<id> # body = operator note
|
||||
```
|
||||
|
||||
The RO binds block push at the kernel level — git plumbing inside the
|
||||
container can't corrupt either authoritative repo.
|
||||
|
||||
## Startup migrations (older hosts)
|
||||
|
||||
hive-c0re runs a couple of idempotent migrations on every startup so a
|
||||
|
|
@ -338,10 +317,9 @@ The root agent container runs through the **same lifecycle as
|
|||
sub-agents**. Nothing creates it at hive boot: you create ruth from the
|
||||
swarm dashboard like any agent, and an existing ruth container starts with
|
||||
the hive like every other. The root agent's flake lives at
|
||||
`/var/lib/hyperhive/applied/ruth/`. The root agent can read its own
|
||||
`agent.nix` (visible inside the container at `/agents/ruth/config/`)
|
||||
and open a config PR on `agent-configs/ruth` for operator review,
|
||||
same as any other agent.
|
||||
`/var/lib/hyperhive/applied/ruth/`. The root agent can clone
|
||||
`agent-configs/ruth` to read its own `agent.nix` and open a config PR there
|
||||
for operator review, same as any other agent.
|
||||
|
||||
Differences from sub-agents:
|
||||
|
||||
|
|
@ -352,11 +330,9 @@ Differences from sub-agents:
|
|||
- Web UI port via `lifecycle::agent_web_port("ruth")` — same
|
||||
FNV-1a hash as every other agent (8100..8999 range).
|
||||
- The root agent holds `manage_root_agent` (the swarm UI's setup
|
||||
assistant creates her with it), for which `set_nspawn_flags` adds two extra binds:
|
||||
`/var/lib/hyperhive/agents` → `/agents` (RW) for every agent's state,
|
||||
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent
|
||||
can `git fetch` deployed/failed/denied tags from any agent's
|
||||
authoritative applied repo (see "Root-agent view of applied" below).
|
||||
assistant creates her with it), for which `set_nspawn_flags` adds extra
|
||||
binds: every agent's `state` dir (RW) under `/agents/<n>/state`, and the
|
||||
hive's plain state under `/meta` (RO).
|
||||
- Its socket comes from the generic per-agent registration, in the same
|
||||
per-agent runtime dir as any other agent (`/run/hyperhive/agents/ruth/`),
|
||||
not a separate manager-only path.
|
||||
|
|
|
|||
|
|
@ -255,8 +255,8 @@ than an accident of storage.
|
|||
Under `/var/lib/hyperhive/agents/<name>/`:
|
||||
|
||||
- `config/` — unused. Older hives kept a proposed nix repo here; nothing
|
||||
reads it any more, and you can delete it. The agent's config mount at
|
||||
`/agents/<name>/config` is the applied repo, read-only.
|
||||
reads it any more, and you can delete it. An agent reads its config by
|
||||
cloning `agent-configs/<name>` from the forge.
|
||||
- `claude/` — claude OAuth credentials, bind-mounted RW to
|
||||
`/home/<name>/.claude` inside the container.
|
||||
- `state/` — durable notes and `hyperhive-harness.json`. Bind-mounted
|
||||
|
|
@ -459,38 +459,25 @@ the lines written after the last push.
|
|||
### Cross-agent access to state
|
||||
|
||||
An agent holding the `manage_root_agent` permission gets every other
|
||||
agent's `state` dir bind-mounted **read-write** and its `config` dir
|
||||
**read-only** (`bind_child_agent_dirs` in `lifecycle/host_config.rs`).
|
||||
The RW on `state` is deliberate, not an oversight: the holder recovers
|
||||
other agents, which includes writing into their state (for example
|
||||
seeding notes, clearing a stuck sentinel) as well as reading it.
|
||||
agent's `state` dir bind-mounted **read-write** (`bind_child_agent_dirs` in
|
||||
`lifecycle/host_config.rs`). The RW is deliberate, not an oversight: the
|
||||
holder recovers other agents, which includes writing into their state (for
|
||||
example seeding notes, clearing a stuck sentinel) as well as reading it.
|
||||
|
||||
This is the **only** cross-agent mount. An agent without that permission
|
||||
sees its own dirs and nothing else.
|
||||
|
||||
**`harness` isn't mounted at all.** It holds that agent's own runtime
|
||||
material — `bash-tasks/`, the turn-stats and event sqlite dbs — and
|
||||
material - `bash-tasks/`, the turn-stats and event sqlite dbs - and
|
||||
nothing argues for anyone else reading it, let alone writing it.
|
||||
hive-c0re reads a harness dir **directly on the host** when it wants
|
||||
those stats, which needs no mount into another container.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
**`config` is read-only, including for the holder.** A config change is
|
||||
a PR on that agent's config repo, made from a clone and merged after
|
||||
review — so the bind-mounted `config` dir is a *copy to read*, never a
|
||||
tree anyone edits in place. Mounting it writable would leave a second
|
||||
path to the same file that skips the review entirely, which makes the
|
||||
boundary a convention rather than a permission.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
⚠️ Don't confuse it with the config-repo seeding hive-c0re does at
|
||||
spawn (`lifecycle::setup_applied`): that writes the agent's initial
|
||||
config repo as **hive-c0re, against the host path**, and `read_only` on a bind
|
||||
constrains writers *inside* a container only. The two are unrelated —
|
||||
conflating them can lead you to reason your way into thinking this
|
||||
mount should be writable when it shouldn't.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
**`config` isn't mounted either.** A config change is a PR on that agent's
|
||||
config repo, made from a clone and merged after review. Anyone who wants to
|
||||
read the config clones `agent-configs/<name>` from the forge. A mount, even a
|
||||
read-only one, would show an agent a tree that's not the one that governs it
|
||||
whenever the forge has moved on.
|
||||
|
||||
Isolation still holds by default: a container has its *own* dirs and,
|
||||
unless it holds the permission, nothing else.
|
||||
|
|
|
|||
Loading…
Reference in a new issue