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*"; }; }; }