[FEATURE] Wire `sandbox.allowPty` from settings.json into the macOS Seatbelt profile (sandbox-runtime already supports it)
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Pty allocation is always blocked inside the macOS sandbox: script -q /dev/null true fails with script: openpty: Operation not permitted, and python3 -c 'import pty; pty.openpty()' fails with OSError: out of pty devices.
This matters because on macOS, R (Rscript) busy-spins at ~100% CPU when run without a controlling terminal, and the reliable workaround is a pty wrapper (script -q /dev/null Rscript … < /dev/null). Since openpty is blocked in-sandbox, every pty-wrapped job must run with dangerouslyDisableSandbox: true — the entire job leaves the sandbox to obtain one pty device pair. The same applies to anything else that needs a pty (expect scripts, tools that insist on a tty).
The embedded @anthropic-ai/sandbox-runtime already supports exactly this: its config schema has allowPty ("Allow pseudo-terminal (pty) operations (macOS only)"), and when set, the Seatbelt profile generator emits (allow pseudo-tty) plus read/write/ioctl on /dev/ptmx and /dev/ttys*. But there is no way to reach it from Claude Code (inspected in 2.1.165):
- The settings.json
sandboxschema has noallowPtyfield — and it ends with.passthrough(), so setting"sandbox": { "allowPty": true }is accepted silently and does nothing. - The settings→runtime conversion builds the runtime config from a fixed key list (
network,filesystem,ignoreViolations,enableWeakerNestedSandbox,enableWeakerNetworkIsolation,ripgrep,seccomp,bwrapPath,socatPath) and never copiesallowPty, so the value never reaches the profile generator (which readsperCallOverride?.allowPty ?? runtimeConfig?.allowPty).
A sandbox-exec A/B on Darwin 24.6 confirmed the emitted Seatbelt block is necessary and sufficient: replaying the sandbox's deny-default profile reproduces the openpty failure, and appending the allowPty block fixes it.
Proposed Solution
Wire sandbox.allowPty through from settings.json:
- Add
allowPty: booleanto the settings.jsonsandboxschema (macOS only, matching the runtime's own description). - Copy it in the settings→runtime conversion — appears to be a one-line change, since runtime support is complete.
- Document it on the sandboxing settings page.
Expected result: with "sandbox": { "allowPty": true }, script -q /dev/null true < /dev/null exits 0 inside the sandbox.
Alternative Solutions
excludedCommands: ["script *"]— rejected:scriptruns arbitrary commands, so this is a wrap-anything sandbox escape, strictly worse than the requested grant.- Per-invocation
dangerouslyDisableSandbox: true— the status quo; runs the entire job unsandboxed to obtain one device pair. - Wrapping commands in the standalone
srtCLI with anallowPtyconfig — Seatbelt can't nest (sandbox_apply: Operation not permittedin-sandbox), so this also requires disabling Claude Code's sandbox for the call, plus maintaining a parallel policy by hand.
Priority
High - Significant impact on productivity
Feature Category
Configuration and settings
Use Case Example
- A statistical analysis project runs R model fits from Claude Code (
Rscript code/fit.R). - On macOS, R busy-spins without a controlling terminal, so the job is launched as
script -q /dev/null Rscript code/fit.R < /dev/null. - In-sandbox this fails immediately with
script: openpty: Operation not permitted, so today the call must setdangerouslyDisableSandbox: true, forfeiting filesystem and network confinement for the whole job. - With
sandbox.allowPty: true, the same job would run fully sandboxed (filesystem + egress bounds intact) with only the pty device pair added.
Additional context: tested on Claude Code 2.1.165 (native install), macOS Darwin 24.6. The grant is bounded — (allow pseudo-tty) + /dev/ptmx + /dev/ttys* (the /dev/ttys* regex does match other live same-user terminal ttys; that trade-off may be worth a sentence in the docs). For comparison, other agent CLI sandboxes (e.g. OpenAI Codex CLI's macOS base policy) ship (allow pseudo-tty) + /dev/ptmx access by default.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