back out bypassPermissions: claude refuses it under root uid

claude-code rejects --dangerously-skip-permissions / defaultMode=
bypassPermissions when running as root, which all hyperhive
containers do. revert to the previous explicit allow-list plumbing
(per-flavor list spliced into permissions.allow + --tools enable
list), keep TodoWrite out of the built-in allow set, and keep the
deny list (TodoWrite, WebFetch, WebSearch, Task) as belt-and-braces
in case anything sneaks past the allow gate.
This commit is contained in:
müde 2026-05-16 15:58:41 +02:00
parent 36c7f3d1c7
commit 7ec658851a
3 changed files with 82 additions and 27 deletions

View file

@ -22,13 +22,9 @@ use crate::mcp;
/// to read and edit; we ship it via `include_str!`. We turn off claude's
/// in-session auto-compaction and its cross-session auto-memory because
/// hyperhive owns those concerns (`/compact` on overflow, notes
/// persistence under `/state`). `permissions.defaultMode =
/// bypassPermissions` skips the per-tool approval prompt entirely;
/// `permissions.deny` keeps a short list of tools we don't want claude
/// reaching for (web egress, nested agents, the ephemeral todo list).
/// Unknown keys are silently ignored by claude-code; if a key gets
/// renamed we'll spot it because the corresponding behavior will start
/// firing mid-turn again.
/// persistence under `/state`). Unknown keys are silently ignored by
/// claude-code; if a key gets renamed we'll spot it because the
/// corresponding behavior will start firing mid-turn again.
const CLAUDE_SETTINGS: &str = include_str!("../prompts/claude-settings.json");
/// Regex-ish marker claude-code emits when context overflows. Same string
@ -85,10 +81,7 @@ pub async fn write_mcp_config(socket: &Path) -> Result<PathBuf> {
/// Drop the static `--settings` JSON next to the MCP config so we can
/// pass a path (`--settings <file>`) instead of an ever-growing inline
/// blob — the CLI argv has a finite length budget. The file carries
/// `permissions.defaultMode = bypassPermissions` + a small `deny` list,
/// so everything not in `deny` auto-approves without a per-flavor allow
/// list.
/// blob — the CLI argv has a finite length budget.
pub async fn write_settings(socket: &Path) -> Result<PathBuf> {
let parent = socket.parent().unwrap_or_else(|| Path::new("/run/hive"));
tokio::fs::create_dir_all(parent).await.ok();
@ -252,7 +245,11 @@ async fn run_claude(prompt: &str, files: &TurnFiles, bus: &Bus) -> Result<bool>
cmd.arg("--system-prompt-file").arg(&files.system_prompt);
cmd.arg("--mcp-config")
.arg(&files.mcp_config)
.arg("--strict-mcp-config");
.arg("--strict-mcp-config")
.arg("--tools")
.arg(mcp::builtin_tools_arg())
.arg("--allowedTools")
.arg(mcp::allowed_tools_arg(files.flavor));
let mut child = cmd
.stdin(Stdio::piped())
.stdout(Stdio::piped())