Skip to content

FE-1220: Harden and document Petrinaut optimization execution isolation#9058

Open
kube wants to merge 2 commits into
cf/fe-1218-petrinaut-optimization-streaming-cancellationfrom
cf/fe-1220-petrinaut-optimization-execution-isolation
Open

FE-1220: Harden and document Petrinaut optimization execution isolation#9058
kube wants to merge 2 commits into
cf/fe-1218-petrinaut-optimization-streaming-cancellationfrom
cf/fe-1220-petrinaut-optimization-execution-isolation

Conversation

@kube

@kube kube commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Closes FE-1220 (part of FE-1217).

Stacked on #9057 — the base is cf/fe-1218-petrinaut-optimization-streaming-cancellation; only the last commit is new here.

What

Process/resource hardening (apps/petrinaut-opt)

  • Complete process-group termination: every close() path now ends with a SIGKILL sweep of the CLI's process group. Previously the graceful (EOF) path never signalled the group at all, and the escalation path stopped once the group leader exited — descendants could survive, reparent to uvicorn as PID 1, and accumulate as zombies. tini now runs as PID 1 to reap orphans.
  • OS resource limits on the CLI via resource.prlimit (Linux): RLIMIT_CPU (default 900 s), RLIMIT_AS (default 2 GiB — the actual resident-memory bound; --max-old-space-size caps only the V8 heap), RLIMIT_NPROC (default 256). All configurable through HASH_PETRINAUT_OPT_CLI_* env vars, 0 disables. Applied from the parent, so no fork-unsafe preexec_fn.
  • Study wall-clock ceiling (HASH_PETRINAUT_OPT_MAX_STUDY_SECONDS, default 900 s): a study can no longer hold one of the 4 admission slots for trials × 240 s. On expiry the stream emits a terminal error frame and the CLI is terminated promptly; a study whose completion sentinel is already queued when the deadline lapses is still reported as completed, not timed out.

Threat model and documentation

  • New apps/petrinaut-opt/docs/threat-model.md: execution path, attacker model, per-layer mitigations (verified against the code), known limitations (per-real-UID RLIMIT_NPROC accounting, setsid() escape from the group sweep, prlimit timing window, pgid recycling), required out-of-repo deployment configuration (read-only rootfs, task memory sized against RLIMIT_AS, deny-all egress security group, pidsLimit), and the documented decision that no additional V8 sandbox is required — the hot code surfaces already go through the restricted HIR compiler and FE-1219 removes the remaining raw scenario-expression execution.
  • README documents the new env vars; the CLI's OPTIMIZATION_INTEGRATION.md now documents the cancellation/shutdown contract (signal-first for busy CLIs, group signalling, escalation).

Tests

  • Grandchild sweep with a real subprocess tree (mutation-verified: fails without the sweep).
  • Prompt close signals before any shutdown wait; busy-process termination under 2 s.
  • rlimit application (real process) and env parsing/disable semantics.
  • Ceiling termination (stuck study) and the completed-at-the-boundary case (no misreport).
  • Real-subprocess tests pin the limits off so per-UID RLIMIT_NPROC accounting on busy hosts cannot fail them.

apps/petrinaut-opt: 55 pytest tests pass.

Production infrastructure follow-ups

ECS task definitions live outside this repository; the required settings are enumerated in the threat model's Required deployment configuration section (read-only rootfs + tmpfs, memory sizing, egress policy, pidsLimit).

🤖 Generated with Claude Code

Petrinaut Optimizer (apps/petrinaut-opt):
- Apply resource.prlimit bounds (Linux) to every spawned CLI process:
  RLIMIT_CPU, RLIMIT_AS, and RLIMIT_NPROC, each configurable via
  HASH_PETRINAUT_OPT_CLI_* environment variables with zero disabling.
- Sweep the CLI's process group with SIGKILL at the end of every close
  path, so descendants that remain in the group cannot outlive the study
  even when the CLI itself exits gracefully on stdin EOF.
