# Codex

This page is for an agent connecting a team's Codex to Zaun. It says what Codex lets an
administrator enforce, how to observe with telemetry and block with managed hooks, and the
proof to show. If you are the person, know the limit first: Codex telemetry can only be set as
a central default, which a user can override. Managed hooks are the enforceable route, on
macOS, Linux and Windows.

| Goal | Use | Enforced? |
| --- | --- | --- |
| See prompts and tool calls, alert, review sessions | Telemetry in Codex's system `config.toml` | No. It is a default a user can override. |
| Stop a prompt or a tool call before it runs | Managed hooks in `requirements.toml` | Yes |

Use telemetry beside the hooks for the record. The console generates the files for your
project; the blocks below show their shape.

## Observe with telemetry

Put an `[otel]` table in Codex's system configuration file: `/etc/codex/config.toml` on macOS
and Linux, `%ProgramData%\OpenAI\Codex\config.toml` on Windows, or the `com.openai.codex`
preference domain on macOS through your device-management tool. Do not use the legacy
`/etc/codex/managed_config.toml`: current Codex reads it as requirements, not configuration.

```toml
[otel]
environment = "production"
log_user_prompt = true

[otel.exporter.otlp-http]
endpoint = "https://api.zaun.ai/v1/ingest/otlp/v1/logs"
protocol = "binary"

[otel.exporter.otlp-http.headers]
"x-zaun-ingest-key" = "<INGEST_KEY>"
```

- `<INGEST_KEY>` is an ingest key for your Codex telemetry source. It can only submit events
  for one project, and it cannot scan or read results.
- Codex sends to the endpoint exactly as written, so it ends in `/v1/logs`.
- `log_user_prompt = true` is what puts prompt text in the export. Without it Zaun sees that
  a prompt happened, not what it said.
- Tool results carry the call's arguments and its output, which Codex truncates to 2 KB by
  default.
- The exporter is a struct: write it as the `[otel.exporter.otlp-http]` table above. Codex
  refuses to load a file that sets `exporter = "otlp-http"` as a plain string, and then
  applies none of it.
- A user's own `config.toml` outranks the system file, so do not rely on telemetry alone for
  coverage. Telemetry scans are billed like any other scan.

## Block with managed hooks

Codex hooks run commands, not HTTP requests, so the console ships a small forwarder that
Codex runs for each check. The forwarder sends the check to Zaun and turns a block into
Codex's deny. Codex trusts hooks declared in `requirements.toml`, and users cannot turn them
off. The file lives at `/etc/codex/requirements.toml` on macOS and Linux and
`%ProgramData%\OpenAI\Codex\requirements.toml` on Windows, or arrives through the
`com.openai.codex` preference domain on macOS:

```toml
allow_managed_hooks_only = true

[features]
hooks = true

[hooks]
managed_dir = "<MANAGED_DIR>"
windows_managed_dir = "<WINDOWS_MANAGED_DIR>"

[[hooks.PreToolUse]]
matcher = ".*"

[[hooks.PreToolUse.hooks]]
type = "command"
command = "python3 <MANAGED_DIR>/zaun_hook.py"
command_windows = "py -3 <WINDOWS_MANAGED_DIR>\\zaun_hook.py"
timeout = 10
statusMessage = "Checking with Zaun"
```

- `<MANAGED_DIR>` and `<WINDOWS_MANAGED_DIR>` are the administrator-owned directories that hold
  the forwarder and its configuration file on Unix and on Windows. The console generates both;
  the configuration file carries the endpoint and the key, so the key is not in
  `requirements.toml`.
- The commands shown run the forwarder as a Python 3 script. Use the commands in the
  console's generated file, which match the forwarder it ships. When the forwarder is the
  Python script, every machine needs Python 3 (`python3`, or the `py` launcher on Windows);
  without it the hook fails, and Codex lets the action through.
- The generated file also checks prompts, with a `UserPromptSubmit` hook.
- Bare keys such as `allow_managed_hooks_only` must come before the first table. TOML nests a
  key written after a table inside that table, and Codex then ignores it.
- Hooks apply the same verdict as the API. Today a tool call is blocked by input checks such
  as the test string; a threat Hex v1.3 finds only in the agent's own tool calls normally raises
  an alert instead. See [what blocks today](capture-paths.md#what-each-path-can-and-cannot-do).
- Hooks fail open: if the forwarder cannot reach Zaun within its 5-second limit, the action
  goes ahead. The forwarder's configuration can make it fail closed instead.

## The proof to show

- **Telemetry:** the source's health report in the console shows Codex events arriving and
  that prompt content is present.
- **Hooks:** on a machine with the managed files in place, ask Codex to create a file whose
  content is `ZAUN-TEST-VIOLATION-` followed directly by `7f3a9c`. The file write carries the
  whole test string and is denied with Zaun's reason, and the event appears on Violations. The
  test string is on [Try it](try-it.md).
