Codex Lid Keeper changes an undocumented macOS power setting with root privileges. Its primary security goals are:
- never expose a general privileged command runner;
- never let a normal user replace the sudo-authorized executable;
- restore only a setting this project owns;
- fail safe after missing lifecycle events or component failure;
- avoid retaining Codex prompt or project content.
The installed executable lives under root-owned
/Library/PrivilegedHelperTools. The sudoers entry permits only:
com.zundu.codex-lid-keeper power enable-ac
com.zundu.codex-lid-keeper power enable-battery
com.zundu.codex-lid-keeper power restore
No command string, path, setting name, or setting value is supplied by Hook input.
Before changing a pmset profile, the root process writes a root-owned
ownership record containing the selected mode and previous AC/battery
disablesleep values. AC-only mode changes only the AC profile. Battery mode
captures and changes both profiles. If that record cannot be decoded,
restoration stops with an error instead of guessing.
A root LaunchDaemon restores owned state if the user agent's heartbeat becomes stale. It follows the recorded power mode, fails safe when live power is unknown, and enforces an independent 30% battery floor.
Hook JSON is read from standard input with a 1 MiB limit. The Hook atomically
queues a minimal event and returns without calling sudo, querying power, or
waiting for the user agent. Only these fields are decoded:
session_idturn_idcwd(reduced to the final path component)hook_event_name
Prompts, transcripts, model responses, tool inputs, and tool outputs are not
decoded or stored. cwd is reduced to its final path component before the
event is persisted.
To recognize tasks immediately, including work that began before Hook
installation, the user daemon opens Codex's local state read-only. It selects
only thread id, rollout_path, cwd, and update/archive metadata, then
examines at most the final 4 MiB of each recent rollout. Lines are rejected
before JSON decoding unless they contain an exact lifecycle marker. The
decoder models only:
- the
event_msgenvelope; task_startedortask_complete; andturn_id.
A separate compatibility query reads turn-state metadata only from rows whose
target is codex_core::session::turn. Neither path selects or models thread
titles, previews, first messages, prompt text, responses, or tool payloads.
No rollout contents are copied into Keeper state or logs. The full cwd is
reduced to its final component before it enters Keeper state. If these local
formats become unavailable or incompatible, runtime detection is disabled and
tracking falls back to Hooks.
Each queued event is limited to 64 KiB, created with 0600 permissions, and
validated again by the daemon. The file and event directory are synchronized
around the atomic rename. Events dated more than five minutes ahead of the
daemon clock are rejected so clock correction cannot create an excessively
long lease. Processing is idempotent by event ID. The spool is capped at 4,096
pending events; if it is full or unavailable, the Hook fails open so Codex work
is not blocked, while existing power ownership remains subject to the root
watchdog. State and log files are user-only, and the log rotates at 1 MiB with
one retained generation.
Hook configuration is merged natively in Swift. The merger preserves unrelated
handlers, removes only the exact Codex Lid Keeper command, creates a timestamped
backup, writes a mode-0600 temporary file, synchronizes it, and atomically
renames it into place. Release installation does not execute a downloaded
Python runtime or accept Hook-provided command paths.
The installer changes sensitive system locations. Review:
scripts/install_components.shscripts/uninstall_components.shSources/CodexLidKeeperCore/HooksConfiguration.swiftSources/CodexLidKeeperCLI/main.swiftResources/com.zundu.codex-lid-keeper.recovery.plistSources/CodexLidKeeperCore/CodexRuntimeTaskDetector.swift- the generated
/etc/sudoers.d/codex-lid-keeper
Before elevation, the bundled installer validates the app identifier, plist
files, architecture support, and strict code-signature consistency. Because
the signature is ad-hoc, this check does not authenticate the publisher. It
validates the generated sudoers fragment with visudo before installing it.
Codex separately requires Hook trust review.
Do not include secrets, Hook payloads, transcripts, or personal paths in a public report. Open a GitHub Security Advisory with:
- affected commit and macOS version;
- reproduction steps using dry-run mode where possible;
- the expected and observed privilege boundary;
- whether live power settings were changed.
The current default branch and latest App Alpha are maintained. The app is ad-hoc signed rather than Developer ID signed and notarized; an ad-hoc signature does not authenticate the publisher. The DMG publishes a SHA-256 checksum but cannot receive normal Gatekeeper trust without an Apple distribution identity. This project remains experimental and should not be deployed unattended.