FE-1220: Harden and document Petrinaut optimization execution isolation - #9058
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
PR SummaryMedium Risk Overview Runtime hardening ( Documentation: new 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. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit 47f2fea. Configure here.
|
Converted back to draft: the stack was reordered so the streaming fixes (#9057) and correlated logging (#9059) can merge first to debug the staging |
47f2fea to
a38af6e
Compare
a38af6e to
e48cebd
Compare
cf9d973 to
633185f
Compare
e48cebd to
a72b3b5
Compare
74f438d to
af8d841
Compare
Bounds and documents what the Petrinaut CLI subprocess can do, and records the deployment isolation model the service relies on. - petrinaut_client.py: a SIGKILL sweep of the CLI's process group on every close path, so descendants cannot outlive the study; resource.prlimit (Linux) bounds each CLI process via RLIMIT_CPU / RLIMIT_AS / RLIMIT_NPROC, configurable through HASH_PETRINAUT_OPT_CLI_* (0 disables). - Dockerfile: tini as PID 1 to reap any orphan that escapes the sweep. - docs/threat-model.md: the execution path, per-layer mitigations, required out-of-repo deployment configuration, and the documented decision that container/process isolation is sufficient (no additional V8 sandbox). - README: the isolation section and the CLI limit environment variables. - OPTIMIZATION_INTEGRATION.md: the CLI cancellation/shutdown contract. The wall-clock ceiling this branch originally added to the legacy stream_all and stream_best generators is dropped: main already bounds detached runs via HASH_PETRINAUT_OPT_MAX_STUDY_SECONDS, and the legacy generators are removed by the frontend cutover below this in the stack. The threat model is updated for the detached-run architecture — runs outlive connections, ownership is enforced in the optimizer by account tag, and slot occupancy is bounded by the ceiling and detach-grace reaping rather than by connection deadlines.
a72b3b5 to
0cdc094
Compare


🌟 What is the purpose of this PR?
Bounds what the Petrinaut CLI subprocess can do, and writes down the isolation model the optimizer service depends on. A malicious optimization manifest can no longer leave descendants running after its study ends, and each CLI process is capped on CPU time, address space, and process count.
🔗 Related links
🚫 Blocked by
#9066 → this → #9060)🔍 What does this change?
petrinaut_client.py: aSIGKILLsweep of the CLI's process group on every close path, so descendants cannot outlive the study;resource.prlimit(Linux) bounds each CLI process viaRLIMIT_CPU/RLIMIT_AS/RLIMIT_NPROC, configurable throughHASH_PETRINAUT_OPT_CLI_*(0disables).docker/Dockerfile:tinias PID 1, so any orphan that escapes the sweep is reaped instead of becoming a zombie under uvicorn.docs/threat-model.md(new): the execution path, per-layer mitigations, the required out-of-repo deployment configuration, and the documented decision that container/process isolation is sufficient — no additional V8 sandbox.README.md: the security/isolation section and the CLI limit environment variables.OPTIMIZATION_INTEGRATION.md: the CLI cancellation/shutdown contract that parents must implement.Dropped during the rebase: this branch originally added a study wall-clock ceiling to the legacy
stream_all/stream_bestgenerators.mainnow bounds detached runs viaHASH_PETRINAUT_OPT_MAX_STUDY_SECONDS(FE-1224), and #9066 below this in the stack deletes those generators entirely — so that part would have been written and then deleted.The threat model is written against the current architecture rather than the one this branch was first drafted on: runs are detached and outlive their connections, ownership is enforced in the optimizer by account tag (a foreign run answers 404 so ids cannot be probed), and study-slot occupancy is bounded by the wall-clock ceiling plus detach-grace reaping instead of by connection deadlines. Each claim was checked against the code.
Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
RLIMIT_NPROCis enforced by Linux against the service user's total task count, not this child's descendants; container-levelpidsLimit(SRE-830) is the real backstop.prlimitis applied from the parent immediately after spawn, so the child is briefly unbounded; container limits remain the outer bound.setsid()/setpgid()leaves the CLI's process group and escapes the sweep — documented in the threat model's limitations.pidsLimit) live in the out-of-repo infra and are tracked in SRE-830.🛡 What tests cover this?
apps/petrinaut-opt: 93 pytest green on this layer — including the process-group sweep verified with a real grandchild process (mutation-checked: removing the sweep fails the test),prlimitapplication and env parsing, and the prompt-termination paths.❓ How to test this?
cd apps/petrinaut-opt && uv run pytesttiniis PID 1:docker build --file apps/petrinaut-opt/docker/Dockerfile --tag petrinaut-opt:local .thendocker run --rm petrinaut-opt:local ps -o pid,comm(or inspect the entrypoint).🤖 Generated with Claude Code