Files
the-nexus/docs/ADR-001-shell-boundary.md
Claude (Opus 4.6) c97364ac13
Some checks failed
Deploy Nexus / deploy (push) Failing after 5s
Staging Verification Gate / verify-staging (push) Failing after 5s
[claude] ATLAS Cockpit: operator inspector rail and session shell (#1695) (#1696)
2026-04-22 05:19:13 +00:00

3.6 KiB
Raw Blame History

ADR-001 — Shell / Terminal Boundary and Transport Model

Status: Accepted Date: 2026-04-22 Issue: #1695 — ATLAS Cockpit: operator inspector rail and session shell patterns


Context

The Nexus operator cockpit needs a real shell/terminal surface so the operator can run commands, inspect logs, and interact with the system without leaving the 3D world UI.

Three transport models were evaluated:

Option Description
A. Native local PTY server.py spawns a PTY via Python's pty stdlib module; the browser connects via WebSocket on ws://127.0.0.1:8766/pty
B. Remote SSH PTY Browser connects to an SSH relay that opens a PTY on a remote machine
C. Browser pseudo-terminal Pure JavaScript terminal emulation (e.g., a custom REPL) with no real OS shell

Decision

Option A — Native local PTY is chosen.

The Nexus is explicitly a local-first system (CLAUDE.md: "local-first training ground for Timmy"). The operator is the same person sitting at the machine. A local PTY bridged through server.py is:

  • Architecturally consistent with the existing server.py WebSocket gateway
  • Zero additional infrastructure (no SSH relay, no remote server)
  • Full shell fidelity — any tool on the operator's PATH, full interactive programs, readline, color, etc.
  • Bounded: PTY_PORT (8766) binds to 127.0.0.1 only; no external exposure

Transport Detail

Browser (xterm.js) ←→ ws://127.0.0.1:8766/pty ←→ server.py pty_handler ←→ OS PTY ($SHELL)
  • Each WebSocket connection gets its own pty.openpty() pair and subprocess.
  • Input from xterm.js onData_ptyWs.send(data)os.write(master_fd, data)
  • Output from PTY → os.read(master_fd)websocket.send(text)_term.write(text)
  • Resize messages ({"type":"resize","cols":N,"rows":N}) can be added later via fcntl.ioctl(TIOCSWINSZ).

Why not Option B (Remote SSH PTY)?

Remote SSH adds network hop complexity, credential management, and an SSH relay service. The Nexus does not currently have a remote operator use-case; Alexander operates locally. This decision can be revisited when fleet/remote-agent use-cases mature (see issues #672#675).


Why not Option C (Browser pseudo-terminal)?

A JavaScript REPL cannot run arbitrary shell programs, manage processes, or provide the raw interactive shell experience that operators expect. It would be a toy, not a tool.


Rejected Patterns

  • tmux over WebSocket — adds server-side state complexity without enough benefit at this scale
  • ttyd — external binary dependency; overkill for a local-first single-operator setup
  • xterm.js + websocketd — external binary dependency; server.py already owns the WebSocket gateway

Consequences

  • server.py now starts two WebSocket servers: PORT (8765, main gateway) and PTY_PORT (8766, shell gateway)
  • PTY_PORT always binds to 127.0.0.1 regardless of NEXUS_WS_HOST
  • cockpit-inspector.js loads xterm.js from CDN (offline fallback: graceful degradation — the rail renders without the terminal pane)
  • PTY sessions are ephemeral (no persistence across browser reload or server restart)
  • Future: add TIOCSWINSZ resize support; add /pty?shell=zsh query param selection

  • cockpit-inspector.js — implements the browser side (xterm.js + WebSocket)
  • server.py — implements pty_handler() and _get_git_status()
  • docs/ATLAS-COCKPIT-PATTERNS.md — documents source patterns adopted
  • Issues: #1695, #686, #687