Architecture

Outbound polling with owner-scoped work

The worker never needs inbound network access. It connects to the host, claims eligible work, runs Codex locally, and submits the result.

Sequence

Invocation lifecycle

The host app keeps product identity and persistence. The local worker keeps local runtime access and claims only queued work for its owner.

sequenceDiagram
  autonumber
  actor User as "User"
  participant UI as "Host App UI"
  participant Host as "Host App Routes"
  participant SDK as "@codexdock/sdk"
  participant Store as "Persistence Adapter"
  participant Worker as "Local codexdock CLI"
  participant Codex as "Local Codex Runtime"

  User->>UI: Start AI generation
  UI->>Host: POST app generation route
  Host->>Host: Resolve owner from cookie/session
  Host->>SDK: invoke({ type, prompt, parameters }, owner)
  SDK->>Store: createInvocation(status: pending, owner)
  Store-->>SDK: invocation record
  SDK-->>Host: invocationId + statusUrl
  Host-->>UI: pending handle

  Worker->>Host: POST /worker/connect
  Host->>SDK: authenticate worker token
  SDK->>Store: upsertWorker(owner, capabilities)
  Store-->>SDK: worker record
  SDK-->>Worker: polling policy

  loop Short polling with backoff
    Worker->>Host: POST /worker/next
    Host->>SDK: authenticate worker token
    SDK->>Store: claimNextInvocation(owner, capabilities)
    Store-->>SDK: pending invocation or null
    SDK-->>Worker: invocation or 204
  end

  Worker->>Codex: Run prompt in local workdir
  Codex-->>Worker: generated result
  Worker->>Host: POST /worker/result
  Host->>SDK: validate worker claim + result schema
  SDK->>Store: completeInvocation(result)
  UI->>Host: GET statusUrl
  Host-->>UI: completed result

Owner scope

The host owns identity. CodexDock enforces scoped work.

The example uses an anonymous browser UUID cookie. A product app can swap that resolver for login, account, workspace, or system-job ownership. Worker tokens map back to the same owner.

sequenceDiagram
  autonumber
  participant Browser as "Browser"
  participant Host as "Host App"
  participant Pairing as "Pairing Store"
  participant SDK as "@codexdock/sdk"
  participant Store as "Persistence Adapter"
  participant Worker as "Owner-Scoped CLI"

  Browser->>Host: Open playground
  Host->>Browser: Set anon owner cookie
  Browser->>Host: POST create pairing code
  Host->>Pairing: Store code hash for anon owner
  Host-->>Browser: Show codexdock connect --code

  Worker->>Host: POST /pairing/exchange with code
  Host->>Pairing: Atomically consume valid code
  Pairing-->>Host: owner scope
  Host->>Pairing: Store worker token hash
  Host-->>Worker: worker token

  Browser->>Host: Create generation
  Host->>SDK: invoke(input, owner: anon uuid)
  SDK->>Store: createInvocation(owner: anon uuid)

  Worker->>Host: POST /worker/next with token
  Host->>SDK: authenticate token
  SDK->>SDK: token resolves to anon owner
  SDK->>Store: claim pending where owner matches
  Store-->>SDK: matching invocation only
  SDK-->>Worker: work item

  Worker->>Host: POST /worker/result
  Host->>SDK: authenticate token
  SDK->>SDK: require same owner, same workerId, status running
  SDK->>Store: complete invocation

Boundaries

Architecture decisions

01

Host-controlled persistence

Apps choose the persistence adapter and keep invocation ownership, status reads, quota, and product-level authorization in their own backend.

02

Outbound worker traffic

Local machines poll the host for work, so no inbound tunnel or public local port is required for a user's Codex runtime.