Full interactive shell access to the sandbox.
Each session has a PTY running inside the sandbox:
- Runs as
agentuser with passwordless sudo - Starts in
/agent/workspace - Full color support (xterm-256color)
- Persists across app backgrounding
CLI:
netclode shell # New sandbox + shell
netclode shell <session-id> # Attach to existingCtrl+D exits. Ctrl+] detaches (session stays running).
iOS App: Tap terminal icon in bottom nav. Supports touch keyboard, copy/paste, pinch-to-zoom.
Terminal I/O flows through Connect streams: Client → Control Plane → Agent → node-pty → bash
HOME=/agentSHELL=/bin/bashTERM=xterm-256colorPATHincludes mise shims
/agent/ # Home (persistent)
├── workspace/ # Your code
├── .local/share/mise/ # Installed tools
├── .cache/ # Package caches
└── .claude/ # SDK session data
mise use node@22 # Install runtimes via mise
docker compose up -d # Docker available
sudo apt install htop # Passwordless sudoTools persist across pause/resume (stored on JuiceFS).
PTY spawns lazily on first terminal interaction. Survives app backgrounding and reconnection.
Destroyed on session pause/delete or shell exit. After resume, new PTY created on first interaction (shell history may be available from .bash_history).
When the PTY process exits (e.g., exit or Ctrl+D), the agent sends an OSC 9999 escape sequence (\x1b]9999;pty-exit;<exitCode>\x07) through the terminal output stream. The CLI shell client detects this and auto-detaches. Regular terminals ignore unknown OSC sequences, so this is invisible to the iOS app.
Terminal not connecting - check session is ready/running, check control-plane logs:
kubectl --context netclode -n netclode logs -l app=control-plane | grep terminalNo output after connecting - try sending a keystroke (triggers PTY creation).
Commands hang - check network policy and DNS (nslookup google.com).