$ tmx boot

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.

~/.config/operad/operad.toml
# Global settings
boot_timeout_s = 120
poll_interval_s = 30
max_restarts = 3
# Claude Code session
[[session]]
name = "my-project"
type = "claude"
path = "$HOME/git/my-project"
# Daemon with custom command
[[session]]
name = "bridge"
type = "daemon"
command = "node bridge/dist/cli.js"
depends_on = ["my-project"]
# Health check
[session.health]
type = "http"
url = "http://127.0.0.1:18963/health"
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
adb fixed
cleverkeys started (claude)
bridge started (daemon)
playwright started (service)
~ $ tmx status
tmx daemon uptime 4h 23m
NAME STATUS RSS UPTIME HEALTH
cleverkeys running 245MB 4h 23m ok
bridge running 38MB 4h 22m ok
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

tmux server (PPid: 1)
│── claude-session-1 ── node claude
│── claude-session-2 ── node claude
├── service-session ─── custom cmd
 
tmx daemon (PPid: 1, detached)
│── IPC socket: $PREFIX/tmp/tmx.sock
├── HTTP dashboard: :18970
 
watchdog.sh (PPid: 1, bash loop)
 
Termux app killed → only the terminal UI dies
All three process trees survive (parented to init)

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.

recovery flow (<1s)
1. CLI runs tmx status
2. Socket missing → probe HTTP :18970 → daemon alive
3. CLI POSTs /api/fix-socket
4. Daemon re-creates IPC socket immediately
5. CLI connects via socket → normal operation resumes

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.

watchdog loop
while true:
check daemon_alive() via IPC + HTTP
if dead → tmx boot (daemon re-adopts sessions)
sleep 60

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

1

Clone and install dependencies

$ git clone https://github.com/tribixbite/operad
$ cd operad
$ bun install
2

Build the bundle

$ bun run build

Produces dist/tmx.js (~361KB CJS bundle via esbuild).

3

Create symlink

$ mkdir -p ~/.local/bin
$ ln -sf $(pwd)/dist/tmx.js ~/.local/bin/tmx
$ chmod +x ~/.local/bin/tmx

Ensure ~/.local/bin is in your PATH.

4

Create config and boot

$ mkdir -p ~/.config/tmx
# Edit ~/.config/tmx/tmx.toml (see config section above)
$ tmx boot

# Requirements

tmux Node.js 18+ or Bun 1.0+ Android/Termux or Linux or macOS ADB

required   optional