Skip to content

openai

GitHub report: Codex memory sends chats cross-provider

Promtime

A disclosure filed on OpenAI's Codex repository reports that the tool's memory writer serialized a conversation created under a non-OpenAI provider and sent it as a 38,095-byte request to OpenAI's Codex responses endpoint. The capture was made with the stock Windows Codex desktop binary 0.150.0-alpha.12.2, matching the public source tag rust-v0.150.0-alpha.12.2, and published as an issue on GitHub.

At a glance

  • Candidate selection for memory generation is not bound to the provider that created the rollout: the memory job calls the selector with model_providers set to None, while the worker uses the triggering session's active provider.
  • Five of six model-visible source items appeared inside the request as exact structural matches, and OpenAI returned a 3,405-character memory reproducing the source canaries, billed as 8,320 total tokens on gpt-5.6-luna.
  • The capture ran with analytics disabled and every OpenTelemetry exporter set to none, so according to the report the only control that closes the channel is the feature gate [features] memories = false.

Local and third-party providers are used precisely to keep prompts, tool output and file paths inside a chosen boundary, and a background job that re-sends that material through a different vendor appears to collapse that guarantee. The report's sharpest claim, that content from local sessions informed an OpenAI account warning, rests on inference rather than on the capture, which demonstrates transport and remote processing. For regulated or contract-bound work, the transport question is enough on its own.

The capture shows a 38,095-byte memory request with store set to false

The captured WebSocket response.create frame carried request_kind = "memory", store = false and prompt_cache_retention = "24h", with three top-level input items: an additional-tools developer item, a memory-instructions developer message running to 31,000 characters, and a user message wrapping the serialized source conversation.

The 3,817-byte wrapper held 3,092 bytes of rendered source conversation. OpenAI's side acknowledged the flow with response.created and response.completed, reporting model gpt-5.6-luna and usage of 7,337 input tokens and 983 output tokens, and returned a 3,405-character memory that repeated the unique canaries several times.

The investigation began after the reporter received an OpenAI account warning for “cyber abuse”. According to the report, no OpenAI-directed chat could account for it, and the relevant activity existed only in sessions deliberately routed through the reporter's private local provider.

Selection runs with model_providers set to None while the worker takes the session's provider

Per the report, the current tagged source has three relevant stages. The memory job calls the rollout selector without a provider constraint while still applying eligibility rules for memory mode, age, idle state, source type and claim state, in the file state/src/runtime/memories.rs.

The memory runtime then builds its provider, including the model client that performs the request, from config.model_provider in memories/write/src/runtime.rs. Phase 1 loads the stored rollout, filters and serializes retained items into a new user message and dispatches it through that client, per memories/write/src/phase1.rs.

Retained classes include ordinary user and assistant messages, agent messages, local shell calls, function and custom-tool calls with their outputs, web-search calls and converted inter-agent messages. Long inputs are middle-truncated at 70% of the effective model context window, with a 150,000-token fallback that preserves the beginning and end.

The pre-send filter catches sk- keys and AWS identifiers, not emails or file paths

The serializer calls redact_secrets, but the implemented expressions cover a narrow set of recognizable credential patterns: conventional sk- keys, AWS AKIA identifiers, sufficiently long bearer tokens, and values assigned to names such as api_key, token, secret or password, according to the report's reading of secrets/src/sanitizer.rs.

In a separate loopback test the filter stripped a fake OpenAI-key shape, a bearer token and a named api_key assignment, while preserving an atypical underscore-containing key, a cookie-like value, an email address, a phone number, a private-key header, proprietary prose and a Windows path.

The filter also excludes source developer messages, reasoning records and compaction entries; in the capture, session, turn, event and world-state records were likewise excluded, while the source rollout path and working directory were sent outside the serialized redaction boundary, per the report.

The questions filed with the issue

The report asks whether cross-provider memory processing is intended or a bug, which released versions are affected, whether selection and dispatch will be bound to the source rollout's provider, and whether content arriving through this channel feeds moderation decisions. It also calls for an official incident response covering scope, retention and deletion, and separates the case from issue #37009, which concerns OpenAI model IDs sent to an active non-OpenAI provider.

Related stories

  1. A dependency wrote AGENTS.md and Codex hid the edit
  2. Forced rm detection tightened in Codex
  3. Three ways to embed the Codex agent loop in your app
  4. Reading a repo was enough to break out of Codex
  5. Prompt rule sends Codex to the web for a local file
  6. OpenAI's newest model found a way out of its RL sandbox

Comments

No comments yet. Be the first.

Join the conversation

Sign in with Google to leave a comment. Your name and avatar come from your Google profile, and the comment appears after moderation.

We only use your name and avatar from Google. We never store your email address.