Thin Rust HTTP + WebSocket shell around catcode-core. Leptos CSR UI. One binary. No agent loop.
The browser is a render surface. Cores are server-owned and stay live when you close a tab or sign out.
./scripts/build-web-frontend.sh
cargo run --manifest-path web/Cargo.toml -- --host 127.0.0.1 --port 49283Or via the launcher (builds the WASM UI if frontend/dist is missing, points CATCODE_CORE at the in-repo release core):
./scripts/run-web.shOpen http://127.0.0.1:49283.
| Flag / env | Default | Meaning |
|---|---|---|
--host / HOST |
127.0.0.1 |
Bind address |
--port / PORT |
49283 |
Bind port |
--origin / CATCODE_WEB_ORIGIN |
http://<host>:<port> |
Public origin for cookies (Secure when https) |
--core / CATCODE_CORE |
TUI resolution | Core binary, used as-is when set |
Core resolution matches the TUI: CATCODE_CORE as-is → sibling catcode-core → core/target/release/core → PATH.
Cores live in the hub process, keyed by absolute session JSONL path.
- Close the tab: WebSocket subscribers detach. Live cores keep running.
- Sign out: this device cookie is revoked. Cores and
hub-layout.jsonstay. - Sign back in on any device: restore layout → reopen project tabs + last session → reattach mid-turn.
- Idle GC: only after 2h with zero viewers and not mid-turn. Never reap streaming or blocked (approval / ask / sudo / intercom / oauth) sessions.
- A core dies only on idle GC, explicit session delete, or hub process exit. On-disk JSONL still resumes.
Account files under ~/.config/catalyst-code/ (mode 0600, atomic tmp+rename):
account.json { name, password_hash, created_at }
devices.json [{ id, name, token_hash, user_agent, created, last_seen, revoked }]
hub-layout.json { version: 1, projects: [{ path, session, title? }], active_path }
projects.json [{ path, name, added_at }]
web-settings.json { theme, desktop_notifications }
sessions/<fnv64a-hex>/<2006-01-02_15-04-05_NNNNNNNNN>.jsonl
Session identity is the TUI's: FNV-1a 64 (0xcbf29ce484222325 / 0x100000001b3) over filepath.Abs(cwd) bytes, printed as %x.
First-run setup is display name + password (min 8). Reset: delete account.json + devices.json and restart. See .catalyst-code/skills/reset-webui-password/SKILL.md.
One WebSocket GET /ws (cookie required). Fan-in of every live core. Commands on stdin, events on stdout, protocol v2, client name catcode-hub.
The hub reconstructs AgentState in Rust (src/reduce.rs). The Leptos client applies the same reducer on live events and always re-attaches after reconnect.
# Rust: HTTP + WebSocket + frontend contract + fake-core bridge
cargo test --manifest-path web/Cargo.toml
# Leptos crate: markdown + helpers
cargo test --manifest-path web/frontend/Cargo.toml --lib
# WASM package (needs trunk + wasm32-unknown-unknown)
./scripts/build-web-frontend.sh
# Browser: Playwright against catcode-web + fake-core
(cd web/e2e && npm install && npx playwright install chromium && npm test)
cargo build --manifest-path web/Cargo.tomlLocal/dev uses this Rust binary. If an installer or release-web.sh still mentions node_modules / Next tarballs, that packaging path is a follow-up — do not mix two frontends.