From be3a3af0931ac19bb0abf6b8b23e3546fbb35d2e Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 27 Oct 2026 22:12:52 +0100 Subject: [PATCH] say that the backend env leaf and file are agent-writable, and why that is accepted --- docs/turn-loop/config.md | 6 +++++- nix/agent-modules/agent-service.nix | 5 ++++- nix/agent-modules/backend-env.nix | 6 ++++++ swarm-secret-client/src/policy.rs | 9 +++++++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/turn-loop/config.md b/docs/turn-loop/config.md index aa8cd24b..2a8cf6aa 100644 --- a/docs/turn-loop/config.md +++ b/docs/turn-loop/config.md @@ -305,7 +305,11 @@ The file lives in the agent's bind-mounted **harness** dir, not `state/` — `harness/` survives container rebuilds exactly like `state/` does, but (unlike `state/`) is never bind-mounted into a parent agent's container (see `docs/agent-lifecycle/persistence.md`'s "Parent access to child state"), so an API -key placed here is reachable by nothing but this agent and the host. +key placed here is reachable by nothing but this agent and the host. The +file doesn't stay root-owned, though: the container's activation chowns the +whole harness dir to the agent, so after the next boot the file is agent-owned +whatever owner you installed it with, and the agent can rewrite it. That holds +until the swarm secret store replaces the file. diff --git a/nix/agent-modules/agent-service.nix b/nix/agent-modules/agent-service.nix index ee3dc284..0061386b 100644 --- a/nix/agent-modules/agent-service.nix +++ b/nix/agent-modules/agent-service.nix @@ -192,7 +192,10 @@ in read-write for recovery — see `docs/agent-lifecycle/persistence.md`'s "Cross-agent access to state"), so this credential is reachable by nothing but this agent and the host. Permissions should be `0600`, owned by the agent's - unix user. Loaded with a leading `-` (optional `EnvironmentFile`), + unix user. Whatever owner it was installed with, the file becomes + agent-owned at the container's next boot, so the agent can rewrite it; + it is no boundary the agent can't cross until the swarm secret store + replaces the file. Loaded with a leading `-` (optional `EnvironmentFile`), so a path that doesn't exist yet — an operator setting this option before creating the file, or a fresh host rebuild before state is restored — makes systemd skip it rather than refuse to start the diff --git a/nix/agent-modules/backend-env.nix b/nix/agent-modules/backend-env.nix index bd4dace4..806fab19 100644 --- a/nix/agent-modules/backend-env.nix +++ b/nix/agent-modules/backend-env.nix @@ -18,6 +18,12 @@ # systemd lets a later file override an earlier one, so the store's value wins # when there is one, and the file carries the agent when the store is down. # +# The leaf's create grant cannot constrain content, so while the leaf is empty +# the agent's own process can also create it with any text, and the store's +# value then wins over the file. That is no wider than the file the agent +# already owns: `./user.nix`'s `heal_own_dir` chowns the harness dir to the +# agent at every activation, a gap the issue tracker has an open bug for. +# # The unit runs as root because the file is root-owned `0600` where an operator # placed it. Root can still read the agent's `0400` store identity. { diff --git a/swarm-secret-client/src/policy.rs b/swarm-secret-client/src/policy.rs index d1e4fd11..4303cfd4 100644 --- a/swarm-secret-client/src/policy.rs +++ b/swarm-secret-client/src/policy.rs @@ -205,8 +205,13 @@ const ENTITY_NAME_TEMPLATE: &str = "{{identity.entity.name}}"; /// /// The one write is `create` on the agent's own [`backend_env`] leaf, without /// `update`: the agent seeds it from the file it has today, once, and cannot -/// replace it. Any other write would let an agent hand itself an identity it -/// was never issued. An agent has no business with another agent's +/// replace it. The stanza cannot constrain the content, and the agent's own +/// process holds the identity, so while the leaf is empty it can also create it +/// with any text, and the store's value then wins over the file. That is no +/// wider than the harness-dir file the agent already owns: +/// `nix/agent-modules/user.nix`'s `heal_own_dir` chowns that dir to the agent at +/// every activation, a gap the issue tracker has an open bug for. Any other +/// write would let an agent hand itself an identity it was never issued. An agent has no business with another agent's /// credentials, a hive's, a service's, or the controller's, so none of /// [`render_shared_hives`]'s breadth applies. A hive still reads this /// agent's secrets, and closing that is its own decision.