Skip to content

feat: Support for sudo and password-requiring commands in Bash tool #9808

Closed as not planned

Description

@bernatfp

Problem

The Bash tool currently runs with stdio: ["ignore", "pipe", "pipe"], meaning stdin is disconnected. This breaks any command requiring interactive password input:

  • sudo apt install ...
  • ansible-playbook -K ... (ask-become-pass)
  • ssh -t user@host "sudo ..."
  • GPG passphrase prompts

Users working with system administration, deployment automation, or remote servers cannot use these common workflows through OpenCode.

Proposed Approach

Use PTY-based execution for commands that may require interactive input:

  1. Auto-detect interactive commands - Pattern match for sudo, ssh -t, ansible -K, etc.
  2. PTY execution - Spawn an ephemeral PTY for interactive commands
  3. Prompt detection - Detect password prompts via patterns ([sudo] password for, Password:, BECOME password:, etc.)
  4. SecureInput system - Event-based system that requests password from user when prompt detected
  5. Direct PTY write - Password goes directly to PTY, never stored or logged
  6. Output sanitization - Replace password prompts with [Password prompt - user input required] before sending to LLM

Security considerations

  • Password never logged or stored in memory beyond immediate use
  • Password never appears in conversation history or LLM context
  • UI uses type="password" input
  • 60s timeout, max 3 retry attempts

Why it belongs in OpenCode

This is a common pain point for DevOps/sysadmin workflows. The PTY infrastructure already exists for the terminal feature, so this leverages existing code. It's also how Claude Code handles similar scenarios.

Happy to share implementation details if there's interest.

Activity

  1. github-actions commented on Jan 21, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  2. ArabianCowboy commented on Mar 22, 2026

    @ArabianCowboy

    +1 on this. As someone working in system administration and deployment contexts, this is a consistent blocker. Running sudo commands, SSH connections, or anything requiring a password mid-execution currently forces me to leave the agent context entirely. The proposed PTY + SecureInput approach looks like the right solution — would love to see this prioritized. Gemini CLI and Codex CLI both handle this natively and it significantly changes the day-to-day usability for sysadmin workflows.

  3. mikegilchrist commented on Apr 8, 2026

    @mikegilchrist

    Alternative for headless/SSH workflows: global sudo timestamp + PreToolUse hook

    SUDO_ASKPASS with GUI helpers is great for local desktop sessions, but breaks on SSH+tmux workflows where there's no reachable X display. Here's a lighter approach that works without any custom askpass program.

    Setup

    /etc/sudoers.d/claude-global-timestamp:

    Defaults timestamp_type=global
    Defaults timestamp_timeout=20
    

    timestamp_type=global makes sudo's credential cache per-user instead of per-tty. With that, authenticating in any terminal (a second SSH session, a sibling tmux pane, or the parent shell via Ctrl-Z) caches credentials that Claude's Bash-tool subprocess can then reuse within the timeout window.

    Usage

    1. Ctrl-Z to suspend Claude (or open a second pane / SSH session)
    2. sudo -v
    3. fg to resume Claude (if you suspended)
    4. Claude's subsequent sudo commands work for 20 minutes
    5. sudo -k in any terminal to end the session early

    Important gotcha: Claude's ! command prefix does not give you an interactive tty — it runs through the same ttyless Bash-tool mechanism, so plain sudo still fails there. Use Ctrl-Z + fg, or a separate pane.

    Automation with a PreToolUse hook

    A small sudo-cache-check.sh hook runs before every Bash tool call, scans for sudo, probes sudo -n true, and if the cache is cold returns an ask decision with a message telling the user exactly what to do. Claude never wastes a tool call on a command that would immediately fail with "a terminal is required to read the password."

    Sketch:

    #!/bin/bash
    set -o pipefail
    input=$(cat)
    command=$(echo "$input" | jq -r '.tool_input.command // empty')
    [ -z "$command" ] && exit 0
    
    # No sudo => allow
    echo "$command" | grep -Eq '(^|[^a-zA-Z0-9_/.-])sudo([[:space:]]|$)' || exit 0
    
    # Cache-management (-v, -k, -K) or explicit -n => allow through
    if echo "$command" | grep -Eq '(^|[^a-zA-Z0-9_/.-])sudo[[:space:]]+-[vkKn]([[:space:]]|$)'; then
        jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"sudo cache-management or non-interactive"}}'
        exit 0
    fi
    
    # Probe
    if sudo -n true 2>/dev/null; then
        jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"sudo cache warm"}}'
        exit 0
    fi
    
    # Cold: ask user to authenticate
    msg='Sudo cache is cold. Press Ctrl-Z, run `sudo -v`, then `fg` and tell me to retry.'
    jq -n --arg msg "$msg" '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":$msg}}'

    Registered under PreToolUse.Bash in ~/.claude/settings.json.

    Why this beats the alternatives for SSH workflows

    Approach Works over SSH Maintenance Auto-expires User control
    Full NOPASSWD yes low no weak
    Narrow NOPASSWD yes per-command no strong but rigid
    SUDO_ASKPASS (GUI) no low yes (per-cmd) per-cmd confirm
    Copy-paste yes tedious n/a strong
    This approach yes low yes (20 min) strong

    Trust model

    During the window, any process running as your user can sudo without a prompt — effectively the same trust boundary as the interactive shell you're already typing in. Appropriate for single-user workstations where you're actively supervising Claude; not appropriate for multi-user or unattended-agent setups.

    Happy to PR a canonical version of the setup script + hook into a docs page if the maintainers would find that useful.

  4. ziuus commented on May 21, 2026

    @ziuus

    I'd like to take this on. The PTY infra in src/pty/ already has write()+onData() — my plan is to add an interactive: boolean flag to the Bash tool schema and route those commands through the PTY instead of the current child process spawn. Would that approach work for you all?

  5. github-actions commented on Jul 21, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  6. ziuus commented on Jul 23, 2026

    @ziuus

    Reopening this — I've implemented the PTY-based sudo support with SecureInput. PR: #38510

    The implementation:

    • Adds a SecureInput Effect service (password prompt detection, PTY execution, event-driven user input)
    • Adds interactive: boolean parameter to ShellTool (auto-detected for sudo/ssh/ansible)
    • Uses bun-pty for PTY-based execution when interactive
    • Password goes directly to PTY fd — never stored or logged
    • Output sanitization replaces password prompts with [password supplied]
    • API routes: GET /secure-input (list), POST /:id/submit, POST /:id/cancel
    • Bus events for real-time UI integration

    Follow-up work needed: TUI prompt component, web app prompt component, SDK types.

  7. ziuus commented on Jul 24, 2026

    @ziuus

    Opened PR #38640 implementing PTY-based interactive command execution with SecureInput. The updated version removes the InstanceState dependency so the service can be provided globally without Scope/InstanceRef requirements.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions