# Process boundaries

> Which process runs each part, how calls cross the boundaries, how sessions relate to worktrees and threads, and where to start reading the source.

Handbook / Architecture · Checked against the source on 2026-10-05 · https://www.hbcode.ai/docs/architecture

## The implementation stack

HB Code has four application boundaries and two agent process boundaries. The desktop frontend is React and TypeScript inside a Tauri 2 WebView. Its Rust backend owns local commands, SQLite product state, the remote RPC router, and both agent processes: the Codex app-server child process and the Claude agent host, a Node process that runs the Claude Agent SDK. A session picks its agent when it’s created and keeps it. The server is Rust with Actix Web, Tokio, SQLx for PostgreSQL, and Redis integration. The iOS companion uses Swift and SwiftUI, with UIKit for its chat collection view, and the Android companion uses Kotlin and Jetpack Compose. Both phones speak the same RPC contract to the desktop.

**The WebView, the agents, and your phones exchange data only through the Rust runtime**

Inside the dashed line is your computer. The WebView talks to the Rust runtime through Tauri commands and events, and the Codex app-server runs as a child process that exchanges JSON-RPC lines with the runtime over stdin and stdout. The runtime owns appdata.db and reads the project folder and Git itself. Beside the Codex child, a Claude session runs through the Claude agent host, a Node process that the runtime starts for each run and talks to in JSON lines over stdin and stdout. Both agents run commands and edit files in the project. Codex keeps transcripts and its ChatGPT sign-in in its own profile home and calls OpenAI. Claude keeps them in ~/.claude and calls Anthropic. Every arrow that crosses the dashed line starts inside it: the desktop opens a WebSocket to the regional server, each phone opens its own socket to the same relay, and each agent calls its provider over HTTPS.

