Skip to article
PlanToCodeDocsGet 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 17 September 2026

On this page

The implementation stack

PlanToCode has four application boundaries and one agent process boundary. 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 the Codex child process. 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 Codex child, and your phones meet only in the Rust runtime
The WebView, the Codex child, and your phones meet only in 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. The Codex child runs commands and edits files in the project, keeps transcripts and its ChatGPT sign-in in its own profile home, and calls OpenAI over HTTPS. Every arrow that crosses the dashed line starts inside it: the desktop opens a WebSocket to the regional server, and each phone opens its own socket to the same relay.
your computer
WebViewReact · Tauri 2
Tauri commands · events
Rust runtimeRPC router · domain services · outbox
appdata.dbsessions · outbox
reads files · runs git
Project folderGit worktrees
JSON-RPC lines over stdin/stdout
Codex app-serverchild process
commands · edits
Codex profile hometranscripts · ChatGPT sign-in
PhonesiOS · Android
WebSocket
Regional serverrelay · accounts
WebSocket
OpenAImodel requests
HTTPS
Every connection that crosses the dashed line is opened from inside it.
  • Rust runtimecommands · 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.

  • 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 end in the same service
A phone request and a desktop click end in 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 checks for text or attachments and a valid send-mode pair for every caller, saves the message, and returns a receipt. A drain task starts the turn in the Codex child later.
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
saved, receipt returned
Codex childgets turn/start or turn/steer later
  • 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 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. 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

    PlanToCode 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/services/codex_app_server/Child process transport, client actors, turn admission, and registry ownership.
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/plantocode/mobile/Android entry routing, workspace tabs, relay client, and Compose screens.