Remote Control turn from account B silently executes against account A usage

Open 💬 1 comment Opened Aug 17, 2026 by samdickson22

What version of the Codex App are you using (From “About Codex” dialog)?

26.810.52044 (build 6662); bundled codex-cli 0.148.0-alpha.9

What subscription do you have?

Unknown. This reproduction used account A with exhausted Codex usage and account B with remaining usage.

What platform is your computer?

Controller: Darwin 24.6.0 arm64 arm

Remote host: Apple-silicon Mac mini, macOS 26.5.2

What issue are you seeing?

A user signed into account B on the controlling Mac can submit a new message to an existing Remote Control task that was created under account A on the remote host. The request reaches the remote task, but the sampling request executes with the remote host task's existing account A authentication and fails against account A's usage limit.

The controller gives no indication that sending from account B will execute against account A's quota.

Sanitized remote-host log sequence from one reproduction:

13:31:51.190 session handler accepted UserInput for the existing remote thread
13:31:51.550 run_sampling_request model=gpt-5.6-sol auth_mode=Chatgpt
13:31:51.844 Turn error: You've hit your usage limit

The persisted task transcript records the user message and marks the turn complete roughly 300 ms later, with no assistant response or tool call. A later thread/resume also logged that configuration and developer-instruction overrides were ignored because the thread was already loaded.

Account B had remaining usage at the time; account A did not.

What steps can reproduce the bug?

  1. Sign into account A on a remote Mac host and start a Codex task.
  2. Exhaust account A's Codex usage while leaving the task and Remote Control enrollment intact.
  3. On a separate controlling Mac, sign out of account A and sign into account B, which has remaining usage.
  4. Open the historical remote task and submit a new message.
  5. Observe that the remote host accepts the message.
  6. Inspect the remote host log and observe that the turn uses the loaded thread's account A authentication and fails on account A's usage limit.

What is the expected behavior?

Desktop should make the execution identity explicit and consistent. Safe options include:

  • rebind the new turn to the controller's active account B after explicit confirmation;
  • keep the task bound to account A but block account B from sending and explain why; or
  • expose the bound account/usage state before submission.

It should not accept a submission from account B while silently executing it against account A's quota.

Additional information

Related reports include #32173 (active threads retain account A after switching to B), #29904 (remote and controller usage state disagreement), and #8310 (resume behavior after a usage limit). This report adds a concrete Remote Control, cross-account reproduction where the controller and execution host are different Macs.

The report intentionally omits account IDs, request IDs, hostnames, environment IDs, cookies, and task contents.

View original on GitHub ↗

1 Comment

jdcodes1 · 10 days ago

Some structure from the OSS side (main @ 1f41cc5d92) that splits this into the expected part and the actual gap:

Quota attribution to account A is structural, not a routing accident. A remote-controlled turn executes in the host's session, and run_sampling_request resolves credentials from the host session's auth manager — nothing in the turn pipeline rebinds authentication to the controlling client's identity. So once a message reaches the host task, "executes against account A" is by construction. The defect is upstream: authorization and labeling — account B should either be prevented from driving A's task or be told whose quota it runs on.

The account-binding invariant exists — but only on the host side. The enrollment is explicitly account-scoped (chatgpt-account-id accompanies enrollment; cached per account — remote_control/enroll.rs), and the host actively enforces its own side of the invariant: if the host's signed-in account changes relative to its enrollment, it clears the enrollment and disables remote control (remote_control/websocket.rs#L1468-L1491, "remote control disabled after account changed"). What's missing is the symmetric enforcement for the controller: your reproduction (plus the stale visible tasks in #39095) indicates that switching accounts on the controlling Mac does not tear down or re-authorize the account-A-era channel and task references, so account B's UI can keep publishing into a channel enrolled to A.

Where the fix has to live (two layers, only one visible in this repo):

  1. Controller side: an auth/account change should invalidate all remote-control client channels and task references established under the previous account — the same teardown the host already performs, mirrored. #39095's "tasks remain visible after account switch" is the same missing teardown observed from the UI, which is why these two reports are best treated as one bug.
  2. Relay side (not in the OSS tree): defense-in-depth would reject publishes where the publishing token's account doesn't match the enrollment's account — that makes the controller-side bug non-exploitable rather than merely fixed.
  3. Regardless of 1-2, the controller UI should label remote turns with the executing account whenever it differs from the signed-in account — the silent-attribution part of your report is a labeling gap even in legitimate shared-machine flows.

Worth noting for triage: since both accounts here belong to the same person, the observed impact is quota confusion — but the same missing controller-side teardown would apply to any scenario where a controller machine changes hands between accounts, which is why the authorization fix (not just the label) matters.