Architecture
The Elm Architecture (TEA) #
All state lives in a single
App
model. Events become messages,
update()
applies them,
view()
renders the result.
Event → Message → update(model, msg) → view(model) → Frame
TEA makes state transitions explicit and testable. Every input has a traceable path from event to screen change. There's no hidden state scattered across components.
Event loop
tokio::main → init backends (BackendRegistry)
→ open SQLite DB
→ init terminal
→ spawn/restore sessions
→ loop {
draw frame → poll crossterm events (10ms)
→ convert to AppMessage
→ app.update()
→ app.tick()
}
→ app.shutdown() (detach sessions)
→ restore terminal
Module Structure #
Dependencies flow strictly one-directionally. Enforced by
tests/architecture_rules.rs
.
session ← pure data types, no project-local imports
project ← imports session only
agent ← imports session only (NEVER ui, git, or project)
ui ← imports session and project only (NEVER agent or git)
mcp ← imports storage, session, project, sync, paths only
app ← coordinator, imports all modules
Module responsibilities
| Module | Responsibility |
|---|---|
app/ |
Model (
App
struct), Update (
AppMessage
+ handlers), View. Owns all state, coordinates side effects.
|
agent/ |
Side-effect layer.
AgentProvider
trait abstracts CLI command construction.
Session
wraps
SessionBackend
.
BackendRegistry
manages multiple backends.
ContainerManager
and
VmManager
handle lifecycle.
|
session/ |
Plain data types:
SessionId
,
SessionStatus
,
SessionInfo
,
SessionConfig
,
VmState
,
ContainerState
. No logic beyond Display/Default.
|
project/ |
Plain data:
ProjectId
,
ProjectConfig
,
ProjectInfo
. Imports session only.
|
ui/ |
Pure rendering functions.
layout.rs
computes panel areas (responsive breakpoints). Widgets: project_list,
terminal_view, info_panel, status_bar.
|
mcp/ |
MCP server (
thurbox-mcp
binary). Exposes CRUD over stdio or Streamable HTTP JSON-RPC.
|
Session Pipeline #
A
SessionBackend
trait abstracts session lifecycle. The default is
LocalTmuxBackend
(
tmux -L thurbox
).
vt100::Parser
interprets escape sequences,
tui_term::PseudoTerminal
renders into ratatui.
Backend trait methods
-
check_available— verify backend prerequisites -
ensure_ready— initialize backend resources -
spawn()— returns(backend_id, output_reader, input_writer) -
adopt()— reconnect to existing session, returns initial screen content -
discover()— list existing sessions for restore-on-startup -
resize()— update terminal dimensions -
detach()— stop streaming without killing session -
kill()— permanently destroy session
tmux details
Control mode (
-C
) supports multiple concurrent client connections. Output arrives as
%output
notifications (octal-encoded), input is sent via
send-keys -H
(hex-encoded). Configuration on init includes
remain-on-exit on
,
extended-keys on
, and flow control via
pause-after
.
Multi-Backend Architecture #
Sessions select their backend at creation time via a
BackendRegistry
. Each session stores its
backend_type
in the database.
| Backend | Transport | Use Case |
|---|---|---|
LocalTmuxBackend |
tmux -L thurbox |
Default, local sessions |
DevcontainerBackend |
tmux over
docker/podman exec
|
Container isolation |
QemuVmBackend |
tmux over SSH | Full VM sandbox |
Async Runtime #
The app runs on tokio's multi-threaded runtime. PTY read loops run inside
spawn_blocking
(blocking I/O in a threadpool), while PTY write and event handling run in
tokio::spawn
(async).
PTY reads are blocking by nature. Putting them in
spawn_blocking
prevents stalling the async executor and freezing the UI.
Persistence #
All state is stored in SQLite at
~/.local/share/thurbox/thurbox.db
. Multiple instances synchronize via
PRAGMA data_version
polling (250ms). SQLite WAL mode handles concurrent access.
Multi-instance sync
- Database : Atomic transactions, no race conditions
- Session I/O : Each instance independently connects to tmux control mode. Tmux broadcasts output to all clients.
-
Input
: Commands serialized by tmux (same as
tmux attachwith multiple clients)
Core Principles #
Non-negotiable rules that define what Thurbox must always be. Each has an automated enforcement mechanism.
| # | Principle | Enforcement |
|---|---|---|
| 1 | Crash-free operation | Clippy + code review |
| 2 | Module isolation | tests/architecture_rules.rs |
| 3 | Zero-warning policy |
clippy -D warnings
+
RUSTDOCFLAGS="-D warnings"
|
| 4 | Permissive licenses only | cargo-deny check bans licenses |
| 5 | Zero known vulnerabilities | cargo-deny check advisories |
| 6 | Conventional commits |
cocogitto (
cog verify
)
|
| 7 | TEA as single pattern | Architecture tests + code review |
| 8 | Backend-first sessions | Code review |
| 9 | Logging never touches stdout | Code review |
| 10 | Test-driven development | cargo-nextest |
| 11 | Deterministic CI | Scripts over LLMs |
| 12 | Tag-based versioning |
build.rs
+ release workflow
|
Key technical details
- MSRV: 1.75, Edition 2021
- Async runtime: tokio (multi-threaded)
-
Terminal state:
vt100::Parser+tui_term::PseudoTerminal -
Logging: file-based at
~/.local/share/thurbox/thurbox.log - Panic hook restores terminal before printing
- Requires tmux >= 3.2