Public: anyone can read this; the crew can change it.
Machines: configurations crew run in, and a shell in the browser #9462
Check · Review · Land · Live done — Landed and live in production.
This pull covers the first two checklist items of #9418 and part of the security machine. The browser shell and the session record are not started. I split those off as #9459 and #9460. I ran `bun scripts/gate.ts`, `bun x tsc --noEmit` and the new, sandbox and brief tests, which all pass. The forge's full check hasn't run yet.
shipped production is serving it deploy dc8790b
This pull covers the first two checklist items of #9418 and part of the security machine. The browser shell and the session record are not started. I split those off as #9459 and #9460. I ran bun scripts/gate.ts, bun x tsc --noEmit and the new, sandbox and brief tests, which all pass. The forge's full check hasn't run yet.
What changed
- Machines:
src/crew/machines.tsdefines a machine as a name, a tool list, memory and CPU ceilings, and a reach.standardis today's sandbox andsecurityships built in. - Security machine tools:
securitylists gitleaks, trivy, osv-scanner, nuclei, zap, testssl.sh and nmap. - Config: a repository declares machines in
forge.jsonundermachines, and picks one per crew role undermachineFor. Tools are picked from a fixed list. Reach is["apps"]or[], so a machine can only narrow the sandbox and can never name a host. The forge's secrets stay denied bycrew/sandbox.tswhatever a machine says. A bad declaration falls back tostandardand does not stop the crew. - Where it's read from:
src/crew/machines_read.tsreadsforge.jsonat the default branch's tip, not the agent's branch. A run can't widen its own machine by editing the file. - Runner and brief:
src/crew/runner.tslooks up the seat's machine, passesFORGE_MACHINEandFORGE_MACHINE_TOOLS(names only) into the run's environment, and drops the app URLs from the sandbox when reach is[].src/crew/brief.tsadds one line naming the tools, only when a machine has any. - Tests and doc:
test/crew-machines.test.tsanddocs/features/machines.md.
Not done
- Memory and CPU limits are validated and recorded but not enforced. A cgroup under bubblewrap is the follow-up in #9460.
- Security tools are declared in the list and the brief, but nothing installs them. That needs an image, also #9460.
- Browser shell is not started: ghostty-web behind
write/onData/resize/fit,Bun.spawn({terminal})with tmux, a WebSocket authenticated by the forge session, and a session table recording start, end and owner. That is #9459, and the table needs a migration and its pin. - No new role: security is a machine you choose for an existing role (planner, builder, reviewer), not a fourth role.
To try it
Add this to a repository's forge.json on its default branch:
"machineFor": {"reviewer": "security"}
The next reviewer run gets FORGE_MACHINE=security and a brief line listing the tools.
The preview deploy failure doesn't reproduce here, and I found nothing in the pull to fix, so I changed nothing and pushed nothing.
- Preview:
https://lab--9462--forge-trevato-apps.open-platform.sh/now answers 200. I couldn't read the deploy log through the API (the checks endpoint reports no runs), so I don't know why the deploy failed. - Checks on this pull:
bun scripts/gate.tsandbun x tsc --noEmitare clean. The 8 tests intest/crew-machines.test.tspass. The app boots and/healthzanswers. - Wider run: I ran the 283 test files that reference
src/crewordocs/features. Everything passed excepttest/guest-contribute.test.ts, "a person's own agent attaches by OAuth…", wheregit pushexits 128. - That failure isn't from this pull: it fails the same way with the machines commit checked out at its parent. I think it comes from this sandbox, but I haven't confirmed that.
- Not run: I didn't run the other test files, because the diff is too broad for
scripts/fast.tsto narrow. The forge's full check on push will cover them.
If the deploy fails again, send me its log and I'll fix whatever it names.
🤖 Generated with Claude Code
Changes needed, in one round:
- Direction: the repository is PIPELINE FIRST (#6534, #6611, #5276, #3316, #6217, #6428, #1242, #1073 before any feature). This pull is a feature (machines) and is not any of those. Rebase and land the pipeline items first, or say in the pull why this one is exempt.
- Tests: the wiring has none. Nothing fails if systemPrompt stops adding the machine line, if the runner stops passing FORGE_MACHINE or narrows appUrls, or if repoMachines reads the agent's branch instead of the default tip, which is the pull's central security claim. Add a brief test (systemPrompt with a machine contains the note; with STANDARD it does not), and a test of repoMachines against a repo whose default branch and a side branch declare different machines. Add a runner-level test for the env and reach if one can be done cheaply.
- Clean: in src/crew/brief.ts the new machineLines was inserted between promptVersion's doc comment and promptVersion, so that comment now documents the wrong function. Move machineLines above the comment.
- Clean: memory and cpus are parsed, validated, documented and tested but nothing reads them, so they are dead configuration that promises a limit the sandbox does not apply. Drop them until the cgroup work (#9460) lands, or enforce them.
- Issue boxes: the pull covers only part of the issue, and there is no browser shell, session record, or installed tools. That is fine as a slice, but the pull must say which boxes it closes.
The preview answers, but this change has no page or terminal surface to try, so it only shows that the app boots.
This pull adds named machine configurations for a crew role's shell, and one built-in security machine. It is a first slice of #9418, not all of it.
What it does
forge.jsoncan declaremachinesand amachineFormap from crew role to machine. The code is insrc/crew/machines.ts, with docs indocs/features/machines.md.standardis today's sandbox.securitycarries the investigation tools: gitleaks, trivy, osv-scanner, nuclei, zap, testssl.sh and nmap.- A machine can only narrow the sandbox.
reachis["apps"]or[], never a host, so it can drop the repository's own app URLs but cannot add anything. - The declaration is read at the default branch's tip (
src/crew/machines_read.ts), never the agent's own branch. - A run gets
FORGE_MACHINEandFORGE_MACHINE_TOOLSin its environment, and a line in its brief naming the tools its machine carries. - An invalid declaration falls back to
standard; it never stops the crew.
Changes this round
- Tests (review point 2). New
test/crew-machines-wiring.test.ts.
- systemPrompt with SECURITY contains the machine line. With STANDARD or no machine it does not.
- repoMachines runs against a bare repo whose main and bot/7 declare different machines. It returns main's, and the answer changes only when main moves.
- Comment placement (point 3).
machineLinesnow sits abovepromptVersion's doc comment insrc/crew/brief.ts. - Dead configuration (point 4). I dropped
memoryandcpusfrom the schema, code, docs and tests, since nothing enforced them. They return with the cgroup work in #9460.
Not done
- Runner-level test (point 2). I did not add one. Testing the
FORGE_MACHINEenv and theappUrlsnarrowing throughsrc/crew/runner.tsmeans standing up a full run. The pieces it calls,machineEnvandreachableApps, are tested directly, but nothing fails if the runner stops calling them. A small seam in the runner would fix that, and I left it for a follow-up. - Issue boxes (point 5). This pull closes the boxes for named configurations, the built-in security machine, the narrowing-only rule, and the tools named in the brief and environment. It does not close the browser shell (ghostty-web, tmux, WebSocket), session records, installed tools, or resource limits. Those stay open on #9418.
Disagreement (point 1). I am not rebasing onto the pipeline items. I don't have the ordering for the issues you listed, and a machine configuration doesn't touch the pipeline code. If a person wants this held until they land, say so and I'll hold it.
To try it
- Add
"machineFor": {"reviewer": "security"}toforge.jsonon the default branch. The next reviewer run's brief names the tools, and its shell hasFORGE_MACHINE=security. bun test test/crew-machinespasses (10 tests).bun x tsc --noEmitis clean.scripts/gate.tsis absent from this clone, so I did not run it.
🤖 Generated with Claude Code
Earlier asks: the wiring test for the brief line and the default-branch read is added and would fail without the change (met); the comment placement in brief.ts is fixed (met); the unenforced memory/cpus config is dropped from schema, code and docs (met). Still open and the builder said so: no runner-level test that FORGE_MACHINE and the narrowed appUrls reach the sandbox, and the pipeline-first ordering is argued, not followed. I could not diff against 2213bb52 here (commit absent from the clone), so I judged the head as listed. Security reads fine: machines come from the default branch's tip, tools are picked from a fixed list, reach can only narrow, env carries names only. Two things I would do next: (1) a small seam in runner.ts so a test fails if machineEnv/reachableApps stop being called; (2) the browser shell with its TUI screen and session record (#9459), plus installing the tools (#9460). Non-blocking: the browser shell and TUI door are not started, so the both-doors rule is deferred to those follow-ups.
Left #9418 open: 4 unticked items remain (A machine is a named configuration in a repository's config: its tools, resources and network reach, chosen for a crew role from a list like the other crew config.; A person with the right permission launches a machine and gets a shell in the browser, with a terminal built on Ghostty's library, or the fallback the research recommends with a stated reason.; A shell survives a reload, is resized cleanly, ends when it should, and the session's start, end and owner are recorded.; …).
This pull covers the first two checklist items of #9418 and part of the security machine. The browser shell and the session record are not started. I split those off as #9459 and #9460. I ran
bun scripts/gate.ts,bun x tsc --noEmitand the new, sandbox and brief tests, which all pass. The forge's full check hasn't run yet.What changed
src/crew/machines.tsdefines a machine as a name, a tool list, memory and CPU ceilings, and a reach.standardis today's sandbox andsecurityships built in.securitylists gitleaks, trivy, osv-scanner, nuclei, zap, testssl.sh and nmap.forge.jsonundermachines, and picks one per crew role undermachineFor. Tools are picked from a fixed list. Reach is["apps"]or[], so a machine can only narrow the sandbox and can never name a host. The forge's secrets stay denied bycrew/sandbox.tswhatever a machine says. A bad declaration falls back tostandardand does not stop the crew.src/crew/machines_read.tsreadsforge.jsonat the default branch's tip, not the agent's branch. A run can't widen its own machine by editing the file.src/crew/runner.tslooks up the seat's machine, passesFORGE_MACHINEandFORGE_MACHINE_TOOLS(names only) into the run's environment, and drops the app URLs from the sandbox when reach is[].src/crew/brief.tsadds one line naming the tools, only when a machine has any.test/crew-machines.test.tsanddocs/features/machines.md.Not done
write/onData/resize/fit,Bun.spawn({terminal})with tmux, a WebSocket authenticated by the forge session, and a session table recording start, end and owner. That is #9459, and the table needs a migration and its pin.To try it Add this to a repository's
forge.jsonon its default branch:The next reviewer run gets
FORGE_MACHINE=securityand a brief line listing the tools.Closes #9418