Sandbox writable-path fixes for Kanban and NixOS
Cluster 26203 · agent/transports. Data as of
2 open items. Fix merged in #28435 on 19 May 2026. Check it covers your case.
The short answer
- Known?
- Yes. 7 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #28435. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
unresolved. That is separate from the fix event. - Still open
- 1 issue and 1 PR.
Next step: read #28435, then check whether your report is the one still open.
Read #28435Pick up a Lab taskThese issues and pull requests address cases where sandboxed environments deny write access to necessary paths, specifically Kanban workspace and board DB roots in the Codex app-server and working directories in the NixOS systemd service module.
Open issues1
- Issue#27941[Bug]: codex_app_server Kanban workers cannot write artifacts to HERMES_KANBAN_WORKSPACE when it differs from cwd
Open PRs1
- PR#34637fix(agent): grant Kanban workspace to Codex sandbox
Fixes on record
-
#27557 fix(codex): scope kanban worker writable root in app-server sandbox (salvage #26212)
files:
agent/transports/codex_app_server.py,scripts/release.py,tests/agent/transports/test_codex_app_server_runtime.py,website/docs/user-guide/features/codex-app-server-runtime.mdShow the diff
-
agent/transports/codex_app_server.py@@ -74,12 +74,43 @@ def __init__( env: Optional[dict[str, str]] = None, ) -> None: self._codex_bin = codex_bin - cmd = [codex_bin, "app-server"] + list(extra_args or []) spawn_env = os.environ.copy() if env: spawn_env.update(env) if codex_home: spawn_env["CODEX_HOME"] = codex_home + + app_server_args = list(extra_args or []) + # Kanban workers must be able to write their handoff/status back to + # the board DB, which lives outside the per-task workspace. Keep the + # Codex sandbox on, but add the Kanban root as the only extra writable + # root. Without this, codex-runtime workers finish their actual work + # but crash/block when kanban_complete/kanban_block writes SQLite. + if spawn_env.get("HERMES_KANBAN_TASK"): + kanban_db = spawn_env.get("HERMES_KANBAN_DB") + kanban_root = ( + os.path.dirname(kanban_db) + if kanban_db + else spawn_env.get( + "HERMES_KANBAN_ROOT", + os.path.join( + spawn_env.get("HERMES_HOME", os.path.expanduser("~/.hermes")), + "kanban", + ), + ) + ) + app_server_args.extend( + [ + "-c", + 'sandbox_mode="workspace-write"', + "-c", + f'sandbox_workspace_write.writable_roots=["{kanban_root}"]', + "-c", + "sandbox_workspace_write.network_access=false", + ] + ) + + cmd = [codex_bin, "app-server"] + app_server_args # Codex emits tracing to stderr; default WARN keeps it quiet for users. spawn_env.setdefault("RUST_LOG", "warn") -
scripts/release.py@@ -1159,6 +1159,8 @@ "[email protected]": "bird", # PR #25219 (gateway docker exit-75 restart) "[redacted:email]": "davidcampbelldc", # PR #26834 (web_server proxy_headers=False) "[email protected]": "davidcampbelldc", + "[redacted:email]": "hehehe0803", # PR #26212 salvage (codex kanban writable root) + "[email protected]": "hehehe0803", } -
tests/agent/transports/test_codex_app_server_runtime.py@@ -241,3 +241,58 @@ def kill(self): assert captured["env"].get("CODEX_HOME") == "/tmp/profile/codex" # And HOME still passes through unchanged assert captured["env"].get("HOME") == "/users/alice" + + def test_kanban_worker_adds_only_kanban_writable_root(self, monkeypatch): + """Codex-runtime Kanban workers need to write board state outside + their scratch/worktree workspace, but should not fall back to + danger-full-access. Hermes passes a narrow app-server config override + for the Kanban root only. + """ + import subprocess + from agent.transports import codex_app_server as cas + + captured = {} + + class FakePopen: + def __init__(self, cmd, *args, **kwargs): + captured["cmd"] = list(cmd) + captured["env"] = kwargs.get("env", {}).copy() + self.stdin = None + self.stdout = None + self.stderr = None + self.pid = 1 + self.returncode = None + + def poll(self): + return None + + def terminate(self): + pass + + def wait(self, timeout=None): + return 0 + + def kill(self): + pass + + monkeypatch.setattr(subprocess, "Popen", FakePopen) + monkeypatch.setenv("HOME", "/users/alice") + monkeypatch.setenv("HERMES_HOME", "/users/alice/.hermes/profiles/backend-worker") + monkeypatch.setenv("HERMES_KANBAN_TASK", "t_smoke") + monkeypatch.setenv( + "HERMES_KANBAN_DB", + "/users/alice/.hermes/kanban/boards/smoke/kanban.db", + ) + + client = cas.CodexAppServerClient(codex_bin="codex") + client._closed = True + + cmd = captured["cmd"] + assert cmd[:2] == ["codex", "app-server"] + assert 'sandbox_mode="workspace-write"' in cmd + assert ( + 'sandbox_workspace_write.writable_roots=["/users/alice/.hermes/kanban/boards/smoke"]' + in cmd + ) + assert "sandbox_workspace_write.network_access=false" in cmd + assert all("danger" not in part for part in cmd) -
website/docs/user-guide/features/codex-app-server-runtime.md@@ -91,11 +91,11 @@ What works inside a codex-runtime worker: - The Hermes tool callback for browser_*, vision, image_gen, skills, TTS What also works because the MCP callback exposes them: -- **`kanban_complete` / `kanban_block` / `kanban_comment` / `kanban_heartbeat`** — the worker handoff tools. These read `HERMES_KANBAN_TASK` from env (set by the dispatcher), gate access correctly, and write to `~/.hermes/kanban.db`. Without these in the callback, a worker on this runtime could do its task but couldn't report back, hanging until the dispatcher's timeout. +- **`kanban_complete` / `kanban_block` / `kanban_comment` / `kanban_heartbeat`** — the worker handoff tools. These read `HERMES_KANBAN_TASK` from env (set by the dispatcher), gate access correctly, and write to the per-board SQLite DB pinned by `HERMES_KANBAN_DB`. Without these in the callback, a worker on this runtime could do its task but couldn't report back, hanging until the dispatcher's timeout. - **`kanban_show` / `kanban_list`** — read-only board queries for the worker to check its own context. - **`kanban_create` / `kanban_unblock` / `kanban_link`** — orchestrator-only operations. Available for orchestrator agents running on the codex runtime that need to dispatch new tasks. -The kanban tools are gated by `HERMES_KANBAN_TASK` env var the dispatcher sets — that var is propagated to the codex subprocess (codex inherits env) and from there to the spawned `hermes-tools` MCP server subprocess. So the tools see the right task id and gate correctly. +The kanban tools are gated by `HERMES_KANBAN_TASK` env var the dispatcher sets — that var is propagated to the codex subprocess (codex inherits env) and from there to the spawned `hermes-tools` MCP server subprocess. So the tools see the right task id and gate correctly. For Codex app-server workers, Hermes also passes narrow app-server sandbox overrides when `HERMES_KANBAN_TASK` is present: keep `workspace-write` sandboxing, add only the current board directory (derived from `HERMES_KANBAN_DB`) as an extra writable root, and keep network disabled by default. This avoids the brittle `:danger-no-sandbox` workaround while letting `kanban_complete` / `kanban_block` update the board DB. ### Cron jobs
-
-
#28435 fix(codex_app_server): include every Kanban-pinned path in writable_roots (#27941) (#27950)
files:
website/docs/user-guide/features/codex-app-server-runtime.md
2 PRs closed without merging
From the close comments: done elsewhere 1 · merged via another change 1
Closed issues · 1
- Closed#26203Codex app-server Kanban workers cannot complete tasks under sandbox without board DB writable root
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.26: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the Lab