Skip to content

feat: add --tty flag for interactive terminal applications#77

Open
abe238 wants to merge 1 commit intoanthropic-experimental:mainfrom
abe238:feature/tty-flag
Open

feat: add --tty flag for interactive terminal applications#77
abe238 wants to merge 1 commit intoanthropic-experimental:mainfrom
abe238:feature/tty-flag

Conversation

@abe238
Copy link

@abe238 abe238 commented Dec 22, 2025

Summary

Add -t/--tty CLI flag to enable pseudo-terminal (PTY) passthrough for interactive terminal applications.

Problem

Interactive terminal applications (like vim, htop, Claude Code, or any TUI app) fail with setRawMode EPERM when run inside the sandbox because PTY operations are blocked by default.

Solution

This PR exposes the existing allowPty functionality (which already exists in SandboxRuntimeConfig and macos-sandbox-utils.ts) via a new CLI flag:

srt --tty vim file.txt
srt -t htop

Changes

  • src/cli.ts: Add -t/--tty flag that sets allowPty: true on the runtime config before initialize()
  • test/cli.test.ts: Add 4 tests covering --tty, -t, -c mode combo, and negative case
  • README.md: Document the new flag and allowPty config option

Implementation

The flag sets allowPty: true on runtimeConfig before calling SandboxManager.initialize(), keeping the change minimal and consistent with how other config options flow through the system. The wrapWithSandbox call remains unchanged.

When allowPty is enabled, the macOS Seatbelt profile adds:

  • (allow pseudo-tty) - Master PTY permission
  • (allow file-ioctl (literal "/dev/ptmx") (regex #"^/dev/ttys")) - Ioctl operations on PTY devices
  • Read/write access to /dev/ptmx and /dev/ttys*

Testing

 19 pass, 0 fail — bun test test/cli.test.ts
 55 pass, 0 fail — all project test files
  • npm run typecheck passes
  • npm run lint:check passes
  • npm run build passes
  • Pre-commit hooks (eslint, prettier) pass
  • All existing tests continue to pass
  • 4 new tests for --tty flag

Security Notes

  • PTY access does not bypass filesystem/network sandbox restrictions
  • Opt-in only (secure by default)
  • macOS only; Linux handles PTY access differently

Closes #76

🤖 Generated with Claude Code

@abe238
Copy link
Author

abe238 commented Dec 22, 2025

Detailed Technical Analysis

Root Cause Investigation

When running interactive terminal applications (like Claude Code) inside srt, users encounter:

Error: setRawMode EPERM
    at ReadStream.setRawMode (node:tty:81:24)

This occurs because:

  1. Node.js readline module calls setRawMode() on stdin to enable character-by-character input
  2. setRawMode() requires ioctl operations on the controlling terminal device (/dev/ttys*)
  3. macOS Seatbelt sandbox blocks ioctl operations on PTY devices by default

Code Path Analysis

The sandbox profile generation in macos-sandbox-utils.ts already supports PTY access:

// Lines 585-598
if (allowPty) {
  profile.push('(allow pseudo-tty)')  // Master PTY permission
  profile.push('(allow file-ioctl')
  profile.push('  (literal "/dev/ptmx")')
  profile.push('  (regex #"^/dev/ttys")')  // Slave TTYs
  profile.push(')')
  profile.push('(allow file-read* file-write*')
  profile.push('  (literal "/dev/ptmx")')
  profile.push('  (regex #"^/dev/ttys")')
  profile.push(')')
}

The allowPty option exists in the config schema (sandbox-config.ts:174-177) and is properly propagated through SandboxManager.wrapWithSandbox() at line 536:

const allowPty = customConfig?.allowPty ?? config?.allowPty

However, there was no CLI flag to enable this option for one-off interactive commands.

Why This Matters

  1. Security tools should be usable - If users can't run interactive applications, they'll either:

    • Skip sandboxing entirely (worse security)
    • Use workarounds that may introduce vulnerabilities
  2. Explicit opt-in is correct - The --tty flag maintains the secure-by-default philosophy while enabling legitimate use cases

  3. The code existed, just wasn't exposed - This PR is minimal because the underlying functionality was already implemented and tested

Security Considerations

PTY access does NOT bypass the core sandbox restrictions:

Restriction With --tty
Filesystem writes Still limited to allowWrite paths
Network access Still limited to allowedDomains
Read restrictions Still respects denyRead

PTY access does allow:

  • Terminal escape sequence injection (limited impact - user is at terminal)
  • Keystroke simulation (only affects the sandboxed process's terminal)
  • Raw mode for character-by-character input

Alternative Solutions Considered

Approach Pros Cons
Config file only Persistent All commands get PTY, not discoverable
CLI flag (this PR) Explicit, per-command Extra flag to remember
Auto-detect TTY Zero friction Silent security relaxation, unpredictable in CI

The CLI flag approach was chosen because:

  • Explicit is better than implicit for security tools
  • Per-command control is more flexible than global config
  • Users can combine with config file for persistent setting

Testing Notes

  • The --tty flag shows correctly in --help
  • Debug mode confirms allowPty: true is set when flag is used
  • Pre-commit hooks (eslint, prettier) pass
  • Build succeeds

For full interactive testing, users should test with actual TUI applications like vim or htop on their macOS system.

@abe238
Copy link
Author

abe238 commented Dec 22, 2025

PS: I love you guys <3 My public GH before Claude Code was almost empty of little green squares. Kudos on the best app of the year.
contributions (1)

@webdevcody
Copy link

we are running into an issue using the https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk where when sandbox mode is enabled (on mac and windows users) the agent just seems to freeze up. We had to turn off sandbox mode because of it. Hopefully this PR fixes that issue and it can get merge upstream to the claude-agent-sdk

@kkyr
Copy link

kkyr commented Jan 15, 2026

Would love to see this get merged!

Expose the existing allowPty config option via a -t/--tty CLI flag,
enabling interactive TUI apps (vim, htop, Claude Code) to run inside
the sandbox without EPERM errors on PTY operations.

- Set allowPty on config before initialize() for clean integration
- Add 4 tests covering --tty, -t, -c mode combo, and negative case
- Document flag in README CLI usage and configuration reference

Closes anthropic-experimental#76

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@abe238
Copy link
Author

abe238 commented Feb 7, 2026

Rebased onto latest main, simplified the implementation, and added tests.

Changes from v1:

  • Rebased onto current main (was 51 commits behind)
  • Simplified approach: set allowPty on runtimeConfig before initialize() instead of passing customConfig to wrapWithSandbox
  • Added 4 tests in test/cli.test.ts covering --tty, -t, -c combo, and negative case
  • All 55 project tests pass, typecheck + lint clean

This addresses a real pain point — multiple people in this thread and #76 have confirmed they can't run Claude Code or other TUI apps in the sandbox without this. The underlying allowPty plumbing already exists; this just exposes it via CLI.

@ollie-anthropic @ddworken — would appreciate a review when you get a chance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: TTY passthrough for interactive terminal applications

3 participants