From af5dce0648ed3860b2d4396a2038b9c59d1c43bb Mon Sep 17 00:00:00 2001 From: atlas Date: Thu, 3 Sep 2026 20:44:11 +0200 Subject: [PATCH] matrix: scope the daemon's token watcher to this agent's own state dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `hive-matrix-daemon.path` globbed `/agents/*/state/matrix-token*`. Every agent's state dir is visible from inside every container, so the condition is satisfied by a sibling's token. That is reachable, not cosmetic. The daemon deliberately exits 0 when it has no token of its own — `Restart = "on-failure"` therefore does not restart it, and the unit sits inactive, which is the state the path unit exists for. In that state a sibling's token keeps the glob satisfied: the path fires, the daemon exits 0, the unit deactivates, the path re-arms, the condition is still true. systemd.path(5) activates a `PathExists`-family condition that already holds immediately on arming, so it repeats until the start limit stops it. Scoped to this agent, the condition is false exactly when the daemon would have nothing to do. The glob is quoted in four other places, all of which would otherwise name a pattern that no longer exists — a doc, a Rust doc-comment in hive-c0re, a nix comment, and an assertion message an operator reads. Each is reworded to the basename (`matrix-token*` in this agent's state dir), which is what the assertion actually enforces via `baseNameOf`, so they stay true wherever the directory moves. Refs #4030. --- docs/agent-lifecycle/persistence.md | 4 ++-- hive-c0re/src/workers/credential.rs | 5 +++-- nix/agent-modules/matrix.nix | 18 ++++++++++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/agent-lifecycle/persistence.md b/docs/agent-lifecycle/persistence.md index 7778acd4..9513568c 100644 --- a/docs/agent-lifecycle/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -553,8 +553,8 @@ auto-injected `extraMcpServers.matrix` entry read). **First-boot ordering**: hive-c0re provisions the matrix token AFTER agent containers come up. Without the path-trigger sibling (`systemd.paths.hive-matrix-daemon`, `PathExistsGlob = -/agents/*/state/matrix-token*` — the trailing `*` also catches a -secondary multi-account token like `matrix-token-ccc`), the daemon +/matrix-token*` — the trailing `*` also catches +a secondary multi-account token like `matrix-token-ccc`), the daemon would exit 0 quietly the first time it ran and the MCP would have no backend until the next restart. The `.path` unit makes the appearance of the token re-fire the service so the daemon comes alive in the diff --git a/hive-c0re/src/workers/credential.rs b/hive-c0re/src/workers/credential.rs index 2b8856dc..42e4c643 100644 --- a/hive-c0re/src/workers/credential.rs +++ b/hive-c0re/src/workers/credential.rs @@ -5,8 +5,9 @@ //! account; this reads the value out of the store and writes it where the //! agent's matrix daemon already watches for it. Nothing here activates //! anything: `nix/agent-modules/matrix.nix` has a `systemd.paths` unit -//! globbing `/agents/*/state/matrix-token*` that re-fires the daemon when a -//! token appears, so arrival is the whole trigger. +//! globbing `matrix-token*` inside that agent's own state dir, which +//! re-fires the daemon when a token appears, so arrival is the whole +//! trigger. //! //! 🔑 The notice carries no secret — see [`swarm_queue_client::credential_subject`] //! for why that is a requirement rather than a preference. The value is read diff --git a/nix/agent-modules/matrix.nix b/nix/agent-modules/matrix.nix index 83e93114..691a2005 100644 --- a/nix/agent-modules/matrix.nix +++ b/nix/agent-modules/matrix.nix @@ -191,8 +191,9 @@ in + "--- that name is reserved for the hive-internal account."; } # Token files must land at the `matrix-token*` name the daemon - # path-watcher globs (`/agents/*/state/matrix-token*`), or the account - # never gets picked up live (it loads only on a full daemon restart). + # path-watcher globs (`matrix-token*` inside this agent's own state + # dir), or the account never gets picked up live (it loads only on a + # full daemon restart). # Enforce the basename prefix so a deviating name (e.g. # `matrix-catgirl-token`) is caught at build time, not silently. { @@ -202,7 +203,7 @@ in message = "every hyperhive.matrixAccounts..tokenFile basename must start with " + "\"matrix-token\" so the daemon path-watcher glob " - + "(/agents/*/state/matrix-token*) picks it up live. Offending: " + + "(matrix-token* in the agent's state dir) picks it up live. Offending: " + lib.concatStringsSep ", " ( lib.mapAttrsToList (n: a: "${n}=${baseNameOf a.tokenFile}") ( lib.filterAttrs ( @@ -311,7 +312,16 @@ in # `matrix-token*` (not just `matrix-token`) so a secondary # multi-account token (e.g. `matrix-token-ccc`) landing also # re-fires the daemon to pick up the freshly-provisioned account. - pathConfig.PathExistsGlob = "/agents/*/state/matrix-token*"; + # + # ⚠️ This agent's own state dir, not a glob over `/agents/*/`. Every + # agent's state dir is visible from inside every container, so a + # wildcard is satisfied by a sibling's token — and this daemon exits 0 + # when it has no token of its own (see `Restart = "on-failure"` + # above), so the unit deactivates, the path unit re-arms, the + # sibling's token still matches, and it fires again until the start + # limit stops it. Scoped to this agent, the condition is false exactly + # when the daemon would have nothing to do. + pathConfig.PathExistsGlob = "/agents/${userName}/state/matrix-token*"; }; }; }