Skip to the content.

Security and privacy

Durable Codex ntfy notifier 2.4.2 is an unofficial local hook and worker that reads local Codex lifecycle metadata and sends a small notification to a server selected by the operator. Treat its topic, authentication values, hook configuration, Codex state, notifier state, and backups as private data.

Privacy defaults

Fresh installations use:

{
  "include_message": false,
  "max_message_chars": 180,
  "include_thread_title": false,
  "include_full_path": false,
  "tags": ["white_check_mark"],
  "priority": 3,
  "markdown": false,
  "suppress_subagents": true,
  "suppress_technical_turns": true,
  "idle_detection_mode": "strict",
  "goal_aware": true,
  "watch_rollouts": true,
  "allow_insecure_auth": false,
  "dead_retention_days": 30
}

The installers migrate existing private configuration conservatively and do not print secrets. Review content-related settings after any upgrade from a private or older public build.

strict is also the privacy/conservatism default for behavior: when root classification or matching rollout evidence is unresolved, the candidate stays local instead of becoming an uncertain network notification.

Local data read for idle detection

To distinguish a final root completion from an intermediate result, the notifier can read:

SQLite access is opened read-only and query-only. Goal awareness selects the goal status, not its objective. The notifier does not update Codex databases.

The rollout watcher reads newly appended complete JSONL lines in memory to find task_complete and turn_aborted. It persists a cursor containing the rollout path, byte offset, timestamp, and thread ID. It does not copy prompt lines into watch/.

The local Codex rollout can itself contain prompts, assistant content, tool data, and paths. The notifier needs read access to that pre-existing file, but its own state intentionally stores neither Codex input-messages nor prompt bodies. Never attach rollout or database files to a public issue.

What leaves the host

By default, one ntfy publication can contain:

The default JSON title is only <project>, while the single white_check_mark tag renders one completion emoji before it in ntfy. The one-line body is <origin> · #<thread8>. The templates add no notifier name, completion word, model name, status label, or text emoji. Markdown is disabled, and default ntfy priority 3 is represented by omitting the priority member from the outgoing JSON. These choices reduce visual and wire noise; they do not make the remaining metadata anonymous.

include_thread_title: true opts into the locally indexed Codex task title when available. When that title differs from the project, the project moves into the body so the location is retained. A task title can summarize the user’s request.

include_full_path: true uses the sanitized working-directory path as the body’s location context instead of a project-name context item. A full path can reveal usernames, clients, repository names, mounts, or organization structure.

include_message: true adds a sanitized and truncated copy of the final assistant message ahead of the context. max_message_chars limits that excerpt to 180 characters by default. With the default markdown: false, whitespace is normalized and the whole body stays on one line. The complete ntfy message is also hard-capped at 3,500 UTF-8 bytes, even if max_message_chars is increased. Truncation and the byte cap limit size, not sensitivity.

A thread title or assistant response can summarize sensitive prompt context. “Raw prompts are not copied” is not equivalent to “no sensitive context can leave the host.”

The ntfy server sees normal connection metadata such as source IP, request time, and user agent. Its retention/access logs are outside this project’s control. ntfy clients may display content on a lock screen; configure client privacy accordingly.

The server URL must be absolute HTTP(S) and must not contain URI userinfo. Put authentication only in the dedicated token or username/password fields. Doctor output reduces the server to scheme, host, and non-default port, omitting path, query, fragment, and credentials.

What remains on disk

The private configuration is ~/.codex/ntfy-config.json. It can contain server URL, topic, token, username, password, and installer-managed watch_roots entries with local/UNC Codex and SQLite paths for selected WSL distributions.

~/.codex/ntfy-state can contain:

The operational log records short event keys, gate reasons, origin labels, retry state, and sanitized errors. Redaction is best-effort; logs are not automatically safe to publish.

Installers keep up to ten timestamped directories under ~/.codex/ntfy-backups. A backup can contain credentials, config.toml, and hook registration. Rotation is by count, not age. Remote hosts own their own copies and backups.

include_message is enforced both when content is captured and again when the worker builds the network payload. If it is changed from true to false, final-message text already present in pending or outbox records is excluded at send time and does not leave the host through a later retry. The setting does not scrub that content from existing state, dead letters, logs, or backups.

For an urgent opt-out, stop the worker before editing the private config so it cannot begin another request concurrently. No local setting can recall a request already in flight or content already accepted, retained, or displayed by ntfy. Other content-setting changes likewise do not rewrite existing local state or remote/client history.

Hook review and trust

