18 KiB
Prior art: permission boundary for an unattended Self run
Serves fork eureka-body:question:self-run-permissions (campaign eureka-body, session self-2026-10-22-eureka-c2a). Eyes pass, 2026-10-22. Facts and evidence pointers only; no recommendation.
Source status key: FETCHED = page read in full or in the relevant part via WebFetch. SEARCH-ONLY = seen only as a WebSearch summary, page not read. NOT FETCHED = not obtained.
Claims in the fork's prior-art line, checked
| Fork claim | Result |
|---|---|
Managed settings at /etc/claude-code/managed-settings.json on Linux |
Confirmed (FETCHED, managed-settings). |
| Managed outranks user and project settings | Confirmed (FETCHED, settings). |
| Docs advise bypassPermissions only in an isolated container or VM | Confirmed, wording stronger: see 1.3 (FETCHED). |
--permission-prompts none exists |
Confirmed, needs Claude Code v2.1.259+ (FETCHED, cli-reference, headless). |
| Codex "full-auto" runs approval never inside workspace-write | Not supported by the official page: see 2 (FETCHED). The current official page does not define --full-auto. A search summary cited third-party blogs that disagree with each other (on-request vs never); those pages were NOT FETCHED. |
1. Claude Code
1.1 Managed settings and precedence
Source: https://code.claude.com/docs/en/managed-settings (FETCHED), https://code.claude.com/docs/en/settings (FETCHED).
- Linux and WSL file:
/etc/claude-code/managed-settings.json; optional/etc/claude-code/managed-settings.d/*.jsondrop-ins merged with it;managed-mcp.jsonsame directory. macOS/Library/Application Support/ClaudeCode/, WindowsC:\Program Files\ClaudeCode\. - Precedence, highest first: managed, command line (
--settings), project local, shared project, user. "Nothing in your own settings files or--settingsoverrides a managed key." - List keys such as
permissions.allowmerge across scopes, unlessallowManagedPermissionRulesOnlyis set. Deny rules from any scope are evaluated before allow rules. - Exceptions where a stricter lower-scope value still wins over managed exist for a short list (e.g.
permissions.blockReadsOutsideWorkingDirectories: truefrom any scope); none loosens a managed deny. - Keys that exist in managed settings for policy (FETCHED, managed-settings table):
allowManagedPermissionRulesOnly,allowManagedHooksOnly,allowManagedMcpServersOnly,permissions.disableBypassPermissionsMode,permissions.disableAutoMode,sandbox.network.allowManagedDomainsOnly,sandbox.filesystem.allowManagedReadPathsOnly.disableBypassPermissionsMode"works from any scope". permissions.defaultModevaluesautoandbypassPermissions"don't take effect from project or local settings; set them in user or managed settings instead, or pass--permission-mode". Before v2.1.257 bypassPermissions took effect from any file.- What a developer can change: "a developer who is an administrator on the machine can edit the managed source itself".
- Unreadable managed file: "If the operating system denied the read, for example on a root-only file, every session starts without that source's policies." A present-but-unparseable managed file makes Claude Code refuse to start. Invalid single-value restrictive keys read as the restrictive value.
- On WSL,
/etc/claude-codeis user-writable (not relevant to a native Linux unit).
1.2 Permission rules
Source: https://code.claude.com/docs/en/permissions (FETCHED; large page, read by targeted extraction).
- Evaluation order: deny, then ask, then allow; first match decides; specificity does not reorder.
- Bash rules match the whole command text;
*wildcard;Bash(git *)and legacyBash(git:*)are equivalent when the colon-star is a trailing suffix. A space before*matters (Bash(git diff*)also matchesgit diff-index). - Edit/Write/Read take path rules (
Read(./.env),Read(./secrets/**));WebFetch(domain:example.com),WebFetch(domain:*.example.com). Bash(command:rm *)-style constraint on the primary content field is ignored with a startup warning because it "would be bypassable by a compound command".- Compound commands (
&&,;, pipes) are split and each subcommand must match. Built-in wrapper stripping:timeout,time,nice,nohup,stdbuf,command,builtin,noglob; not stripped:direnv exec,devbox run,mise exec,npx,docker exec,watch,setsid,ionice,flock; soBash(devbox run *)matchesdevbox run rm -rf ..
1.3 Documented limits of Bash patterns
Same source (FETCHED).
- "A deny or ask rule covers the invocation Claude usually produces and isn't a security boundary around the program." Table of non-matches:
Bash(curl *)does not stop/usr/bin/curl ...orsh -c 'curl ...';Bash(rm *)does not stop/bin/rm -rforbash -c;Bash(git push *)does not stopgit -C . push origin main,git -c push.default=current push ...,git 'push' origin main. - "Bash permission patterns that try to constrain command arguments are fragile" (curl-URL example: option reordering, protocol change, redirects, variables).
- Docs' pointers for reliable enforcement: sandboxing ("filesystem and network enforcement that doesn't depend on the command text"), PreToolUse hooks, WebFetch domain rules plus sandbox network allowlist.
- A blocking PreToolUse hook (exit code 2) stops the call before rules; hooks cannot override a matching deny rule.
- Read/Edit deny rules apply to built-in file tools and recognised file commands (
cat,head,sed,tee, redirects); not to commands that read without naming a file (e.g.grep -r pattern .).
1.4 Permission modes and bypassPermissions
Sources: https://code.claude.com/docs/en/permission-modes (FETCHED, last 812 chars unread), permissions page (FETCHED).
- Modes:
default/manual,acceptEdits,plan,auto(classifier reviews actions),dontAsk,bypassPermissions. Table row for bypassPermissions: "Isolated containers and VMs only". - permission-modes page text on bypassPermissions (via WebFetch summarizer, near-verbatim): use only "in isolated environments like containers, VMs, or dev containers without internet access, where Claude Code cannot damage your host system"; "offers no protection against prompt injection or unintended actions". The permissions page says "Only use this mode in isolated environments like containers or VMs where Claude Code can't cause damage."
- On Linux/macOS Claude Code refuses bypassPermissions when running as root or under sudo unless it detects a recognised sandbox; the docs recommend a non-root user (dev container).
- Even in bypassPermissions these are not auto-approved: explicit
askrules; connector tools set toaskby an org; tools requiring user interaction; recognisedrm/rmdircritical-path removals ("no allow rule or PreToolUse hook approves"); cross-session messaging safeguards; reads outside working dirs whenblockReadsOutsideWorkingDirectoriesis on. Writes to protected paths (.git,.claude,.bashrc,.gitconfig,.mcp.jsonetc.) are allowed in bypass. dontAsk: auto-denies everything that would prompt; reads in working directories, read-only commands, andpermissions.allow/--allowedToolsmatches still run. "Locked-down CI and scripts".- Flags:
--permission-mode,--dangerously-skip-permissions(equivalent to bypassPermissions),--allow-dangerously-skip-permissions,--restricted(v2.1.248+: loads only managed settings and--settings, refuses bypassPermissions, removes command-running tools unless named in--tools).
1.5 --permission-prompts
Sources: https://code.claude.com/docs/en/cli-reference (FETCHED), https://code.claude.com/docs/en/headless (FETCHED).
--permission-prompts host|none, print mode, v2.1.259+. Withnone: "Anything that would prompt is denied unless aPermissionRequesthook allows it, Claude is told that nobody can approve the request and not to retry it". Rules,PermissionRequesthooks and the permission mode "still decide every call first". RemovesAskUserQuestion; unanswered MCP elicitations are cancelled. Denials appear aspermission_deniedsystem messages and inpermission_denialsof the result withstream-json.--permission-prompt-tool mcp__<server>__<tool>handles prompts in non-interactive mode.- Headless page: from v2.1.302
claude -pstarts in Manual mode when nothing sets a mode; a call needing approval is denied unless a hook or prompt tool approves.--bareis recommended for scripted calls and skips CLAUDE.md, hooks, plugins, MCP, auto memory discovery (relevant: a bare run would not load the Self's own CLAUDE.md or skills; managed settings are still read per--restricted/safe-mode wording, but--bareand managed settings were not explicitly cross-checked on the fetched page). - Without
--bare, a-psession runs hooks from the project's.claude/settings.jsonand connects its.mcp.jsonservers, with no trust dialog.
1.6 Claude Code's own sandbox (relevant to "OS boundary" options)
Source: https://code.claude.com/docs/en/sandboxing (FETCHED, targeted grep of the saved page).
- Linux/WSL2/macOS only; uses
bubblewrapandsocat; optional seccomp filter adds Unix-socket blocking. Native Windows runs commands unsandboxed. - Covers Bash, PowerShell, Monitor commands and children only. "Claude's file tools, MCP servers, and hooks run outside it."
- Network: no direct route out; connections go through a proxy on the machine that checks each host against
sandbox.network.allowedDomains(starts empty); hostname-level. - Escape hatch:
dangerouslyDisableSandboxretry; disabled withsandbox.allowUnsandboxedCommands: false;failIfUnavailablesetting exists. A matching allow rule such asBash(curl *)also approves the unsandboxed retry. - Inside containers bubblewrap can fail with
Operation not permitted; Ubuntu 24.04+ AppArmor settingkernel.apparmor_restrict_unprivileged_usernscan block bubblewrap. - Permissions and sandbox are described as complementary: "sandbox restrictions still apply even if a prompt injection bypasses Claude's decision-making".
2. OpenAI Codex CLI
Source: https://learn.chatgpt.com/docs/agent-approvals-security.md (FETCHED; redirect target of developers.openai.com/codex/agent-approvals-security.md). The GitHub docs/sandbox.md in openai/codex is a pointer only (FETCHED).
- Approval policies:
on-request,never("Codex doesn't prompt for approval. Sandbox modes still apply."), granular table form;untrustedretired. - Sandbox modes:
read-only,workspace-write("Network access is off by default"; enable with[sandbox_workspace_write] network_access = true),danger-full-access(no sandbox). features.network_proxyconstrains enabled traffic by domain rules, allowlist-first, deny wins; does not grant network by itself; local/private destinations blocked by default.- "Auto" preset is
--sandbox workspace-write --ask-for-approval on-request. The page does not define--full-auto. - OS sandbox: macOS Seatbelt; Linux
bwrap+seccomp; Windows MXC or legacy modes; WSL2 uses the Linux implementation. --dangerously-bypass-approvals-and-sandbox(--yolo): removes sandbox and approvals, "not recommended".- Externally sandboxed environments: if a container blocks namespace/setuid bwrap/seccomp operations, "configure the container for the isolation you need, then run Codex with
danger-full-accessinside it". Warns Dev Containers "do not prevent every attack"; with full access inside a container "a malicious project could exfiltrate data available there, including Codex credentials". - Third-party summaries about
-a never -s workspace-writefor CI came from a WebSearch summary only; NOT FETCHED.
3. Other unattended coding agents
| Agent | Boundary | Network egress | Status |
|---|---|---|---|
| GitHub Copilot cloud/coding agent | Ephemeral GitHub Actions environment plus a firewall for processes started via its Bash tool | Firewall on by default; always-allowed GitHub hosts plus a recommended allowlist (OS package repos, container registries, language package registries, CAs, Playwright browser hosts); org/repo allowlist additions; blocked requests are reported in the PR. Not covered: MCP server processes and processes started in setup steps. Docs: "should not be considered a comprehensive security solution." | FETCHED https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall ("runs on Actions infrastructure" statement was SEARCH-ONLY) |
| OpenHands | Docker sandbox container "recommended"; "process" sandbox is "unsafe, but fast", "No container isolation"; remote option | Overview page does not describe egress; Docker page does not describe egress restrictions, names read-write mounts as the main risk (SANDBOX_VOLUMES, --mount-cwd). Search summary: FAQ says agent can reach the internet from the container (NOT FETCHED); confirmation policies AlwaysConfirm/ConfirmRisky with pluggable security analyzers exist in the SDK (SEARCH-ONLY) |
FETCHED https://docs.openhands.dev/openhands/usage/sandboxes/overview and .../docker; SDK security page SEARCH-ONLY |
| SWE-agent | Docker container per run via SWE-ReX (local Docker, or remote such as Modal/AWS); shell session inside the container | Architecture page silent on egress; no command allow/blocklist described on that page (a tool-bundle blocklist config exists in the project, NOT FETCHED) |
FETCHED https://swe-agent.com/latest/background/architecture/ |
| Devin | Per-session VM ("Devbox"); enterprise option runs Devbox in customer VPC; Brain in Cognition tenant; Outposts: customer machines dial out only | VPC option needs outbound HTTPS 443 only; docs "strongly recommend" granting internet access to in-VPC instances | SEARCH-ONLY (docs.devin.ai/enterprise/vpc/overview, deployment pages, devin.ai/blog/introducing-devin-outposts); no page fetched |
Pattern observed across the five agents fetched or searched: in each the unattended boundary is an OS/container/VM isolation unit; command allowlists appear as a secondary layer (Claude Code rules, OpenHands confirmation policies), not as the sole boundary. Egress handling differs: Codex default off; Copilot allowlist firewall scoped to the Bash tool; Claude Code proxy allowlist scoped to Bash; OpenHands and SWE-agent pages read do not state egress controls.
4. systemd sandbox directives
Sources: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html (FETCHED, chars 0-200000 of 244377; last 44377 unread), https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html (FETCHED).
ProtectSystem=strict: whole file-system hierarchy read-only except/dev,/proc,/sys;ReadWritePaths=re-opens paths;StateDirectory=,LogsDirectory=etc. stay writable; withPrivateTmp=/tmpand/var/tmpwritable. Caveat: "This setting cannot ensure protection in all cases".ProtectHome=true:/home,/root,/run/userinaccessible and empty. No protection if homes are at non-standard locations.PrivateTmp=true: private/tmpand/var/tmp, removed on stop.ReadWritePaths=/ReadOnlyPaths=: refused "even if the usual file access controls would permit";-prefix ignores missing paths. Caveats: writable host mounts that propagate in remain writable; "Privileged processes can undo these settings", pair withCapabilityBoundingSet=~CAP_SYS_ADMINorSystemCallFilter=~@mount.StateDirectory=: created under/var/lib/, owned byUser=/Group=, not removed on stop.NoNewPrivileges=true: no privilege gain throughexecve()(setuid/setgid bits, file capabilities). It "does not affect" processes invoked on request throughat,crontab,systemd-run, "or arbitrary IPC services" (page does not name polkit or D-Bus; the generic IPC statement is the evidence).DynamicUser=impliesProtectSystem=strict,ProtectHome=read-only,PrivateTmp=disconnected,NoNewPrivileges,RestrictSUIDSGID; "currently incompatible with D-Bus policies".RestrictAddressFamilies=: allow-list (or~deny-list) ofAF_*; applies only tosocket(2); not to sockets passed in (socket activation),socketpair(), orio_uring; no effect on 32-bit x86, s390, s390x and some others; recommendsSystemCallArchitectures=native; keepAF_UNIXfor local IPC and syslog. It selects families (e.g. AF_INET/AF_INET6 vs AF_UNIX), not destinations.IPAddressAllow=/IPAddressDeny=: lists of IPv4/IPv6 addresses with optional prefix length, plusany,localhost,link-local,multicast; apply to IPv4/IPv6 sockets of the unit; filter ingress (source) and egress (destination). Order: allow match grants; else deny match denies; else granted. Both empty by default. Recommended allow-list pattern:IPAddressDeny=anyon a higher slice, per-serviceIPAddressAllow=. The page does not mention hostnames, DNS, or SNI; the directives take IP literals only. May have no effect where eBPF cgroup support is missing; the page advises against relying on them alone. Socket-unit lists do not cover the activated service's sockets.- Consequence stated by the directives' own definitions (no separate source): hostname-based egress filtering is not expressed by any of these directives.
Gaps
- Codex
--full-autocurrent meaning not stated in any fetched official page. - Devin, OpenHands V1 egress/security pages and SWE-agent tool blocklist docs not read.
- systemd.exec last 44377 characters unread (covers later sections, not the directives above).
- Claude Code permissions page (84 KB) and sandboxing page (97 KB) read by targeted extraction, not end to end; the
--bareinteraction with managed-settings loading is not confirmed from a fetched sentence. - No test was run of
--permission-prompts nonewith a managedbypassPermissionsfile on the target Claude Code version.