Architecture
How the Go daemon, Electron app, storage, controllers, and adapters fit together.
Tallpine is a local, single-user system built around a long-running Go daemon. The Electron/React desktop app, optional CLI, and Expo mobile app connect to it over HTTP, server-sent events (SSE), and terminal streams.
This page is a technical reference for contributors and users who want to understand Tallpine internals. For everyday controls and workflows, start with the Dashboard guide.
System overview
Electron + React desktop ─┐
ao CLI ──────────────────┼─> Go daemon ─> services/managers ─> SQLite
Expo mobile (opt-in) ┘ │ │
│ ├─> agent/runtime/workspace adapters
├─> SSE events ├─> native Chat drivers
├─> terminal mux └─> GitHub/GitLab SCM observer
└─> browser bridge to ElectronThe desktop app finds, starts, and supervises the daemon. Its renderer uses a generated typed client to call the API. The CLI is also an HTTP client; it does not open SQLite or launch agent adapters directly.
Sessions and worktrees
Workers in Git projects use isolated worktrees. A workspace can include its root repository and child repositories. Scratch and standalone sessions use Tallpine-managed directories. Each session uses one agent interface at a time:
- Terminal UI runs the harness in a detached native pseudo-terminal (PTY) host on macOS/Linux. Older sessions and fallback launches can use tmux. Windows uses ConPTY.
- Chat runs a native provider controller without an agent terminal runtime.
Tallpine saves Chat conversation history, including provider identity, messages, activities, approvals, structured input, usage, compaction, and rollback state.
Compatible Claude Code and Codex sessions can move the same provider conversation between Terminal UI and Chat. Tallpine finishes or interrupts the old controller before starting the replacement, so both controllers are never active at once. The session and worktree stay the same.
Saved facts and derived status
Tallpine saves session facts and derives the status shown in the app when it reads a session. Saved facts include:
- Agent activity: active, idle, waiting for input, blocked, or exited.
- Whether the session has been terminated.
- The current interface mode, controller handle, and generation.
- Interface-transition checkpoints and queued messages.
- Pull request, check, and review-comment facts.
The service layer combines these facts to show labels such as working, needs input, CI failed, or ready to merge. This keeps a displayed status tied to the underlying activity and pull-request data.
Failed or unknown runtime probes are observations, not proof of death. Cleanup also refuses to force-delete a dirty registered worktree.
Storage and live updates
Tallpine stores local data in SQLite under ~/.tallpine/data by default. Database triggers append changes to change_log. A background poller reads that log and tells in-process subscribers and GET /api/v1/events which data to refresh.
The event stream supports replay with Last-Event-ID. Clients use it to invalidate targeted queries rather than transferring every full session or Chat payload on each update.
All application state stays under ~/.tallpine, including the run file, managed worktrees, Electron data, and mobile configuration. TALLPINE_DATA_DIR and TALLPINE_RUN_FILE override the daemon's data and discovery paths.
Services and adapters
HTTP controllers validate requests and call domain services. Services decide how data is read and changed. Session and lifecycle managers coordinate multi-step work. External systems connect through Go interfaces and adapters.
Adapters currently support more than twenty agent harnesses, native PTY and ConPTY hosts, tmux for older sessions and fallback launches, Git worktrees, native Chat drivers, and GitHub/GitLab source-control observation. Tallpine uses the provider binaries and sign-ins already installed on the machine.
Issue intake from GitHub or GitLab requires opt-in in both daemon and project settings. Tallpine reads issues but does not change them. Recurring Automations run through a local scheduler. Project Cues save reusable prompts or commands. These features are separate from repository lifecycle reactions.
Pull requests and lifecycle reactions
The source-control observer connects to GitHub and GitLab. GitHub authentication happens only when needed. Tallpine uses HTTP ETags to avoid fetching unchanged data and compares changed pull-request details before updating SQLite. The observer notifies lifecycle logic when CI fails, a review requests changes, or a merge conflict appears.
The daemon also exposes pull-request merge and comment-resolution actions plus reviewer-agent routes. Desktop clients receive concise summaries rather than raw CI logs or complete review bodies.
Network boundaries
The primary daemon listener binds 127.0.0.1:3101 and is unauthenticated. It serves the desktop app and CLI and is never configurable to a public bind.
Connect Mobile starts a second listener only when you enable it. The listener is protected by a bearer password and serves the mobile app API and terminal connection. It does not serve shutdown, mobile-control, or browser-control routes.
Direct connections use plain HTTP and are intended for trusted networks. A managed connector can provide an HTTPS endpoint to this authenticated listener for remote access. Current Tailscale pairing has additional limits described in the access guide. Only GET /api/v1/identity is public; the phone uses it to verify the computer and app version before sending the password. See Connect Mobile.
Terminal and browser bridges
/mux lets clients connect to a terminal. Terminal UI sessions attach to the agent runtime. Chat sessions can use a separate shell in the worktree.
Browser automation uses a local connection between the daemon and Electron. The daemon authorizes and matches commands to results. Electron displays each session's browser in an isolated WebContentsView. The mobile listener cannot control the browser.
The temporary browser profile is isolated per session. Named profiles save cookies and storage and can be reused across sessions. Sessions using the same named profile share its browser state. Tallpine copies imported browser data into Tallpine-owned storage and does not change the source profile.
The architecture above describes local sessions. Cloud execution is a separate gated integration requiring build enablement, account access, and a reachable control plane. Standalone local workers are projectless; they are not Cloud sessions.
Load-bearing rules
- Never store display status; derive it from durable facts.
- Never treat failed probes as proof that a session died.
- Never force-delete dirty worktrees.
- Keep all app state under
~/.tallpineunless an explicit supported override applies. - Keep the primary listener on loopback; only Connect Mobile may create the authenticated opt-in LAN listener.
- Keep the CLI and UI thin; domain logic belongs in the daemon.
- Emit change events through SQLite triggers and the background poller.
- Add new migrations; never modify an already-merged migration.