The installer writes a managed Stop handler to ~/.codex/hooks.json and preserves unrelated hook groups and metadata. Codex requires the operator to review a new hook through /hooks.

The installer deliberately does not edit Codex’s trust store or simulate approval. This keeps code execution consent with the user. Review the exact command in every Windows, WSL, Linux, or remote Codex environment before trusting it.

The hook command contains local executable paths but no ntfy topic or credential. Those secrets remain in the private config or worker environment. Treat hooks.json as environment metadata even though it should not contain authentication values.

Redaction limits

When content storage is enabled, the notifier normalizes whitespace, truncates values, and redacts several common patterns, including authorization headers, password/token/key assignments, selected provider token prefixes, and ntfy topic URLs.

Regex redaction cannot reliably detect every secret, custom hostname, source-code credential, private key, personal datum, or value whose context has been removed. It can also produce false positives. The safest settings for sensitive work are include_message: false, include_thread_title: false, and include_full_path: false.

Topics and credentials

An unguessable topic on a public ntfy service acts like a capability: anyone who learns it may be able to subscribe or publish, depending on server policy. Prefer explicit access control on a trusted server when notification metadata is sensitive.

Recommended practice:

Environment variables override server/authentication fields. They reduce long-lived config content only if the surrounding process, service manager, shell history, crash reporting, and process inspection are also trusted. Clear temporary variables after installation.

Transport protections

The notifier:

Anonymous HTTP publication is not blocked because no authorization header is present, but topic and message remain visible and modifiable in transit. HTTPS is strongly recommended.

allow_insecure_auth: true sends reusable credentials without transport confidentiality. Reserve it for a separately protected network tunnel whose risk is understood.

TLS validation uses the host operating system/runtime trust store. The project does not implement certificate pinning.

Filesystem protections

Installers attempt to limit access:

Platform Protection
Windows Private ACLs for the current user, SYSTEM, and local administrators on config, state, staging, hooks, and backups where managed.
Linux/WSL Mode 0600 for private config/TOML/JSON and 0700 for executable or state directories where installed.
Linux systemd UMask=0077, NoNewPrivileges=true, and PrivateTmp=true.

These controls do not protect against the same user account, administrator/root, malware in that security context, a compromised SSH endpoint, or offline access to unencrypted storage. Use full-disk encryption and appropriate host security.

Inspect permissions without printing contents:

icacls "$HOME\.codex\ntfy-config.json"
icacls "$HOME\.codex\hooks.json"
icacls "$HOME\.codex\ntfy-state"
stat -c '%a %U:%G %n' "$HOME/.codex/ntfy-config.json" "$HOME/.codex/hooks.json" "$HOME/.codex/ntfy-state"

Remote installation trust

Remote installers use existing OpenSSH configuration and host verification. They copy the private destination, authentication, and policy through a restricted staging directory, then install it for the remote account. Host-local watch_roots are cleared because source-machine WSL/custom paths are not portable. Therefore:

Prefer a host-specific publish-only token. Verify a new host interactively before unattended installation.

Retention and erasure

Defaults are 14 days for sent/suppressed receipts and 30 days for dead letters. Cleanup runs when a worker starts.

Pending idle candidates and network-ready outbox records have no time-based expiry because silently dropping an unfinished/offline notification would violate the delivery goal. Watch cursors persist so scans can resume without replaying old rollout history. In strict mode, a candidate with permanently missing evidence can therefore remain on disk until corrected or deliberately removed.

For deliberate local erasure:

  1. stop the worker;
  2. decide whether pending and outbox events should be delivered or discarded;
  3. delete the relevant state and backup directories on every host;
  4. delete or replace private config and managed hook/config entries as appropriate;
  5. revoke or rotate server-side credentials and topic access;
  6. clear ntfy client history and follow the server’s deletion/retention procedure.

Deleting local files does not recall a notification already published to ntfy. See Uninstall and rollback.

Pure cloud boundary

The notifier observes local hook invocations, local Codex rollout files, and local databases. A task executed only in a hosted/cloud environment with no lifecycle state mirrored locally is outside that observation boundary. No local configuration can guarantee a notification for such a task.

Safe support and disclosure

--doctor/-Doctor reports configuration state rather than secret values and is the preferred first diagnostic. Review hostnames and paths before sharing even sanitized output.

Never attach raw files from ntfy-state, ntfy-backups, Codex sessions, Codex databases, shell environments, hooks.json, or private config to a public issue. Replace hostnames, usernames, paths, IDs, topics, and URLs in logs. Report vulnerabilities through SECURITY.md.