Under the hood / Technical explainer

One protocol.
Many computers.

Freestyle Remote speaks ChatGPT’s native Remote protocol, then multiplexes its JSON-RPC stream across a fleet of thread-affine VMs. The trick is deciding what stays local, what wakes compute, and where every event goes on the way back.

CLIENT EDGENative Remote
MESSAGE MODELJSON-RPC + notifications
WORKER HOPSSH tunnel → WebSocket
ROUTING KEYthread · path · handle
Clientpaired

ChatGPT

Opens one native connection to what appears to be a single remote coding device.

"method": "turn/start"
"params": {
  "threadId": "thr_8f…",
  "input": […]
}
CoordinatorRust

Protocol switchboard

Terminates the client connection, owns routing state, and forwards raw protocol messages without becoming the execution environment.

threadId?Look up durable worker binding
routed cwd?Decode worker UUID + guest path
handle?Resolve client-scoped live session
none?Serve cache/control call or fail closed
Executionper chat

Codex app-servers

Each VM runs the full model loop, tools, shell, code-mode helper, and workspace.

worker 6ec…thread thr_8f… · /workspace
↳ helper chatephemeral · shares worker 6ec…
worker b31…thread thr_c2… · paused

01 / Route selection

Not every packet takes the same road.

The method and its routing evidence determine the destination. That distinction lets ChatGPT browse history without waking a VM, while guaranteeing that execution never falls through to the gateway host.

CONTROL

Answer locally

Pairing, account status, device status, model preferences, thread lists, and cached completed reads are coordinator concerns.

thread/list → PostgreSQL
account/read → AuthManager
threadId

Route by thread

The durable thread → worker binding selects exactly one VM. A resume reattaches the worker stream when necessary.

turn/start
thread/read
thread/revert
/__freestyle/…

Route by path

Desktop operations without a thread ID carry an encoded worker UUID. The gateway strips the alias before forwarding.

/__freestyle/<worker>
/workspace/repo
LIVE HANDLE

Route by session

Processes, commands, file watches, and fuzzy searches use translated IDs scoped to both client and worker.

processHandle
processId
watchId · sessionId

02 / A turn in motion

The VM accepts before the client commits.

For an idle chat, the gateway wakes the original VM and waits for the app-server to accept the turn. ChatGPT receives the app-server’s real turn ID, so a failed wake or gateway rollout cannot leave a synthetic in-progress turn behind.

  1. Wake + attachAcquire a worker lease, resume the VM, open an SSH-forwarded WebSocket, initialize the app-server, and reattach the thread.
  2. Deliver onceForward turn/start without replaying it after an uncertain disconnect.
  3. AcknowledgeReturn the app-server’s response and real turn ID only after the worker accepts the request.
  4. StreamForward subsequent notifications with the same native turn ID.
Illustrative tracethr_8f…
CLIENT →turn/start { threadId }
GATEWAYbinding → worker 6ec… · acquire lease
VMwake · SSH tunnel · WebSocket ready
→ WORKERthread/resume { excludeTurns: true }
→ WORKERturn/start { threadId, input }
WORKER →turn/started { id: actual }
← CLIENTturn { id: actual, status: inProgress }
← CLIENTturn/started { id: actual }

Times and identifiers are illustrative. The acknowledgement ordering reflects the implementation.

03 / Identity translation

Stable outside. Scoped inside.

The coordinator is an identity boundary. It rewrites IDs and paths so the native client sees one coherent device without gaining a global handle into the worker fleet.

ChatGPT sees/__freestyle/6ec…/workspaceA unique desktop-visible workspace route.
Gateway resolvesworker = 6ec…
path = /workspace
Ownership and path are validated before forwarding.
Worker sees/workspaceThe app-server receives only its normal guest path.
Bindings do not drift

A saved thread cannot silently move to a different VM. Durable allocation intent reconciles ambiguous creation responses.

Unknown means stop

An unrouteable execution method returns an error. There is no fallback that runs the command on the coordinator host.

Uncertain means no replay

If a side-effecting worker call times out after delivery may have occurred, its outcome is marked unknown and it is not blindly repeated.

04 / Two planes

State and execution are deliberately split.

The coordinator holds just enough durable state to route, recover, and answer cheap reads. The worker remains the authority for live execution and conversation behavior.

Control plane

Rust gateway + PostgreSQL

Owns device identity, routing, subscriptions, lifecycle, cached history, and the shared authentication manager.

Durablethread ↔ worker, allocation intent, metadata, completed reads
Local readsthread lists and completed history do not wake compute
Lifecycleleases prevent active work from being paused or evicted
Execution plane

App-server inside each VM

Owns the model loop, live thread state, approvals, tool calls, files, terminals, and streaming notifications.

Transportlocalhost WebSocket reached through an encrypted SSH tunnel
Credentialsshort-lived access tokens supplied through native login RPC
Idleworker connection closes after inactivity; the VM may pause later
AUTH PATHThe long-lived ChatGPT refresh credential stays with the gateway’s native AuthManager. Workers receive access tokens via account/login/start; refresh requests travel back over the authenticated worker channel. Worker snapshots contain no ChatGPT auth file.

The gateway is a switchboard—not the computer.

That separation is what lets one paired ChatGPT device feel continuous while every conversation gets its own isolated execution boundary.

Back to the overview →