This document explains how Tandem clients communicate with tandem-engine, how runs stream, and how desktop/TUI coordinate engine lifecycle.
tandem-engine(Rust binary): the shared HTTP + SSE runtime and source of truth.- Tauri desktop app (
src/+src-tauri/): the native client shell; its rootindex.htmlis the desktop UI entrypoint, and it starts/stops an engine sidecar when needed. - TUI (
crates/tandem-tui): a terminal client that attaches to an existing engine when available, otherwise bootstraps/spawns one. - Web control panel (
packages/tandem-control-panel/): a browser-based client plus service bootstrap layer that connects to the same engine runtime.
- Host:
127.0.0.1 - Default port:
39731(moved away from3000to avoid common frontend dev collisions) - Desktop sidecar behavior:
- Prefer configured/default port.
- If unavailable, fall back to an ephemeral local port.
- TUI behavior:
- Try configured/default base URL first.
- If not healthy, spawn engine with configured/default port.
TANDEM_ENGINE_PORT- Engine CLI
servedefault (--port) via clap env binding. - Desktop sidecar preferred port default.
- TUI connect/spawn port default.
- Engine CLI
TANDEM_ENGINE_HOST- Engine CLI
servedefault (--hostname) via clap env binding.
- Engine CLI
TANDEM_ENGINE_URL- TUI explicit base URL override (takes precedence over host/port composition).
TANDEM_API_TOKEN- When set, engine requires
Authorization: Bearer <token>orX-Tandem-Token: <token>for API calls (except health).
- When set, engine requires
TANDEM_SHARED_ENGINE_MODE- Desktop/TUI shared-engine behavior toggle.
Core session/run endpoints:
POST /sessioncreate sessionGET /sessionlist sessionsPOST /session/{id}/messageappend messagePOST /session/{id}/prompt_asyncstart async runPOST /session/{id}/prompt_syncsync run pathGET /session/{id}/runinspect active runPOST /session/{id}/run/{run_id}/cancelcancel by run IDPOST /session/{id}/cancelcancel active runGET /eventSSE streamGET /global/healthreadiness/phase/build info
Compatibility aliases under /api/... are maintained where noted in server routes.
- Resolve sidecar binary path (bundled/update/dev fallbacks).
- Pick port (
TANDEM_ENGINE_PORTor default39731, fallback ephemeral if occupied). - Spawn
tandem-engine serve --hostname 127.0.0.1 --port <port> --state-dir <canonical>. - Poll
/global/healthuntil ready. - Route UI actions to engine HTTP APIs through
SidecarManager. - Subscribe once to
/eventand fan out viastream_hub(sidecar_event+sidecar_event_v2).
Reference code:
src-tauri/src/sidecar.rssrc-tauri/src/stream_hub.rssrc-tauri/src/commands.rs
- Compute base URL:
TANDEM_ENGINE_URLif set- else
http://127.0.0.1:<TANDEM_ENGINE_PORT|39731>
- Health-check existing engine.
- If unavailable, ensure/download binary and spawn:
tandem-engine serve --port <configured_port>
- Use HTTP APIs directly through
EngineClient.
Reference code:
crates/tandem-tui/src/app.rscrates/tandem-tui/src/net/client.rs
Recommended async pattern:
- Append user message to session (
/session/{id}/message). - Start run with
POST /session/{id}/prompt_async?return=run. - Read response
runIDandattachEventStream. - Stream events from
/event?sessionID=<id>&runID=<runID>. - On reconnect, recover via
GET /session/{id}/runthen re-attach. - Cancel with
/session/{id}/run/{run_id}/cancel(preferred) or/session/{id}/cancel.
This is the contract used by desktop and validated in server/sidecar tests.
Engine emits permission/question requests during tool execution:
- Pending permissions:
GET /permission - Reply:
POST /permission/{id}/reply - Pending questions:
GET /question - Reply:
POST /question/{id}/reply - Reject:
POST /question/{id}/reject
Desktop/TUI map these into their request-center UI flows.
- Health/readiness:
GET /global/health
- Structured logs:
- Desktop:
tandem.desktop.*.jsonl - Engine:
tandem.engine.*.jsonl - TUI:
tandem.tui.*.jsonl
- Desktop:
- Correlation fields:
correlation_id,session_id,run_id
- Engine binds to loopback (
127.0.0.1) by default. - Optional token auth is supported via
TANDEM_API_TOKEN(or runtime token endpoints) for exposed deployments. - Desktop sidecar mode auto-generates a local API token, injects it into sidecar env (
TANDEM_API_TOKEN), and sendsX-Tandem-Tokenon requests. - Token persistence is keychain-first with fallback to the shared token file path.
- TUI uses the same shared token material and also sends
X-Tandem-Token. - Desktop Settings exposes token management UX: masked by default, explicit reveal, and copy.
- Keep
39731as the default shared port for predictable desktop/TUI attach behavior. - Use
TANDEM_ENGINE_PORTwhen running multiple isolated dev stacks. - Use
TANDEM_ENGINE_URLin TUI for explicit remote/forwarded test setups. - Avoid
3000for engine defaults to reduce collisions with frontend dev servers. - For headless installs, prefer
npm i -g @frumu/tandemfollowed bytandem install panelandtandem panel initwhen you want the control-panel gateway layer. Usetandem-engine servedirectly when you want the raw engine exposed only on localhost.