operad orchestrator
Cross-platform tmux session orchestrator
Health checks · memory monitoring · auto-restart · real-time dashboard
# Architecture
┌─────────────────────────────────────────────────────────┐
│ tmx.toml ──> TMX Daemon (:18970) │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ Session Session Session │
│ (claude) (daemon) (service) │
│ │ │ │ │
│ └─────────┼─────────┘ │
│ ▼ │
│ tmux server │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ Health Memory Dashboard │
│ checks monitor SSE + REST │
└─────────────────────────────────────────────────────────┘ Session Types
claude — polls readiness, sends "go"
daemon — custom command with env
service — headless background process
Health Checks
tmux_alive, http, process, custom.
Configurable thresholds + auto-restart.
Memory Pressure
Reads /proc/meminfo in real time.
Levels: normal → warning → critical → emergency.
# Configuration
Config lives at ~/.config/operad/operad.toml.
Supports $ENV_VAR expansion and schema validation.
| Field | Type | Description |
|---|---|---|
| name | string | Unique session identifier (becomes tmux session name) |
| type | enum | claude | daemon | service |
| path | string? | Working directory ($ENV expansion supported) |
| command | string? | Command to run (daemon/service types) |
| depends_on | string[] | Sessions that must start first |
| env | object? | Extra environment variables |
| health | object? | Health check config (type, url, interval) |
| max_restarts | number? | Override global restart limit |
# CLI
tmx boot — Start daemon + all sessions in dependency order tmx status — Show daemon uptime, memory, battery, and session table tmx health — Run health checks on all sessions tmx go <name> — Send "go" to a Claude session waiting for input tmx start <name> — Start a stopped session (fuzzy-matches names) tmx stop <name> — Stop a running session tmx restart <name> — Stop + start a session tmx open <path> — Open a new Claude session dynamically tmx close <name> — Stop and remove a dynamic session from registry tmx recent — List recent Claude projects from history.jsonl tmx tabs — Open Termux tabs for all running sessions tmx memory — Show system memory and per-session RSS tmx config — Validate and display parsed tmx.toml tmx upgrade — Rebuild, shutdown daemon, let watchdog auto-restart tmx shutdown — Gracefully stop all sessions and the daemon # Dashboard
Astro 5 + Svelte 5 static site served by the daemon on port 18970.
Real-time updates via Server-Sent Events. Three pages:
Overview
System gauges (memory, battery, ADB).
Session table with start/stop/close controls.
Recent projects panel with search + play.
Memory
System memory breakdown.
Per-session RSS usage.
Android app manager (force-stop apps).
Logs
Real-time log tail via SSE.
Filter by level and session.
Auto-scroll with pause on hover.
# REST API
All endpoints served on http://127.0.0.1:18970.
SSE endpoint pushes state updates in real time.
| Endpoint | Method | Description |
|---|---|---|
| /api/status | GET | Full daemon state (sessions, memory, battery) |
| /api/events | SSE | Real-time state push via Server-Sent Events |
| /api/start/:name | POST | Start a stopped session |
| /api/stop/:name | POST | Stop a running session |
| /api/restart/:name | POST | Stop + start a session |
| /api/go/:name | POST | Send "go" to a Claude session |
| /api/open/:name | POST | Open/register a session dynamically |
| /api/close/:name | POST | Stop and remove a dynamic session |
| /api/recent | GET | Recent Claude projects from history.jsonl |
| /api/tab/:name | POST | Open Termux tab + bring to foreground |
| /api/memory | GET | System memory + per-session RSS |
| /api/processes | GET | Android app list via adb shell ps |
| /api/kill/:pkg | POST | Force-stop an Android app |
| /api/adb | GET | List connected ADB devices |
| /api/adb/connect | POST | Run wireless ADB scan |
# Session Lifecycle
pending → waiting → starting → running ↔ degraded
│
├→ failed
│
└→ stopping → stopped pending — Session defined in config but not yet processed waiting — Waiting for depends_on sessions to start starting — Tmux session created, process launching running — Process active, health checks passing degraded — Running but health checks failing failed — Exceeded max_restarts, manual intervention needed stopped — Gracefully stopped via CLI or API # Crash Resilience
Android routinely kills the Termux app — OOM killer, battery optimization, user swipe-away. TMX is designed so that every running session survives and the daemon recovers automatically.
Process Independence
When tmx boot runs, it forks the daemon and watchdog as detached
processes with PPid: 1 (parented to init). The tmux server is similarly independent.
When Android kills the Termux app, only the terminal UI process dies — all session processes,
the daemon, and the watchdog continue running undisturbed.
Socket Self-Healing
One thing does break: $PREFIX/tmp/ is a tmpfs that gets cleaned
when the Termux app crashes. The IPC socket file vanishes even though the daemon is alive.
The daemon also checks for socket presence during each health sweep (every
health_interval_s) and recreates it proactively. Between sweeps,
the CLI triggers instant recreation via the HTTP API.
Watchdog
watchdog.sh is a bash loop that monitors the daemon process.
If the daemon itself gets SIGKILL'd (Android low-memory killer), the watchdog
restarts it. Sessions remain attached to tmux and get re-adopted on restart.
Defense Layers
Wake lock
Acquired once, never released. Prevents Android from killing background processes.
Phantom fix
Disables phantom process killer, Doze standby, and app idle for Termux + Edge.
Process detach
Daemon, watchdog, and tmux all run as PPid: 1 — independent of Termux app.
Socket self-heal
IPC socket recreated via HTTP API when tmpfs is cleaned. Recovery in <1s.
Watchdog loop
Bash loop restarts daemon after OOM kill. Sessions persist in tmux for re-adoption.
Trace log
appendFileSync log survives crashes. Check last line to see what daemon was doing before SIGKILL.
# Build & Install
Clone and install dependencies
Build the bundle
Produces dist/tmx.js (~361KB CJS bundle via esbuild).
Create symlink
Ensure ~/.local/bin is in your PATH.
Create config and boot
# Requirements
● required ○ optional