Talkak's core architecture: the eight technical decisions that make a terminal into a startup OS
A standing reference for what Talkak is actually made of: Tauri (Rust + React) hosting xterm.js panes whose processes live in per-pane tmux sessions; terminal state kept outside the React lifecycle; a BYO-Claude wrapper that injects the platform directive into every agent; in-band channels read from transcripts, never the TUI stream; an append-only provenance-checked graph fed by both git (PULL) and the agents themselves (PUSH); hooks as the enforcement layer; and a test gate every release must pass. Each decision exists because something broke without it.
What Talkak is, in one layer-cake
┌────────────────────────────────────────────────────────────┐
│ PLATFORM — the connective layer (the actual product) │
│ living per-startup graph: structure · issues · flows · │
│ changes · metrics, with provenance │
├────────────────────────────────────────────────────────────┤
│ AUGMENTATION — cards, summaries, status, phrases/terms, │
│ usage pulse, honesty gates │
├────────────────────────────────────────────────────────────┤
│ SUBSTRATE — multi-pane terminal where the user's own │
│ Claude/Codex do the real work (BYO, we never call AI) │
└────────────────────────────────────────────────────────────┘The terminal is not the product; it's where the data is born. Everything above it exists to turn "a solo founder running several startups through AI agents" into one legible operating picture.
The eight decisions
1. Tauri 2.x — Rust backend + web renderer, not Electron
One binary, small footprint, and the backend is real Rust: portable-pty for terminals, tokio
for async workers, tracing for runtime logs. The renderer is React + TypeScript + xterm.js.
IPC is Tauri commands (renderer→Rust) and events (Rust→renderer, e.g. the PTY output stream).
2. Every pane is a tmux session — work survives the app
Each pane spawns tmux -L dalkkak new-session -A -s dalkkak-<paneId>. The tmux server is a
separate OS process, so quitting (or crashing, or updating) Talkak kills nothing: relaunch
re-attaches and the agents are still running mid-task. This is the single most load-bearing
decision in the product — sessions have survived ~20 reinstalls in two days of heavy dogfooding.
3. Terminal state lives OUTSIDE the React lifecycle
xterm Terminal objects, PTY subscriptions, and buffers live in a module-scoped registry
(terminalRegistry.ts), not in components. React remounts freely (layout changes, re-renders);
the terminal never loses a byte. React displays; Rust owns the process; the registry bridges.
4. BYO AI, injected — the wrapper is the platform's lever
Talkak never calls an LLM itself. Instead it prepends a claude wrapper to each pane's PATH that
adds --append-system-prompt with the platform DIRECTIVE: honesty rules, the dk-summary card
contract, English refinement, and graph-push instructions. One file changes → every agent in
every startup behaves differently. AI features that need a one-shot call (phrase correction,
summaries) shell out to the user's own claude -p — their auth, their account, our zero keys.
5. In-band channels are read from TRANSCRIPTS, never the TUI stream
Agents emit <dk-summary> (session card) and <dk-node> (graph record) blocks. Parsing them out
of the PTY stream fails — a TUI redraws, wraps, and mangles text. So capture reads Claude Code's
own transcript files (JSONL on disk), incrementally, with persisted byte offsets. The stream is
for humans; the transcript is for machines.
6. The connective graph — append-only, provenance-checked, two feeds
Per-startup JSONL stores with a locked schema (schema_version, deterministic node_id,
provenance: confirmed|inferred|hypothesis). Two write paths, one validator: PULL (a 20s
worker turns git commits in the granted project folder into confirmed change nodes — evidence
is the commit hash) and PUSH (agents file structure/issue/flow facts as they work; confirmed
requires evidence and evidence.source is force-set to agent, so provenance can't be spoofed).
Security model: per-startup folder grants through a single Rust allowlist chokepoint — never
blanket disk access.
7. Hooks are the enforcement layer — the harness is the law
Claude Code hooks feed Talkak twice over. Status: every agent event appends to a JSONL stream
that powers per-pane state (thinking/tool/idle) and usage. Enforcement: a Stop-hook runs after
every reply and blocks unverified done-claims and missing summary blocks — prompt rules decay,
process-level gates don't. Scoped per-pane via the DALKKAK_PANE_ID env var that pty.rs injects.
8. The regression gate — a red test means no release
build = vitest run && tsc && vite build, and the Tauri release pipeline calls exactly that.
The tests are the product's actual scar tissue: the pane-sharing corruption (with the real
corrupted tree as a fixture), the copy-banner bug on both copy paths, the Ctrl+3=ESC chord
contract, dk-summary capture across every chunk boundary, the honesty gate's fail-open. Rust has
its own cargo test suite for the graph-push validator. Every regression that reaches the user
becomes a test the same day.
Cross-cutting laws (learned, not designed)
- Env hygiene for every spawned process: augment PATH, set TERM, and subtract hostile vars
(
npm_config_*broke nvm-installed CLIs once). - Fully consume intercepted chords —
Ctrl+3is ESC in a terminal; a half-handled shortcut silently interrupts a TUI agent. - Ownership travels with data — the layout cross-write race shipped because a save effect inferred ownership from timing.
- Self-heal persisted invariants at boot — corruption that can happen will; a sanitizer turns it into one weird launch instead of a permanent state.
- Visible failure beats silence — PTY EOF prints a marker; wrapped spawns surface their exit codes in the pane.
Stack summary
| Layer | Tech |
|---|---|
| Desktop shell | Tauri 2.x (Rust) |
| Terminal | xterm.js + portable-pty + tmux (-L dalkkak) |
| UI | React 19 + TypeScript (strict) + react-mosaic + Vite |
| Async/runtime | tokio workers, tracing + daily-rolling runtime logs |
| Data spine | append-only JSONL graph, locked schema v1, dual validators (TS + Rust) |
| AI | BYO Claude Code/Codex only — wrapper-injected directive, user's own claude -p |
| Quality | vitest (48) + cargo test (7) gating every build; weekly regression trend |
| Release | minisign-signed tar.gz + latest.json, Tauri updater, Vercel landing |