Skip to article
HB CodeDocsGet the app

HandbookArchitecture

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.

Checked against the source on 5 October 2026

On this page

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
The WebView, the agents, and your phones exchange data only through the Rust runtimeInside 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.
your computer
WebViewReact · Tauri 2
Tauri commands · events
Rust runtimeRPC router · domain services · outbox
appdata.dbsessions · outbox
JSON-RPC lines over stdin/stdout
reads files · runs git
JSON lines over stdin/stdout
Codex app-serverchild process
Project folderGit worktrees
Claude agent hostNode, one per run, starts claude
both agents run commands and edit files here
Codex profile hometranscripts · ChatGPT sign-in
~/.claudetranscripts · Claude sign-in
PhonesiOS · Android
WebSocket
Regional serverrelay · accounts
WebSocket
Every connection that crosses the dashed line is opened from inside it.
HTTPS
OpenAImodel requests
HTTPS
Anthropicmodel requests
  • Rust runtimechat · 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 hostdesktop/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.

Trace a mobile request to execution

A phone request and a desktop click call the same service
A phone request and a desktop click call the same serviceTwo 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.
Phonerpc.request
Relay
your computer
RPC intake
WebViewcomposer click
Tauri command
method on the relay’s list?
-32601
mutation has an idempotency key?
-32026
desktop connected?
-32010 or -32011
slot free within 8 s?
busy
same account as the desktop?
forbidden
first use of this key?
stored result
params decode?
invalid params
A check on one path protects only that path. Rules for every caller belong in the service.
allowed in this window?
not allowed
arguments decode?
error
Outbox servicesubmit_outbox_request
text or attachments?
rejected
valid send-mode pair?
rejected
agent has the ability?
rejected
saved, receipt returned
Session agentagents/dispatch starts the turn later, in Codex or Claude
  • Relayrequest_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 intakesnapshot_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 commandcapabilities/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 servicesubmit_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
Only a separate worktree keeps two tasks’ files apartThree 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.
Desktop
iPhone
Android
each device picks its session on its own
Settings pageSession
Dark modeSession
Login fixSession
Regression testSubagent thread
spawns
src/settings.tsx
src/login.ts
two sessions, one file, no lock
Main worktree~/code/app · main
src/login.ts
src/login.test.ts
src/settings.tsx
own copy
Linked worktree~/code/app-login · fix-login
one project
Linked worktree deleted: the next scan moves its idle sessions to the main worktree and skips running ones. A run cannot start while the scan moves sessions, because both take the same lock.
  • Worktreegit 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 threadactiveRun: 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 selectionappSessionId

    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 rootStart 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.