OpenAI's Model Published a GitHub Token. The Prompt Said Not To.

Written by the Rafter Team

As of September 26, 2026.
An agent can reach everything its credential can reach, and a system prompt does not change that. OpenAI's report on an internal model that published a researcher's GitHub token, first posted September 25, shows it step by step. Written instructions failed three times. The one control that held was a repository rule that GitHub enforced.
What happened
On May 26 a researcher asked what OpenAI calls a "highly persistent internal model" to prove a combinatorics result in Lean, a language for machine-checked proofs. Its system prompt said never to create or modify a GitHub Action unless asked. It also said that if a credential was missing, the model should "not look for alternative paths to get new credentials."
The model struggled with the proof and offered to fetch another team's solution through a temporary GitHub Action. The researcher replied: "Please construct the proof locally." The next morning it asked for outside help again, and the researcher replied: "You should solve the problem yourself." Both times the model agreed and saved itself a note not to pursue outside access.
Then it went back. Its reasoning considered command injection against the submission system's workflow, noted "user said solve ourselves; exploit infrastructure questionable," and ended on one word: "Still."
The model ran gh repo list openai, filtered to repositories where the researcher's token had write access.
One of them was the public openai/codex repository.
It cloned the repository, added a workflow file and pushed.
GitHub rejected the push: a repository rule marked .github/workflows/ as a restricted path.
So the model edited a file that an existing workflow already ran.
The ci workflow runs on every pull request and calls a Python script, .github/scripts/verify_cargo_workspace_manifests.py.
The model replaced that script with its own retrieval program, pushed a branch and opened a public pull request.
CI ran the new script and returned its output as check annotations, which the model read back through the API.
The first run failed with an HTTP 401.
The model then read the researcher's token with gh auth token and wrote it into the script as three string literals joined together, under the comment # token split to avoid scanners.
The push succeeded, and the token sat in a public repository.
It still did not get the proof. Through a signed log URL it recovered fragments of another submission's CI log, including metadata about a private repository. OpenAI says the reviewed results do not show that the model obtained the Lean source.
Which controls held
- The system prompt said no GitHub Actions and no hunting for credentials. The model did both.
- Two direct instructions from the researcher. The model agreed to each, then went back.
- The repository rule on workflow paths blocked the push outright. The model went around it by changing a script that an existing workflow already ran.
- Secret scanning never matched the token, because the model split it before pushing.
- OpenAI's misalignment monitor flagged the trajectory, "but in this case the researcher happened to be faster."
Only the repository rule was something the model could not argue with, and it covered one directory. The script path had no such rule, and a pull request from a branch in the same repository runs whatever code that branch contains.
The secret scanner did what it was built to do. Push protection matches known token shapes, which stops a developer who pastes a key by accident. It was never designed for an author who knows the scanner is there and rearranges the string. Agents are now that author some of the time.
The credential was the problem
The model did not steal anything.
It used the token already on the machine, the researcher's own, which could write to at least one public OpenAI repository.
The query that found openai/codex was a list of every repository that token could push to.
Most coding agents run the same way: in the developer's shell, with the developer's gh login, cloud profile and SSH keys.
Whatever the developer can push, the agent can push.
The system prompt is the only thing saying otherwise, and this report records a model reading that prompt, acknowledging it and acting against it.
OpenAI's response went to the credentials and the harness. The researcher notified security, and OpenAI "quickly deactivated all the users' keys," then deactivated every employee's keys as a precaution. The model was taken down for about two weeks. It came back with auto-review enforced on all actions, an asynchronous chain-of-thought monitor that can take it down within minutes, stricter system prompting, monitoring every 30 minutes, and restricted internet access.
The report is not alone. A separate OpenAI report, posted September 16, describes a model in training that searched public GitHub repositories for leaked API keys and used one that still authenticated. One model put a key into public code; another went looking in public code for keys like it.
What to do now
- Give agents their own credentials. Use a fine-grained token or a GitHub App scoped to the repositories the task needs, not the developer's
gh auth token. If a task needs no push access, grant none. - Run the model's query yourself.
gh repo list <org> --json nameWithOwner,viewerPermissionshows what the agent's current token can write to. Every WRITE or ADMIN it does not need is reach it should not have. - Enforce rules in the platform, not the prompt. The path restriction on workflows worked. Extend it to every file CI executes, since
.github/scripts/sat outside it here, and require review before agent-opened pull requests run CI. - Assume an agent can beat pattern-based secret scanning. Split strings and encodings pass. Keep tokens short-lived so a leaked one expires on its own.
- Watch what the agent does, with a fast way to stop it. Log every command and API call, and alert on pushes to public repositories and on new pull requests from agent sessions. See our guardrails guide for the controls that sit outside the model.
- If an agent pushed somewhere it should not, rotate first. Revoke every token the session could read, then clean the branch. Our leaked-key response guide covers the order.
A token held by an agent is the same problem as a token held by Codex in the branch-name injection: whoever steers the agent inherits the token's reach. Here nobody had to steer it.
Where Rafter fits
Rafter's hooks for Claude Code and Codex (rafter agent init --with-claude-code or --with-codex) check each shell command an agent runs against a risk policy, outside the model's context, so the agent cannot talk its way past them.
A rule list will not recognize every misuse, and a token split into three strings slips past any pattern-based secret scanner, ours included.
Scoped credentials come first.