Skip to content

feat: add authenticated recorder control channel - #79

Merged
abrichr merged 1 commit into
mainfrom
codex/capture-control-ipc
Aug 20, 2026
Merged

feat: add authenticated recorder control channel#79
abrichr merged 1 commit into
mainfrom
codex/capture-control-ipc

Conversation

@abrichr

@abrichr abrichr commented Aug 18, 2026

Copy link
Copy Markdown
Member

Summary

  • add an authenticated cross-process status and stop contract for active Capture recorders
  • bind every request and response to the exact session, PID, process start identity, and request ID
  • store the per-session capability only in a current-user-only runtime descriptor
  • use POSIX owner modes, macOS extended-ACL clearing, and a protected current-user Windows DACL, with fail-closed verification
  • make stop idempotent and report success only after database and event integrity verification
  • retain atomic incomplete terminal metadata after timeout, failure, or discovered crash
  • remove exact runtime descriptors after normal exit, abort, direct stop, and authenticated stale-process discovery
  • expose stable Python APIs and capture status / capture stop commands for the launcher
  • add a focused macOS and Windows pull-request workflow for the real subprocess contract

Security model

The endpoint binds to IPv4 loopback on an ephemeral port. Every message uses
HMAC-SHA256 with an unguessable per-session capability. The capability does not
enter process arguments, logs, responses, or capture artifacts. The runtime
directory and descriptor must be current-user-only. Capture refuses to start the
control channel when it cannot establish and verify that boundary, and
Recorder.__enter__ re-raises that refusal, so the recording does not start
either.

Requests carry a UUID request ID with a bounded replay cache. Cache saturation
raises request_limit rather than clearing still-valid IDs, so replay cannot be
reopened by flooding.

Stale discovery verifies the authenticated owner-protected descriptor and exact
process identity. On Windows it checks the kernel process signal directly before
fallible process metadata calls. It rejects PID reuse and keeps an ambiguous or
access-denied process conservatively live.

stop_recording returns only when complete, integrity_verified, and
phase == "complete" all hold. Any other outcome raises CaptureControlError,
and capture stop turns that into exit code 1. A stop can never report success
without proof.

Rebase

This branch is rebased onto main after Capture #76
(07990886b1e206bcc807ba95f822ef859e9b7325) and Capture #78
(b04e8292aad9b2df36bcd9c63fe9ea0c04d5b760).

Two conflict classes were resolved in openadapt_capture/recorder.py:

  1. The event writers now take the new terminate_writers event instead of
    terminate_processing, keeping main's utils.WrapStdout(write_events)
    target. Producers stop before writers, so committed rows can be reconciled.
  2. The browser_event_writer process stays removed. feat: qualify resilient native capture #78 removed the browser
    recording path, and record() refuses RECORD_BROWSER_EVENTS before any
    worker starts.

The pre-rebase head 5fcb2ceee57cabf6cdc1d72dd3fd8060e17bac3a is preserved on
branch codex/capture-control-ipc-original.

This branch carries no lifecycle label change. The package stays Experimental
in README.md and stays Development Status :: 2 - Pre-Alpha in
pyproject.toml.

Verification on the rebased head

  • focused control, launcher, and high-level suite: 60 passed, 1 skipped
  • standard non-slow suite: 449 passed, 2 skipped, 9 deselected
  • Ruff on openadapt_capture/: passed
  • python scripts/check_changelog.py: passed
  • wheel and source distribution build and scripts/verify_distribution.py: passed
  • python scripts/check_source_boundary.py --require-dist: passed

Closes #77

🤖 Generated with Claude Code

Add an authenticated cross-process status and stop contract for an active
Capture recorder, so a launcher can stop a recording and prove that the
recording finished.

- bind every request and response to the exact session, PID, process start
  identity, and request ID, with HMAC-SHA256 over an unguessable per-session
  capability
- store the capability only in a current-user-only runtime descriptor, using
  POSIX owner modes, macOS extended-ACL clearing, and a protected
  current-user Windows DACL, each verified after it is applied
- refuse to start the control channel, and therefore the recording, when that
  boundary cannot be established or verified
- make stop idempotent and report success only after database and event
  integrity verification; a timeout or a failed check never returns a
  success-shaped status
- retain atomic incomplete terminal metadata after timeout, failure, or a
  discovered crash, and remove exact runtime descriptors after normal exit,
  abort, direct stop, and authenticated stale-process discovery
- shut the event writers down with a separate terminate_writers event, so
  producers stop before writers and the committed rows can be reconciled
- expose status_recording, stop_recording, discover_recorders, and
  RecorderStatus, plus capture status and capture stop commands
- add a focused macOS and Windows pull-request workflow for the real
  subprocess contract

The endpoint binds to IPv4 loopback on an ephemeral port. The capability does
not enter process arguments, logs, responses, or capture artifacts. Stale
discovery verifies the owner-protected descriptor and the exact process
identity, checks the Windows kernel process signal before fallible metadata
calls, rejects PID reuse, and keeps an ambiguous or access-denied process
conservatively live.

Closes #77

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@abrichr
abrichr force-pushed the codex/capture-control-ipc branch from 5fcb2ce to 9b4c325 Compare August 20, 2026 01:29
@abrichr
abrichr marked this pull request as ready for review August 20, 2026 01:30
@abrichr
abrichr merged commit 6109678 into main Aug 20, 2026
14 checks passed
@abrichr
abrichr deleted the codex/capture-control-ipc branch August 20, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an authenticated cross-process recorder control channel

1 participant