The whole thing, on one screen
╔══════════════ THE READ POOL ══════════════╗
║ agent transcripts on disk (a LOCATION) ║
║ /root/.claude/projects/… ~/.codex/… ║
╚═══════════════════╤═══════════════════════╝
each reader watches independently — no shared tailer
┌────────────────────────┴───────────────────────┐
▼ ▼
orb-streamer × 7 (Rust) celest-chat (Tauri)
per seat · :8201–8207 · per-engine adapters its own transcript.py
file-watch → SSE file-glob reader
│ SSE │
▼ ▼
O.R.B phone (Kotlin) CelestOS UI
· ·
─────────────────── THE WRITE FUNNEL ───────────────────
O.R.B ──POST /prompt──▶ orb-streamer ──shell──▶ sms ─┐
any seat / human ─────────────────────────▶ sms CLI ─┤
celest-chat ──────────────▶ relay.py ───────────▶ ─┤
▼
╔═══════════════════════════╗
║ injectd ◀── THE NEXUS ║
║ /tmp/injectd.sock ║
║ kitten @ send-text ║
╚═════════════╤═════════════╝
▼
the target seat's live pane
The nexus is asymmetric — and that's the insight
The interesting part isn't that everything connects. It's how: the two primitives converge in completely different ways.
◇ READ → a location
There is no shared read daemon. Each surface watches the transcript files itself — the 7 orb-streamers each spawn a per-seat file watcher, and celest-chat runs its own independent glob reader over the same directories.
What everyone agrees on is a place on disk, not a process.
◆ WRITE → one process
Every inject path — the phone's /prompt, all 7 streamers, the sms CLI, celest-chat's relay.py — bottoms out at one daemon. Even orb-streamer doesn't inject directly: its prompt_command shells out to sms.
No write path bypasses it. One socket, one keystroke-injection function.
injectd · /tmp/injectd.sock.
So a dev orienting on this system only needs two anchors: to see what an agent is
doing, tail the transcript pool; to make an agent do something, everything routes through
injectd. Learn those two and the whole fleet — CLI, phone, web — stops
being a dozen mysteries and becomes one loop with two ends.
The components
| Component | Lang | Where | Role |
|---|---|---|---|
| injectd | Python | /tmp/injectd.sock |
WRITE nexus — the only thing that calls kitten @ send-text; every inject funnels here. |
| transcript pool | — | /root/.claude/projects/…, ~/.codex/sessions |
READ nexus — the shared on-disk location every reader watches independently. |
| orb-streamer | Rust (Axum) | 7 instances · :8201–8207 |
READ (per-seat file-watch → SSE) + WRITE (/prompt → shells sms). Per-engine adapters normalise each transcript format. |
| sms | Python | /usr/local/bin/sms |
Roster-aware WRITE client — every sms <seat> terminates at injectd's socket. |
| O.R.B | Kotlin | Android · talks to :8201–8207 |
Phone UI — pure client of the streamer SSE feed + /prompt return lane. |
| celest-chat | Python / Tauri | celest-chat-fafo.service |
Web presentation — own transcript reader + own write path (relay.py → injectd). |
| DeltaMesh | Rust | :8300 |
File-transfer lane (phone ⇄ PC) — parallel to the loop, not part of read/write. |
| agents.json | JSON | /etc/ctx/agents.json |
The one roster of record — 10 seat ids; every router resolves seats here. |
Scope & roadmap
The substrate is live and load-bearing, and it's built to grow one seam at a time. The
streamer fleet is standardising on a single Rust runtime with per-engine adapters, migrated
seat by seat rather than in a risky big-bang cutover; the file-transfer lane and the phone
surface evolve behind the same stable read/write contract. The two anchors — the
transcript pool and injectd — stay fixed while the
surfaces around them iterate.