docs: move the socket-dir ownership rule to boundary.md

It was written into gateway.md, which only owns half the story: that
doc describes the web.sock path, and before this branch it never
mentioned agent.sock at all. Putting a rule shared by hive-c0re, the
harness and nginx inside the gateway walkthrough means someone asking
"why can't c0re dial agent.sock" has no reason to look there.

boundary.md already covers who may touch what across the trust boundary
-- including the sibling case of hive-priv's socket getting its mode
from the unit rather than the process -- so the rule lives there now,
with the three principals as a table. gateway.md keeps a two-line note
about the one fact it needs (nginx traverses via o=--x) and links out.
This commit is contained in:
atlas 2026-08-04 00:45:58 +02:00 committed by mara
commit 289db00321
2 changed files with 39 additions and 17 deletions

View file

@ -94,6 +94,40 @@ couldn't connect the way the socket unit's `SocketGroup` grant
intends. Requiring socket activation everywhere means dev and prod
take the exact same path and the group grant always holds.
### the per-agent socket dir
`/run/hive-agent/<name>/` is shared by **three principals that share no
group**, which is why its mode is what it is:
| principal | reaches | needs |
|---|---|---|
| the agent's harness | binds + unlinks `agent.sock`, `web.sock` | owner, `rwx` |
| `hive-c0re` | dials `agent.sock` (todo wakes) | traverse |
| the gateway's nginx | dials `web.sock` | traverse |
The last two land in "other", so the dir is **`0751`, owned by the
agent's container uid/gid** — `o=--x` is traverse without listing, and
both sockets are `0666`, which is all a dialer needs.
**Ownership is declared, not repaired.** The tmpfiles.d entry written by
`SyncAgentTmpfiles` names the uid/gid directly. Do not add a chown
alongside it: `d` re-applies on every boot *and* every agent
spawn/destroy, so ownership set afterwards is reverted the next time any
agent changes — which is exactly how this dir spent a long time at
`0777 root root` while a privileged chown appeared to be fixing it.
The mode is load-bearing, not cosmetic. Write permission on a
*directory* is what confers the right to unlink its entries, whoever owns
them, and the sticky bit is the only thing that would restrain that (it
is not set here). A world-writable socket dir therefore lets anything
able to reach the path delete an agent's socket and bind its own — and
the gateway container has all of `/run/hive-agent` bind-mounted in.
Dropping `o=w` removes that permission rather than qualifying it.
⚠️ Contrast `/shared`, which *is* sticky world-writable (`1777`): it has
many legitimate writers, so sticky is the best available answer there.
This dir has exactly one writer, so it needs no world write at all.
### host admin socket access (`hivectl`)
`hivectl` drives the whole hive — spawn / kill / destroy / rebuild /