- Enforce a per-study wall-clock ceiling
  (HASH_PETRINAUT_OPT_MAX_STUDY_SECONDS, default 900 s) in both stream
  loops, emitting a terminal error frame and closing the CLI promptly;
  a study whose terminal sentinel is already queued when the deadline
  lapses is still reported as completed.
- Run tini as PID 1 in the Docker image so orphaned descendants are
  reaped instead of accumulating as zombies.

Documentation:
- New apps/petrinaut-opt/docs/threat-model.md: execution-path threat
  model, per-layer mitigations, known limitations (per-UID RLIMIT_NPROC
  accounting, setsid escape from the group sweep, prlimit timing, pgid
  recycling), required out-of-repo deployment configuration (read-only
  rootfs, task memory sized against RLIMIT_AS, deny-all egress,
  pidsLimit), and the decision that no additional V8 sandbox is needed.
- README documents the new environment variables; the CLI's
  OPTIMIZATION_INTEGRATION.md documents the cancellation and shutdown
  contract.

Tests cover the grandchild sweep (real subprocess, mutation-verified),
prompt-vs-graceful shutdown ordering, rlimit application and parsing,
ceiling-based termination, and the completed-at-the-boundary case. Real
subprocess tests pin the limits off so per-UID NPROC accounting on busy
hosts cannot fail them.
@kube kube self-assigned this Jul 19, 2026
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
hash Ignored Ignored Preview Jul 19, 2026 2:54am
hashdotdesign-tokens Ignored Ignored Preview Jul 19, 2026 2:54am
petrinaut Skipped Skipped Jul 19, 2026 2:54am

@github-actions github-actions Bot added area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/tests New or updated tests area/apps labels Jul 19, 2026
@vercel
vercel Bot temporarily deployed to Preview – petrinaut July 19, 2026 02:53 Inactive
@kube
kube marked this pull request as ready for review July 19, 2026 03:14
@kube
kube requested a review from YannisZa July 19, 2026 03:14
@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches subprocess lifecycle and resource limits on a security-sensitive optimization path; behavior is well-tested but mis-tuned RLIMIT_NPROC or memory limits could affect availability under load.

Overview
Hardens Petrinaut optimization execution isolation and documents the security model for reviewers and operators.

Runtime hardening (petrinaut_client, Dockerfile): every CLI shutdown now ends with a SIGKILL process-group sweep so grandchildren cannot outlive the study; tini is PID 1 to reap orphans. Linux prlimit on each spawned CLI bounds CPU, address space, and process count via HASH_PETRINAUT_OPT_CLI_* env vars. petrinaut_optimizer enforces a per-study wall-clock ceiling (HASH_PETRINAUT_OPT_MAX_STUDY_SECONDS, default 900s) on SSE streams, emitting a terminal error and tearing down the CLI without mislabeling a study that already queued its completion sentinel.

Documentation: new docs/threat-model.md (execution path, mitigations, limitations, required ECS settings, no extra V8 sandbox rationale); README lists isolation env vars; OPTIMIZATION_INTEGRATION.md documents parent cancellation/shutdown expectations.

Tests cover group sweep, rlimit application/parsing, study ceiling vs. boundary completion, and disabling limits in real-subprocess tests.

Reviewed by Cursor Bugbot for commit 47f2fea. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 47f2fea. Configure here.

f"{json.dumps({'state': 'ERROR', 'message': message})}"
f"\n\n"
)
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pending sentinel misclassified as timeout

Medium Severity

When the study wall-clock deadline is reached, timeout handling treats an empty events queue as proof the study is still running. The worker always enqueues the completion sentinel via call_soon_threadsafe, so a study that just finished can look idle until that callback runs, triggering a false execution-limit error instead of the normal done path.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 47f2fea. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) area/tests New or updated tests type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

2 participants