fix(sites): don't list the snippet dir in ReadWritePaths when web.enable is off - #198
Merged
Conversation
7b687b9 added /var/lib/agent-box-sites/<user> to the agent unit's ReadWritePaths unconditionally, but the tmpfiles rule that creates that directory lives inside `mkIf (cfg.enable && cfg.web.enable)`. web.enable is a mkEnableOption, so on a default box the path never exists and systemd fails the whole namespace setup: agent-box-agent.service: Failed to set up mount namespacing: /var/lib/agent-box-sites/agent: No such file or directory agent-box-agent.service: Failed at step NAMESPACE ... 226/NAMESPACE The unit then never starts at all — no tmux sessions on any web-less deployment — and it restart-loops (counter passed 427 in CI). This broke tests/memory-protection.nix on master, the one test that runs with web off, which is how it surfaced. Guard the entry on cfg.web.enable, matching what gates the tmpfiles rule. Deliberately still no `-` prefix: a dir that is genuinely missing while web IS on should stay loud rather than silently reverting to the EROFS the parent commit fixed. memory-protection now asserts the entry is absent, so the web-off case has an explicit guard instead of only failing via wait_for_unit. Verified by evaluating both node configs: web off -> ["/home/agent"] web on -> ["/home/agent" "/var/lib/agent-box-sites/agent"] Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Kq2nqcWR5Y6aryf8WzWbs
This was referenced Aug 5, 2026
…essions The standing watch on this box put four hook-* sessions on defangdevs/agent-box this afternoon for a repo with nothing wrong: merging #189 emitted a green check_run.completed per job, a green workflow_run, a Pages deployment, and a run the concurrency group cancelled. Each session read CI, concluded "nothing to do", and held one of the four hook-session slots that a real failure needs. 0.10.0 was supposed to have stopped that — its own tool description, INSTRUCTIONS and the agent-box-webhook --deliver-to help all say a CI event spawns only on a FAILURE. The implementation only used the outcome to decide whether a CI event could override ignoreSenders, so with the sender not on the ignore list nothing was gated. An ignore list names the box's own identity (`defangdevs`), while CI is triggered by whoever pushed (`lionello`), so on this box the brake was never reached. local-channels 0.10.1 moves the verdict into dispatch_event: a CI outcome that is not a terminal failure never spawns, whoever triggered it. A failure still overrides an ignored sender, and session delivery is untouched. - `webhook.rev` / `webhook.sha256` → local-channels aec22b5 (0.10.1), and `modules/agent-box.nix` regenerated from the `.in` source. - `tests/webhook.nix` gains a leg for it, placed where neither other brake can explain the result: the owning session has already handed the topic back and the sender is on no ignore list, so only the outcome can hold the spawn. It asserts the suppression log line and that no hook-* session appears — the leg fails against a pre-0.10.1 pin, which is what makes it a pin test. Not covered here: the session plugin cache still carries its own copy of webhook.py (#193), so a session's MCP peer keeps running 0.10.0 until that is bumped too. Only the dispatcher's copy decides spawns, so the box gets the fix from this pin alone. ## Verification - `nix run .#assemble` regenerated the module; `nix build` of the five aarch64-evaluable checks (module-generated-up-to-date, module-single-file, webhook-route, download-route, multi-user) passes natively. - `nix eval .#checks.x86_64-linux.webhook.drvPath` resolves, so the new test script is valid Nix, and `check-testscript.sh tests/webhook.nix` passes both the driver's ty and ruff gates. - Upstream fix landed as defangdevs/local-channels#12 with 60 unit + e2e tests green on python 3.9 and 3.x, mutation-checked. Claude-Session: https://claude.ai/code/session_0195u4LJGkuYGkxCQQYEXEK1 Co-authored-by: defangdevs <defangdevs@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
chore(webhook): pin local-webhook 0.10.1 so green CI stops spawning sessions
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Master is red and, more importantly, a default (web-less) agent-box no longer starts any agent sessions.
What broke
7b687b9 (the fix for #195,
~/sitesbeing read-only in the agent's own namespace) added/var/lib/agent-box-sites/<user>to the agent unit'sReadWritePathsunconditionally. The tmpfiles rule that creates that directory lives insidemkIf (cfg.enable && cfg.web.enable), andweb.enableis amkEnableOption— default off. With web off the path never exists, andProtectSystem=strictnamespace setup fails hard:The unit never starts — no tmux sessions at all — and restart-loops (the counter passed 427 in the CI run).
tests/memory-protection.nix, the only test that runs with web off, timed out onwait_for_unit: https://github.com/defangdevs/agent-box/actions/runs/31016454644The parent commit's reasoning ("tmpfiles-created at sysinit, long before this unit, so no
-prefix") is right about ordering — it just doesn't hold when the rule isn't emitted at all.The fix
Guard the entry on
cfg.web.enable, mirroring what gates the tmpfiles rule.Deliberately still no
-prefix: a directory that is genuinely missing while web is on should fail loudly rather than silently reverting to the EROFS that #195 was about.Verification
Both node configs evaluated directly, no VM boot needed:
ReadWritePathsmemory-protection)["/home/agent"]self-serve-domain)["/home/agent" "/var/lib/agent-box-sites/agent"]So the #195 flow keeps working where it applies, and the web-off box boots again.
memory-protectionnow asserts the entry is absent, so the web-off case has an explicit guard instead of only failing indirectly viawait_for_unit.Also run natively on aarch64:
module-generated-up-to-date(regeneratedmodules/agent-box.nixfrom.nix.in), thety/rufftestScript gate, and adrvPatheval of all seven VM tests.Note: the deploy-test leg is expected-red on master for unrelated reasons (#186/#190).
Opened from a webhook triage session that picked up the #195 close event.
🤖 Generated with Claude Code
https://claude.ai/code/session_014Kq2nqcWR5Y6aryf8WzWbs