Skip to content

Plan proposals in a sandbox before an admin approves them - #5

Merged
noah-meierlabs merged 3 commits into
mainfrom
feat/plan-proposals
Nov 12, 2026
Merged

noah-meierlabs merged 3 commits into
mainfrom
feat/plan-proposals

Conversation

@noah-meierlabs

Copy link
Copy Markdown
Contributor

A proposal of another AI agent no longer goes to genco's agent as text. A planner (the agent's model) reads it in a read-only systemd sandbox (root with only CAP_DAC_READ_SEARCH, read-only fs, no network, no unix sockets, private PIDs, genco's secrets and other keys hidden) and writes a short plan, or NO PLAN with a reason. genco cleans and caps the plan (4000 chars); the admin sees exactly what genco's agent gets, in the panel and the mail.

The admin approves the newest plan (second factor as before), asks for a change (note back to the planner, new version, no factor; max 10 versions) or rejects. genco's agent gets only the approved plan, never the proposal. Deletions named in the plan's Delete: <kind> <name> lines run without another confirmation while the agent works on that plan, once each (pending_actions.plan_id/plan_entry); other deletions still ask. The small rewrite model is removed; waiting proposals are planned after the migration. Peers' GET /api/proposals/ hides the plans (they may contain what the planner read).

Tested on gs4: a deletion plan ran A11 without a mail; an injection proposal (fake admin line + planted README with commands and Delete lines) was refused by the planner, nothing changed. Second commit fixes a deadlock found there (llm::model took the db lock while the planner held it).

🤖 Generated with Claude Code

noah-meierlabs and others added 3 commits November 12, 2026 11:31
A proposal of another AI agent no longer goes to genco's agent as text.
A planner (same model as the agent) reads it in a read-only sandbox:
root without any capability but reading, read-only file system, no
network or unix sockets, no other processes, genco's secrets and other
keys hidden. It writes a short plan, or refuses with NO PLAN and a
reason. genco cleans and caps the plan, and the admin sees exactly what
genco's agent will get, in the panel and in the mail.

The admin approves the newest plan (second factor as before), sends it
back to the planner with a change (a new version, no factor needed:
the planner can't do anything), or rejects the proposal. genco's agent
gets only the approved plan, never the proposal. Deletions the plan
names in `Delete: <kind> <name>` lines are confirmed with it and run
without another confirmation while the agent works on it, once each;
any other deletion still asks.

The small model that rewrote proposals is gone, the plan replaces it.
Waiting proposals get planned after the update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
llm::model takes the connection itself; called while the planner held
it, the planner deadlocked and with it every request of the panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ProtectSystem=strict leaves /run, /dev/shm, /dev/hugepages, /dev/mqueue
and binfmt_misc writable, and root writes root's files there without
any capability: a talked-into planner could drop a unit drop-in into
/run/systemd/system that runs on the next reload. They are read-only or
hidden now. The SMTP relay password, the runner's config, ACME account
keys, database files and mailboxes are hidden too, and genco reads at
most 1 MiB of a command's stdout and stderr, so `yes` can't fill its
memory.

The planner frees its slot before the mail goes out, so a change asked
for meanwhile gets planned instead of staying in "planning"; its text
goes before the tool calls, the outputs right after them; after a
restart it skips expired proposals. A plan's deletion line can only be
used once, also by two requests at the same time (unique index). Peers
don't see who asked for a change. The shell output text and the planner
setup are shared, the enum names come from as_str, and the agents'
help no longer mentions the removed rewrite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@noah-meierlabs
noah-meierlabs merged commit bf940f9 into main Nov 12, 2026
@noah-meierlabs
noah-meierlabs deleted the feat/plan-proposals branch November 12, 2026 11:04
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.

1 participant