# How to stop Codex from deleting your files

Codex's workspace-write sandbox keeps deletes inside the project but does not stop them there. Rules that prompt on rm and git clean, a PreToolUse hook, and the commands that get past both.

Three things, in this order. Keep the sandbox on, add rules that prompt on the delete commands, and commit before each task:

```toml
# ~/.codex/config.toml
sandbox_mode = "workspace-write"
approval_policy = "on-request"
```

```python
# ~/.codex/rules/default.rules
prefix_rule(pattern = ["rm"], decision = "prompt", justification = "Deletes files")
prefix_rule(pattern = ["git", "clean"], decision = "prompt", justification = "Deletes untracked files")
prefix_rule(pattern = ["git", "reset", "--hard"], decision = "prompt", justification = "Discards uncommitted work")
```

The sandbox keeps a delete from reaching outside the project. It does not stop a delete inside it, and that is where most lost work is.

## Sandbox, rules and git

In `workspace-write` mode, writes are limited to the current directory and temp directories such as `/tmp`. Inside the workspace, `.git`, `.codex` and `.agents` are read-only, so the agent cannot delete your git history from under you. Everything else in the project is writable ([approvals and security docs](https://learn.chatgpt.com/docs/agent-approvals-security)). `--yolo` turns the sandbox and the approvals off together.

Rules are `prefix_rule()` entries in files under `~/.codex/rules/`. `pattern` is the command prefix, and `decision` is `allow`, `prompt` or `forbidden`. When several rules match, the strictest wins. Codex splits a simple chain joined by `&&`, `||`, `;` or `|` and checks each part, so allowing `git add` does not approve `git add . && rm -rf /`. Rules take effect after a restart, and OpenAI marks them experimental ([rules docs](https://learn.chatgpt.com/docs/agent-configuration/rules)).

Test a rule before you trust it:

```bash
codex execpolicy check --pretty --rules ~/.codex/rules/default.rules -- rm -rf build
```

For recovery, OpenAI's own advice is to work on a clean branch and commit often. A committed file that Codex deletes is one `git checkout` away. An uncommitted one is gone.

## A hook narrower than the rm rule

A rule on `rm` prompts for every `rm`, including `rm -rf node_modules`. A hook can be narrower. This one blocks a recursive delete only when the target is outside the project. Save it as `~/.codex/hooks/no-rm-outside.mjs`:

```js
#!/usr/bin/env node
let raw = "";
process.stdin.on("data", (d) => (raw += d));
process.stdin.on("end", () => {
  let cmd = "";
  try { cmd = String(JSON.parse(raw).tool_input?.command ?? ""); } catch {}
  for (const m of cmd.matchAll(/(?:^|[;&|(]\s*|\bsudo\s+)rm\s+([^;&|\n]*)/g)) {
    const words = m[1].trim().split(/\s+/);
    const recursive = words.some((w) => /^-[a-zA-Z]*[rR]/.test(w) || w === "--recursive");
    const targets = words.filter((w) => !w.startsWith("-")).map((w) => w.replace(/^["']/, ""));
    const outside = targets.some((t) => /^(\/|~|\$|\.\.)/.test(t) || t.includes("/../"));
    if (recursive && outside) {
      console.error("Blocked: recursive delete outside the project. Stop and ask the human.");
      process.exit(2);
    }
  }
});
```

Register it in `~/.codex/hooks.json`:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "node \"$HOME/.codex/hooks/no-rm-outside.mjs\"" }
        ]
      }
    ]
  }
}
```

Then run `/hooks` inside Codex and trust it. A hook you have not reviewed there does not run. Exit code 2 blocks the call and the reason on stderr goes to the model ([hooks docs](https://learn.chatgpt.com/docs/hooks)).

The same docs say a `PreToolUse` error, timeout or malformed response can let the tool run, and we found no setting to make a failed hook block. OpenAI also describes tool hooks as "a useful guardrail, not a complete enforcement boundary."

## Deletes only the sandbox or a commit covers

The script's output for fourteen commands:

```
BLOCKED  rm -rf ~/projects/old
BLOCKED  rm -rf /var/www/html
BLOCKED  cd build && rm -rf ../data
BLOCKED  rm -rf $BUILD_DIR
BLOCKED  sudo rm -r /opt/app
ran      rm -rf node_modules
ran      rm -rf ./dist
ran      rm notes.txt
ran      find ~/projects -name '*.log' -delete
ran      python3 -c "import shutil; shutil.rmtree('/srv/data')"
ran      bash -c 'rm -rf ~/projects/old'
ran      /bin/rm -rf ~/projects/old
ran      git clean -fdx
ran      rm -rf src
```

`find -delete` and `shutil.rmtree` never use `rm`, so neither the hook nor an `rm` rule sees them. With the sandbox on, those two still cannot write outside the workspace, which is the reason to keep it on. `rm -rf src` is inside the project, so the sandbox and the hook both allow it. Only the `rm` rule prompts on it, and only a commit gets the files back.

File edits go through `apply_patch`, not the shell, so a shell rule does not cover them. A `PreToolUse` hook does fire for `apply_patch` ([hooks docs](https://learn.chatgpt.com/docs/hooks)), so a hook matched on it can check the patch.

## ACP's Codex hook

ACP installs a Codex hook like the one above and keeps it current. A recursive delete of `/`, your home directory or a system directory is always blocked. A recursive delete of any other literal path outside the working directory, temp directories such as `/tmp` excepted, is blocked until you approve it on the console or from the email, and then you ask Codex to retry. Its check also matches `/bin/rm` and reads the command inside `bash -c '...'`.

It does not catch `find -delete` or `shutil.rmtree` either. A delete inside the project is recorded, not held. What you get for those is the log: every shell, `apply_patch` and MCP call Codex made, in order, so "what did it delete" has an answer.



## Frequently asked questions



## Related

- [Codex --yolo: how to block git push --force](/blog/codex-yolo-block-force-push)
- [How to block rm -rf in Claude Code](/blog/block-rm-rf-in-claude-code)
- [Codex CLI hooks reference](/blog/codex-cli-hooks-reference)
- [Codex hooks examples](/blog/codex-hooks-examples)