- **Rust runtime** (chat · agents · remote_api): Serves the WebView’s commands and the phones’ RPC with the same domain services. It owns appdata.db and reads project files and Git itself.
- **Codex app-server** (--listen stdio://): A child process, one per profile pair, and no more than three stay running unless all are busy. The runtime reads its history over the pipe with thread/items/list, never from its files.
- **Claude agent host** (desktop/agent-host): A Node process that runs the Claude Agent SDK, which starts the claude executable. The runtime starts one host per run and one-shot hosts for reads, so no Claude process outlives its work.
- **Regional server** (/ws/device-link): Runs in the US or the EU and passes messages between the socket the desktop opened and each phone’s socket. It keeps accounts, device rows, and presence, never project files or transcripts.

- [How Codex and Claude share one chat layer](https://www.hbcode.ai/docs/agents.md): What Codex and Claude share, what each adapter owns, and what the dispatch layer decides.

## Trace a mobile request to execution

**A phone request and a desktop click call the same service**

Two paths end in one service. On the left, a phone request first crosses the relay, which checks the method, the idempotency key, and whether the desktop is connected. Inside your computer, the RPC intake waits for a free slot, matches the account, looks up the idempotency key, and decodes the params. On the right, a click in the WebView passes only the Tauri check that its window may call the command and the decoding of its arguments. Both paths merge into submit_outbox_request, which runs three checks for every caller: text or attachments, a valid send-mode pair, and an ability that the session’s agent has. Then it saves the message and returns a receipt. A drain task later hands the message to agents/dispatch, which starts the turn in the session’s agent: the Codex child or a Claude agent host.

- **Relay** (request_validation.rs): Checks the method name and the idempotency key and leaves the params to the desktop, except for session.syncHistoryState. When the desktop is offline or reconnecting, it answers -32010 or -32011 itself.
- **RPC intake** (snapshot_and_rpc.rs · router): Runs for every phone request: 8 light and 2 heavy slots, then the account match. A mutation takes a durable idempotency claim, and the adapter decodes the params into a request type that rejects unknown fields.
- **Tauri command** (capabilities/default.json): Lets the main and session windows call workspace commands. Tauri decodes the arguments into the same request type, so unknown fields fail here too.
- **Outbox service** (submit_outbox_request): Both paths call it, so its rules hold for every caller, and a repeated operationId never adds a second message. It rejects a replacement of a sent message or a speed mode in a Claude session. It saves the message and returns the receipt, and a drain task starts the turn later.

Keep business rules in the domain service, where both paths meet. The service also rejects what the session’s agent can’t do: a replacement of a sent message or a speed mode in a Claude session. The saved message names no agent process. A drain task later hands it to agents/dispatch, which starts the turn in the Codex app-server child process or in a new Claude agent host. Matching UI controls are not an authorization check. The relay also answers -32012 when a desktop does not finish a request before the relay’s timeout, and the relay chapter lists its codes and timers.

## Projects, worktrees, and sessions

**Only a separate worktree keeps two tasks’ files apart**

Three devices pick sessions on their own. The desktop and the iPhone show the same Dark mode session, and the Android phone shows Login fix. Settings page and Dark mode work in the main worktree and both edit its src/settings.tsx, with no lock between them. Login fix and its Regression test subagent thread work in the linked worktree, a separate checkout with its own copy of src/settings.tsx. Both worktrees belong to one project. When a linked worktree is deleted, the next scan moves its idle sessions to the main worktree and skips running ones.

- **Worktree** (git worktree list): HB Code lists the worktrees that exist and never creates one. A session is created in one of them, and the desktop rejects a projectDirectory that is not a worktree of the project.
- **Subagent thread** (activeRun: null): Spawned by the agent during its parent’s turn, in the parent’s worktree. It has its own timeline and live overlay, and only the parent reports the active run.
- **Device selection** (appSessionId): Each device picks its project, worktree, and session by itself, so two devices can show the same session or different ones. A subagent thread is opened through its session.

A project is a Git repository, grouped under its main worktree. A folder without Git is a project whose only workspace is the folder itself.

## Navigate the source tree

| Path from repository root | Start here for |
| --- | --- |
| desktop/src/app/components/workspace-chat/ | React workspace composition, timeline state, viewport ownership, and outbox display. |
| desktop/src-tauri/src/commands/ | Tauri entry points and desktop domain operations. |
| desktop/src-tauri/src/chat/ | The shared chat layer: outbox, operation ledger, run registry, live timeline overlay, presentation rows, and chat Tauri entry points. It never imports an agent adapter. |
| desktop/src-tauri/src/agents/codex/ | The Codex adapter: app-server child process, auth profiles, turns, history through thread/items/list, and the command output store. |
| desktop/src-tauri/src/agents/claude/ | The Claude adapter: run loop, projection into the shared rows, history, subagents, permissions, accounts, and the agent host runtime. |
| desktop/src-tauri/src/agents/dispatch/ | The code that picks the adapter of a session. The chat layer reaches an adapter only through this module. |
| desktop/src-tauri/src/usage_history/ | Saved plan-limit readings of both agents. It reads both adapters directly, because it reads both account types. |
| desktop/src-tauri/src/services/agent_tools/ | The HB Code tool catalog that both agents get, and the tool handlers. |
| desktop/agent-host/ | The Node agent host in TypeScript. It runs the Claude Agent SDK and speaks JSON lines over stdin and stdout. |
| desktop/src-tauri/src/remote_api/ | RPC types, validation, idempotency, dispatch, and remote method adapters. |
| server/src/services/device_link_ws/ | WebSocket registration, request validation, forwarding, and connection lifecycle. |
| mobile/ios/Core/Sources/Core/Connectivity/Relay/ | iOS relay connection and method contract. |
| mobile/ios/VibeUI/Sources/VibeUI/Features/Workspace/Chat/ | iOS chat ownership, timeline reconciliation, and collection rendering. |
| mobile/android/app/src/main/java/com/hbcode/mobile/ | Android entry routing, workspace tabs, relay client, and Compose screens. |

- [How Codex and Claude share one chat layer](https://www.hbcode.ai/docs/agents.md#layers): The import rule between chat, dispatch, and the two adapters, and one row per dispatch file.
- [The Codex app-server integration](https://www.hbcode.ai/docs/codex-app-server.md): The child process, the JSON-RPC pipe, tool injection, and profile-scoped clients.
- [The Claude Agent SDK integration](https://www.hbcode.ai/docs/claude-agent-sdk.md): The agent host process, history projection, live runs, subagents, and accounts.
- [Message delivery](https://www.hbcode.ai/docs/message-delivery.md): Follow an operation from durable custody to a running turn.
- [Storage ownership](https://www.hbcode.ai/docs/storage.md): Database ownership and conversation source identity.
