Skip to main content

Architecture: Daemon → Headless → PTY

PTY-for-AI is built on three layers. Each layer does one thing and is independently replaceable.

The Three Layers

┌───────────────────────────────────────────────────┐
│  Layer 3: Gateway / P2P                           │
│  Remote access. Cross-machine. Encrypted.         │
├───────────────────────────────────────────────────┤
│  Layer 2: Headless Server                         │
│  Network bridge. Grid rendering. Session relay.   │
├───────────────────────────────────────────────────┤
│  Layer 1: PTY Daemon                              │
│  Process management. PTY spawn. Unix sockets.     │
└───────────────────────────────────────────────────┘

Layer 1: PTY Daemon

The foundation. A persistent process that spawns and manages PTY sessions. Responsibility: Create terminal sessions, resize, close, list. Nothing more. Protocol (JSON over Unix socket):
{
  "type": "create-session",
  "cols": 120,
  "rows": 40,
  "shell": "/bin/zsh",
  "cwd": "/path/to/workspace"
}
Response:
{
  "success": true,
  "sessionId": "pty-abc123",
  "socketPath": "/path/to/session.sock"
}
Key properties:
  • Runs as OS service (launchd on macOS, systemd on Linux)
  • Survives terminal closures, reboots, SSH disconnects
  • Each session has its own Unix socket for I/O
  • Supports cwd and shell per session — this is how plugins work
  • Binary is never modified. All extension happens externally.

Layer 2: Headless Server

Bridges PTY sessions to the network without a GUI window. Responsibility: Connect to gateway, relay terminal I/O, manage grid state. What it does:
  • Creates a PTY session via daemon (Layer 1)
  • Renders terminal grid (VTE parsing)
  • Connects to gateway via WebSocket
  • Relays input/output between gateway and PTY
  • Accepts --default-cwd and --default-shell from plugins
What it doesn’t do:
  • No window. No UI. No display.
  • Does not modify terminal content.
  • Does not interpret commands.

Layer 3: Gateway / P2P

Remote access to headless sessions across machines and networks. Two paths:
Path A: Gateway relay (Cloudflare)
  Client → WSS → session.monolex.ai → WSS → Headless → PTY

Path B: P2P (WebRTC DataChannel)
  Client → DTLS → direct → Headless → PTY
Gateway for reliability. P2P for when gateway is unreachable. Both encrypted end-to-end.

How Sessions Are Controlled

# Write to session (type into terminal)
niia write --session <ID> $'your command\r'

# Wait for output to settle
niia wait-idle --session <ID>

# Read the response
niia get-answer --session <ID> "search text"
These commands work identically for local and remote sessions:
# Local
niia write --session local-123 $'claude\r'

# Remote (same syntax, different session)
niia remote write MACHINE-B $'claude\r'

Persistence Model

Traditional terminal:
  Open terminal → run command → close terminal → gone

PTY-for-AI:
  Daemon starts (boot) → sessions created → machine shuts down
  Machine starts → daemon auto-starts → sessions still accessible
The daemon is an OS service. It starts on boot and restarts on crash. Sessions persist across:
  • Terminal window closures
  • SSH disconnects
  • Network interruptions
  • Application restarts
Only explicit session close or machine shutdown ends a session.

Plugin Injection Points

Plugins transform session parameters before they reach the daemon. The daemon never knows plugins exist.
Plugin Layer (niia CLI):
  --worktree feat-x  →  cwd = /tmp/niia-worktree-feat-x
  --sandbox          →  shell = sandbox-exec -p '...' /bin/zsh

                    ↓ transformed parameters

Daemon receives:
  CreateSession { cwd: "/tmp/niia-worktree-feat-x",
                  shell: "sandbox-exec ... /bin/zsh" }

Daemon just spawns. It doesn't know about worktrees or sandboxes.
This separation means:
  • Daemon binary never changes (stable, signed, tested)
  • New plugins are added to the CLI layer only
  • Plugin development doesn’t require daemon access
  • Third-party plugins are possible (same interface)

What Each Layer Knows

PTY DaemonHeadlessGateway
What CLI is running
Session cwd✅ (at spawn)✅ (passes to daemon)
Terminal content❌ (raw bytes)✅ (VTE parsed)✅ (relayed)
Network access
Which LLM
No layer knows which AI is running inside the PTY. This is by design. LLM-agnostic at every level.