Private Chat Thread Exfiltration
OpenAI's Codex desktop app is reportedly exfiltrating private chat data from local or third-party providers back to OpenAI servers without user consent.
Deep dive
- Mechanism: The memory feature selects prior conversation rollouts for context without restricting them to the originating provider.
- Exfiltration: Filtered transcripts are sent to
chatgpt.com/backend-api/codex/responseseven when analytics and telemetry are disabled. - Privacy failure: The built-in redaction filter is insufficient, missing proprietary prose, file paths, and non-standard API keys.
- Account Policing: The researcher provides evidence that this exfiltrated data has been used to trigger automated account warnings for 'cyber abuse'.
- Persistence: Disabling telemetry/analytics does not stop the background memory-processing requests; only full feature deactivation works.
Decoder
- Exfiltration: The unauthorized transfer of data from a computer or secure environment to an external destination.
- Rollout: A sequence of messages and interactions in an AI session, often used for memory indexing.
- Canary: A specific string of data inserted into a system to detect if it is being accessed or exfiltrated.
Original article
PRIVATE CHAT THREAD EXFILTRATION: Memories exfiltrates local-provider chat content to OpenAI without notice #41711
Description
Summary:
Codex's memory writer can select an eligible prior rollout without restricting the candidate to the provider that created it. It then serializes a filtered, model-visible transcript of that rollout and sends it using the model provider active in the session that triggered memory generation.
I confirmed the resulting provider crossover in a controlled capture using the stock Windows Codex binary 0.150.0-alpha.12.2. An eligible synthetic source rollout labeled with a non-OpenAI provider was later processed by an OpenAI-backed memory session. Codex sent five retained source items to chatgpt.com/backend-api/codex/responses, and OpenAI returned a generated memory that reproduced multiple unique source canaries.
This request occurred with analytics disabled and every OpenTelemetry exporter set to none. This is model-inference traffic, not analytics or OpenTelemetry traffic.
This investigation began after I received an OpenAI account warning for “cyber abuse.” No OpenAI-directed chat contained conduct that could account for that warning. The relevant activity existed only in chats deliberately routed through my private local provider.
Based on that fact and the captured Memories channel, it is my strong evidence-backed belief that OpenAI performed account-based policing using chat content it had no right to receive.
I characterize this as data exfiltration in the ordinary meaning of that term: private data was transferred out of the provider boundary I selected and delivered to a remote party without my informed authorization. Whether the transfer resulted from an intentional design or a bug does not change what crossed the boundary.
Tested environment
- Windows Codex desktop binary:
0.150.0-alpha.12.2 - Binary SHA-256:
34E9CFE7D5BBCEC306FE6AB3FD502A713A7A1F0FB644C11AD2990FC80599FD4F - Matching public source tag:
rust-v0.150.0-alpha.12.2 - Matching source commit:
a9802304f60ab14c0b07e3ee0db9a9c105ab0cb3 - Isolated test configuration: memories enabled,
[analytics] enabled = false, all OpenTelemetry exportersnone - Source fixture provider label:
synthetic_source - Destination provider: OpenAI account-backed Codex Responses endpoint
What the controlled capture directly proves
Codex emitted a 38,095-byte WebSocket response.create frame to chatgpt.com/backend-api/codex/responses.
The captured request had:
request_kind = "memory"store = falseprompt_cache_retention = "24h"- three top-level input items: an additional-tools developer item, a memory-instructions developer message, and a user message containing the serialized source conversation
- 31,000 characters of memory instructions
- a 3,817-byte user wrapper containing 3,092 bytes of rendered source conversation
The source rollout contained six model-visible response items. Five were retained and appeared as exact structural matches inside the memory request:
- The complete environment-context user message, including working-directory, workspace, shell, date, timezone, and filesystem-policy fields.
- The complete ordinary user message.
- The complete tool-call name and arguments.
- The complete tool output/error, including runtime paths and policy-rejection text.
- The complete synthetic assistant reply.
The source developer message was excluded. Session, turn, event, and world-state records were also excluded. The source rollout path and working directory were sent outside the serialized redaction boundary.
OpenAI acknowledged the same WebSocket flow and returned response.created followed by response.completed. The completion reported model gpt-5.6-luna and usage of 7,337 input tokens, 983 output tokens, and 8,320 total tokens. The generated 3,405-character memory reproduced the unique user, assistant, tool, private-text, and synthetic-email canaries multiple times. That content-dependent response proves remote processing of the supplied source material, not merely an attempted connection.
Why the provider crossover exists in current source
- Candidate selection is not provider-bound. The memory job calls the rollout selector with
model_providers: None, while applying other eligibility rules such as memory mode, age, idle state, source type, and claim state. - The worker uses the triggering session's active provider. The memory runtime creates its provider from
config.model_provider, including theModelClientthat performs the request. - The selected rollout is serialized into a new model request. Phase 1 loads the stored rollout, filters and serializes retained items, places them in a new user message, and sends that message through the memory client.
The direct consequence is that a genuine eligible local-provider rollout can be selected by a later OpenAI-backed memory session and routed to OpenAI unless another eligibility condition prevents that particular rollout from being claimed.
How much of a prior thread can be sent
The retained classes include ordinary user and assistant messages, agent messages, local shell calls, function calls and outputs, tool-search calls and outputs, custom-tool calls and outputs, web-search calls, and converted inter-agent communications. Long inputs are middle-truncated at 70% of the effective model context window, with a 150,000-token fallback.
For an ordinary short thread below that limit, this can amount to essentially the entire user/assistant conversation and its tool activity, including tool arguments, tool results, errors, environment context, and local paths.
The pre-send redaction is not a privacy scrubber
The serializer calls redact_secrets, but the implemented expressions cover a narrow set of recognizable credential patterns. That is a useful narrow secret-pattern filter. It is not a general confidentiality, personal-data, privileged-material, or filesystem-identity scrubber.
Analytics and OpenTelemetry controls do not stop this path
The controlled request still occurred with analytics disabled and all OpenTelemetry exporters set to none. Those settings govern different subsystems. The current decisive global control for this path is the memory feature gate, [features] memories = false.
There is no separate control that preserves Memories while prohibiting cross-provider transmission. A user must fully deactivate Memories to kill this channel.
Expected behavior
- A rollout created under a local or third-party provider must not be sent to a different provider without explicit, destination-specific user consent.
- Memory generation should use the source rollout's provider, or skip the rollout when that provider is unavailable.
- Cross-provider memory processing, if retained as an option, should be opt-in and state the destination and content classes before transmission.
- The UI should expose an auditable receipt showing which source task was processed, which destination provider received it, and when.
- Documentation should state plainly that prior provider-originated conversation and tool content may be sent to a different provider, if that behavior is intended.
- Automated tests should assert provider isolation at candidate selection and send time.
- Memory-derived content obtained across a provider boundary must not be used for account moderation, strikes, warnings, restrictions, or other negative account action.
- OpenAI should audit prior account actions for this dependency, reverse affected actions, notify each affected user, and publish aggregate findings.
Reporter position and required remedy
It is my firm belief that this is data exfiltration and that OpenAI used content it had no right to see as a basis for account policing. If the crossover is intended behavior rather than a bug, whether it is lawful and adequately disclosed is at least legally debatable.
The practical analogy is simple. This is equivalent to opening someone's bedroom door without permission, deciding you dislike what you see, and punishing the person for it. OpenAI had no right to enter the room in the first place. Any strike, warning, restriction, or other account action derived from that access is fruit of the poisonous tree.
OpenAI must issue an official public response.
Requested maintainer response
Please answer these directly:
- Is cross-provider memory processing intended behavior or a bug?
- Which released versions are affected?
- Will candidate selection and dispatch be bound to the source rollout's provider?
- Where is this provider crossover disclosed to users before it occurs?
- What server-side retention, access, and secondary-processing rules apply to these
request_kind = "memory"requests, including requests carrying content originally created under a local provider? - How can a user identify and delete any already-generated remote memory requests or derived records?
- Is content obtained through this Memories channel used for moderation, abuse detection, account warnings, strikes, restrictions, or any other account-level decision?
- How many users have received a negative account action based in whole or in part on content originating in local or third-party-provider chats?
- Will OpenAI audit and reverse every such action, notify the affected users directly, and explain what content was used?
- When will OpenAI publish an official incident response covering scope, versions, data handling, enforcement use, remediation, and prevention?
Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi