29,000 Lines of Python in a Single File: How amux Runs an AI Agent Fleet

A Python HTTP server, real-time dashboard, kanban board, CRM, file browser, video player, torrent client, and AI agent orchestrator. One file. No build step. No framework. Here's why we did it, and what we learned.

amux-server.py is 29,455 lines long. It contains a ThreadingHTTPServer, inline HTML/CSS/JS for the entire dashboard, SQLite queries for 30+ tables, a self-healing watchdog, ffmpeg transcoding, tmux session management, Gemini voice chat, and a complete REST API. When you save the file, the server restarts itself via os.execv. Your running AI agent sessions survive the restart.

This is not how you're supposed to build software. We know. Here's why it works.

The problem: orchestrating AI coding agents at scale

Claude Code is powerful. One session can implement a feature, write tests, debug, and commit. But it has failure modes:

If you're running 10–20 agents in parallel—which is where the real leverage is—these failure modes multiply. You need monitoring, auto-healing, coordination, and a way to manage it all from your phone at midnight.

We tried building this as a proper multi-service architecture. React frontend. FastAPI backend. Redis pub/sub. Postgres. Docker Compose with 6 services. It lasted two weeks before we threw it away.

Why a single file?

Three reasons, in order of importance:

1. The edit-reload loop is zero seconds

The server watches its own mtime. Save the file, and it validates the Python syntax with ast.parse(), then calls os.execv() to replace itself in-place. The process ID doesn't change. Tmux sessions don't notice. Your 15 running agents keep working.

# From amux-server.py — the self-restart mechanism
script = Path(__file__)
while True:
    time.sleep(1)
    if script.stat().st_mtime != last_mtime:
        time.sleep(3)  # debounce rapid edits
        ast.parse(script.read_text())  # syntax check
        os.execv(sys.executable, [sys.executable, str(script)] + sys.argv[1:])

This means the development loop for the dashboard, the API, and the agent watchdog is: edit → save → reload browser. No npm run build. No docker compose up. No waiting for TypeScript compilation. The feedback loop approaches the speed of editing a static HTML file.

2. Deployment is scp

Our cloud deployment pipeline for multi-tenant containers:

rsync --inplace amux-server.py root@cloud:/opt/amux-cloud/app/amux-server.py
touch /opt/amux-cloud/app/amux-server.py  # trigger auto-restart in all containers

Every container bind-mounts the same file. Touch it, and every user's server reloads within 3 seconds. No image builds. No rolling deploys. No container registry. The same file runs on your laptop and in production Docker containers—we enforce this with a rule: no if IS_CLOUD branches. Configuration differences come from environment variables and gateway headers, never from code forks.

3. AI agents can read the entire codebase in one context window

This was the unexpected win. When a Claude Code session needs to fix a bug in amux itself, it reads one file. It sees the API endpoint, the HTML that calls it, the CSS that styles it, and the JavaScript that handles the response. No jumping between src/components/Board.tsx and server/routes/board.py and shared/types.ts. The full call chain from button click to database write is visible in one scroll.

This matters because amux is largely built by amux. We run parallel agents that modify their own orchestrator. The single-file architecture makes self-modification tractable.

What's actually in the file

A rough breakdown of the 29,455 lines:

SectionLinesWhat it does
HTML/CSS~9,500Full dashboard: session cards, kanban board, notes editor, CRM, file browser, terminal, map view, settings
JavaScript~13,000Dashboard logic: SSE real-time updates, Quill editor, XTerm.js terminal, Gridstack layout, video player, voice chat (Gemini Live API), service worker
Python server~5,000HTTP handlers, REST API (60+ endpoints), SQLite queries, file streaming, ffmpeg transcode
Agent watchdog~1,500Status detection (ANSI parsing), auto-compact, auto-restart, corruption recovery, stuck detection, stale process reaper
Infrastructure~500TLS setup, auth, self-restart, server.env loading, signal handling

The watchdog: keeping agents alive at 3am

The core loop runs every 10 seconds. For each active tmux session, it:

  1. Captures outputtmux capture-pane, strip ANSI codes, detect status (working/idle/waiting/error)
  2. Checks context health — parses "context left until auto-compact: 18%" from output, triggers /compact if below 20%
  3. Detects corruption — watches for redacted_thinking ... cannot be modified, auto-restarts and replays last message
  4. Unblocks stuck prompts — detects safety prompts, auto-answers in YOLO mode (with cooldowns to prevent loops)
  5. Reaps stale processes — if a Claude process has been running for 48+ hours in an idle session, kills and restarts it (the API connection dies silently after ~2 days)

Every recovery action has a cooldown (5 minutes for compaction, 1 minute for restart) and pushes a notification to the dashboard. You can review everything that happened overnight from your phone.

Multi-agent coordination without a message queue

The conventional approach to agent-to-agent communication would involve Redis pub/sub, or a message queue, or at minimum a shared database with polling. We use SQLite with compare-and-set for task claiming:

# Atomic task claim — prevents two agents from grabbing the same work
cursor.execute("""
    UPDATE issues SET status='doing', session=?
    WHERE id=? AND status IN ('todo','backlog')
""", (session_name, task_id))
# Verify the claim succeeded (another agent might have claimed it first)
cursor.execute("SELECT session FROM issues WHERE id=?", (task_id,))
if cursor.fetchone()[0] != session_name:
    return self._json({"error": "already claimed"}, 409)

Agents discover each other via REST:

# Agent A posts a task for Agent B
curl -X POST /api/board -d '{"title":"Implement auth","session":"worker-b"}'

# Agent B claims it atomically
curl -X POST /api/board/PROJ-5/claim -d '{"session":"worker-b"}'

# Agent A peeks at Agent B's progress
curl /api/sessions/worker-b/peek?lines=50

# Agent B marks it done when finished
curl -X PATCH /api/board/PROJ-5 -d '{"status":"done","desc":"Implemented OAuth2 flow"}'

No Kafka. No RabbitMQ. No gRPC. Just HTTP endpoints backed by SQLite WAL mode. It handles 20+ concurrent agents without breaking a sweat because agent coordination is fundamentally a low-throughput problem—you're talking about dozens of task transitions per hour, not thousands per second.

The dashboard is inline HTML, and that's fine

The entire frontend is a single string literal inside the Python file. No JSX. No virtual DOM. No component library. The JavaScript is vanilla, with some targeted use of CDN libraries:

Real-time updates use Server-Sent Events (SSE), not WebSocket. SSE is simpler, works through proxies and load balancers, and reconnects automatically. The server pushes events on board changes, session status updates, and new notifications. The client subscribes once and stays connected.

Is the JavaScript code beautiful? No. Is it fast to modify and test? Extremely. When an agent needs to add a feature to the dashboard, it edits one file, the server reloads, and the change is live. No webpack. No tree shaking. No hydration.

Cloud: same file, Docker per user

The cloud deployment (cloud.amux.io) runs the exact same amux-server.py in per-user Docker containers. A Python gateway handles authentication (Clerk), billing (Stripe), and container lifecycle.

The single-codebase rule is enforced strictly: there are zero if IS_CLOUD branches in the code. Features that differ between self-hosted and cloud are driven by:

Data replication uses Litestream to stream SQLite WAL files to Cloudflare R2. Each user's database is continuously replicated. A daily backup job also snapshots everything (retained for 30 days). If a container dies, it restarts with full data intact.

What we'd do differently

Honesty about trade-offs:

Numbers

Lines of code29,455
Build stepNone
External services requiredNone (SQLite for everything)
Deploy time (cloud, all users)~3 seconds
Server restart on edit~1 second
Parallel agents tested20+ on a single machine
SQLite tables30+
REST API endpoints60+
External runtime dependenciesPython 3, tmux, Node.js (for Claude Code)

Try it

git clone https://github.com/mixpeek/amux && cd amux && ./install.sh
amux register myproject --dir ~/Dev/myproject --yolo
amux start myproject
amux serve   # https://localhost:8822

The whole thing is MIT + Commons Clause. Self-host it, read the source, modify it. It's one file.

github.com/mixpeek/amux