toolroll 0.9.68 → 0.9.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/README.md +43 -17
- package/THIRD_PARTY_NOTICES.md +21 -1
- package/dist/agent-fence.d.ts +204 -81
- package/dist/agent-fence.js +467 -397
- package/dist/agentconfig.d.ts +1 -1
- package/dist/agentconfig.js +11 -2
- package/dist/batch-checks.js +14 -26
- package/dist/browser/THIRD_PARTY_NOTICES.txt +21 -0
- package/dist/builder.d.ts +9 -0
- package/dist/builder.js +50 -4
- package/dist/check-access.d.ts +51 -0
- package/dist/check-access.js +77 -0
- package/dist/coding-provider.d.ts +14 -0
- package/dist/coding-provider.js +33 -1
- package/dist/coding-workspace.d.ts +15 -0
- package/dist/coding-workspace.js +9 -2
- package/dist/contracts/console-api.d.ts +2 -0
- package/dist/contracts/console-api.js +2 -0
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +4 -0
- package/dist/egress.d.ts +2 -0
- package/dist/egress.js +13 -4
- package/dist/exec.d.ts +17 -3
- package/dist/exec.js +54 -17
- package/dist/flow-actions.d.ts +1 -0
- package/dist/flow-actions.js +3 -2
- package/dist/flow-code.d.ts +31 -2
- package/dist/flow-code.js +57 -5
- package/dist/flow-steps.d.ts +4 -0
- package/dist/flow-steps.js +9 -5
- package/dist/flow-triggers.d.ts +4 -0
- package/dist/flow-triggers.js +8 -3
- package/dist/invoke.d.ts +50 -3
- package/dist/invoke.js +204 -66
- package/dist/operate.d.ts +4 -0
- package/dist/operate.js +119 -0
- package/dist/project-delete-ui.d.ts +23 -6
- package/dist/project-delete-ui.js +94 -1
- package/dist/project-sandbox.d.ts +59 -0
- package/dist/project-sandbox.js +83 -0
- package/dist/project-tools.d.ts +29 -5
- package/dist/project-tools.js +43 -7
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -0
- package/dist/result-follow-ups.js +10 -15
- package/dist/sandbox-launch.d.ts +1 -0
- package/dist/sandbox-launch.js +148 -0
- package/dist/sandbox-runtime.d.ts +62 -0
- package/dist/sandbox-runtime.js +140 -0
- package/dist/scope.d.ts +37 -0
- package/dist/scope.js +32 -5
- package/dist/scout-browser-lock.d.ts +69 -0
- package/dist/scout-browser-lock.js +69 -0
- package/dist/scout-browser.d.ts +60 -1
- package/dist/scout-browser.js +200 -4
- package/dist/scout.d.ts +9 -4
- package/dist/scout.js +14 -4
- package/dist/serve.js +12 -1
- package/dist/server/http.d.ts +2 -0
- package/dist/server/render-results.d.ts +3 -0
- package/dist/server/render-results.js +1 -0
- package/dist/server/render-tasks.js +5 -1
- package/dist/server/route-table.js +2 -0
- package/dist/server/settings.js +64 -0
- package/dist/server/tasks.js +2 -0
- package/dist/store.d.ts +59 -2
- package/dist/store.js +166 -4
- package/dist/subagent-tools.d.ts +1 -0
- package/dist/subagent-tools.js +7 -5
- package/dist/surface.js +2 -1
- package/dist/tool-broker.d.ts +2 -1
- package/dist/tool-broker.js +2 -2
- package/dist/tool-launcher.js +10 -16
- package/package.json +2 -1
- package/scripts/provider-canary.mjs +5 -6
- package/dist/egress-bridge.d.ts +0 -17
- package/dist/egress-bridge.js +0 -111
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.70 — 2026-10-24
|
|
4
|
+
|
|
5
|
+
- **Every sandbox is now Anthropic's sandbox-runtime.** Builds, their setup
|
|
6
|
+
and check commands, a saved result's checks, flows and project tool servers
|
|
7
|
+
all run in `@anthropic-ai/sandbox-runtime` 0.0.79, the same sandbox Claude
|
|
8
|
+
Code uses: its own code writes the macOS Seatbelt profile and the Linux
|
|
9
|
+
bubblewrap and seccomp command for each launch. Toolroll hands it only a
|
|
10
|
+
short list: its own folders and database are off limits, the build's
|
|
11
|
+
checkout is writable, other tasks' checkouts are read and never written,
|
|
12
|
+
your project's read-deny list, and the run's proxy as the only way out.
|
|
13
|
+
`toolroll doctor` and Settings → Project → Home folder name the engine and
|
|
14
|
+
its version. Codex builds keep Codex's own sandbox with the same rules.
|
|
15
|
+
- **Checks that start Chrome don't run sandboxed on macOS yet.** The sandbox
|
|
16
|
+
runtime has no setting for the service Chromium registers at startup, so a
|
|
17
|
+
Playwright or browser check fails inside it. Until a follow-up adds that one
|
|
18
|
+
rule, set such a project's check access to **Not sandboxed** in
|
|
19
|
+
Settings → Project → Check access.
|
|
20
|
+
- **Your home folder is readable to new scopes, as in the sandbox runtime.**
|
|
21
|
+
A project hides what it wants back with Settings → Project → **Home
|
|
22
|
+
folder**, or `toolroll project sandbox --repo <p> --read-deny "~/.ssh"`.
|
|
23
|
+
Each approved scope seals its list. Scopes approved before 0.9.70 keep
|
|
24
|
+
hiding your credentials and agent settings, as they were approved.
|
|
25
|
+
- **The macOS services a project's builds may also reach** (`--mach`, for Go
|
|
26
|
+
tools that verify TLS through trustd) seal only on a person's password.
|
|
27
|
+
Approval cards and the CLI's scope view say what a scope reads and which
|
|
28
|
+
services it adds.
|
|
29
|
+
- **Checks can use your Claude, Codex and GitHub sign-ins again.** The
|
|
30
|
+
"Sandboxed, with model and GitHub sign-ins" level is on: a check reads
|
|
31
|
+
exactly those files, by name, inside its sandbox, never through a link.
|
|
32
|
+
- Flows, tool servers started outside a build and interactive Codex coding
|
|
33
|
+
sessions now run in the same sandbox as builds. A machine that can't
|
|
34
|
+
sandbox runs none of them unless an instance operator allowed unconfined
|
|
35
|
+
agents.
|
|
36
|
+
- On Linux the sandbox needs bubblewrap, socat and ripgrep; without them
|
|
37
|
+
nothing starts (as before, unless unconfined agents are allowed).
|
|
38
|
+
- A result that adds or changes a Git hook or config, editor or Claude
|
|
39
|
+
settings, a shell profile, `.envrc`, `.npmrc` or `.mcp.json` is refused at
|
|
40
|
+
the handoff, with its work kept. Toolroll's own install and the sandbox
|
|
41
|
+
runtime can't be written from inside a sandbox.
|
|
42
|
+
- The scout's browser runs outside the sandbox, as an explicit exception: a
|
|
43
|
+
pinned Playwright release installed from a lockfile into Toolroll's own
|
|
44
|
+
folder, with exactly the arguments and environment it was approved with.
|
|
45
|
+
- Limits that come with the sandbox runtime: rules are fixed when a launch
|
|
46
|
+
starts, concurrent builds share its temp folder, and on macOS a build can
|
|
47
|
+
read another process's arguments and environment. Write unlocks for
|
|
48
|
+
protected files are gone (the runtime can't grant them).
|
|
49
|
+
|
|
50
|
+
## 0.9.69 — 2026-10-23
|
|
51
|
+
|
|
52
|
+
- **Choose how far a project's checks reach.** Settings → Project has a new
|
|
53
|
+
Check access section, and `toolroll project check-access --repo <p>` shows
|
|
54
|
+
or sets the same thing. **Sandboxed** (the default) runs a project's setup
|
|
55
|
+
and check commands in the plane's fence, as before. **Not sandboxed** runs
|
|
56
|
+
them with your full access, as before 0.9.66: for a project whose checks
|
|
57
|
+
must create sandboxes themselves, such as Toolroll's own.
|
|
58
|
+
- Widening takes an instance operator with their password typed now (a
|
|
59
|
+
remembered sign-in or a single sign-on recheck doesn't count). Any approver
|
|
60
|
+
for the project can narrow it back. Each change is in the ledger.
|
|
61
|
+
- The level is part of what you approve. A scope seals it, the approval card
|
|
62
|
+
says "Checks: not sandboxed", and tasks already approved keep the level
|
|
63
|
+
they were approved with. Each run records the level its checks actually ran
|
|
64
|
+
at, and the result page says when they ran without a sandbox. Later checks
|
|
65
|
+
of a saved result never run wider than that run did. A mode, a grant or a
|
|
66
|
+
chat confirmation can't approve a wider level; only a person's password can.
|
|
67
|
+
- **A third level, "Sandboxed, with model and GitHub sign-ins", is built but
|
|
68
|
+
off.** It would let a check read your Claude, Codex and GitHub sign-in files.
|
|
69
|
+
Reading those files safely needs a way to open a file that a running build
|
|
70
|
+
can't redirect partway (Node can't do that yet), so the option is shown
|
|
71
|
+
disabled with the reason. Choosing it is refused, and a level saved before
|
|
72
|
+
runs sandboxed and says so.
|
|
73
|
+
- **The fence holds your home configuration by name.** `~/.codex`,
|
|
74
|
+
`~/.config/gh`, `~/.ssh` and the rest are denied whether or not they exist
|
|
75
|
+
when a build starts, so a build can't create one or swap it for a link
|
|
76
|
+
later. The folders above them can't be renamed or made during a build.
|
|
77
|
+
- A check's temporary folder on a Mac is now short (under `/private/tmp`), so
|
|
78
|
+
tests that open a unix socket there work inside the fence. Other launches'
|
|
79
|
+
folders stay out of reach.
|
|
80
|
+
- With no OS sandbox on the machine and unconfined running allowed, the
|
|
81
|
+
settings page and `project check-access` say checks run without a sandbox,
|
|
82
|
+
whatever level is saved.
|
|
83
|
+
|
|
3
84
|
## 0.9.68 — 2026-10-23
|
|
4
85
|
|
|
5
86
|
- **The lead chat is redesigned.** Your messages sit in grey bubbles and the
|
package/README.md
CHANGED
|
@@ -565,24 +565,47 @@ Use Full access only for repositories and setup commands you trust.
|
|
|
565
565
|
|
|
566
566
|
### What an agent can reach
|
|
567
567
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
568
|
+
#### Sandboxing: built on Anthropic's sandbox-runtime (v0.0.79)
|
|
569
|
+
|
|
570
|
+
Every fenced launch (a build's agent, its setup and check commands, a saved
|
|
571
|
+
result's checks, a flow's setup and script, a project tool's server) runs inside
|
|
572
|
+
[Anthropic's sandbox-runtime](https://github.com/anthropics/sandbox-runtime)
|
|
573
|
+
(`@anthropic-ai/sandbox-runtime`, pinned to 0.0.79): the same sandbox Claude Code
|
|
574
|
+
uses. Its own code writes the macOS Seatbelt profile and the Linux bubblewrap and
|
|
575
|
+
seccomp command for each launch, with its baseline, its protected names and its
|
|
576
|
+
limits; Toolroll only hands it a short config. `toolroll doctor` and Settings →
|
|
577
|
+
Project → **Home folder** name the engine and its version. Where it can't run
|
|
578
|
+
(Windows; Linux without bubblewrap, socat or ripgrep, or with unprivileged user
|
|
579
|
+
namespaces off), nothing starts unless an instance operator allows unconfined
|
|
580
|
+
agents (`toolroll config unconfined-agents allow`). Codex builds use Codex's own
|
|
581
|
+
sandbox, given the same rules.
|
|
582
|
+
|
|
583
|
+
What Toolroll adds to the runtime's config, and nothing else:
|
|
584
|
+
|
|
585
|
+
- **Toolroll's own folders** are denied to read and write: the state folder
|
|
586
|
+
beside the database (your remembered login `up-login.txt`, runner and
|
|
587
|
+
coordinator tokens, chat bot tokens, the database and its backups, other runs'
|
|
588
|
+
evidence) except the checkout pool, and `~/.toolroll` (stored provider keys and
|
|
589
|
+
project tool secrets); other launches' private folders are unread.
|
|
590
|
+
- **The build's own checkout** is writable; **other tasks' checkouts** are read,
|
|
591
|
+
never written (those there when it starts).
|
|
592
|
+
- **Your home folder** is readable, as in the runtime (a scope approved before
|
|
593
|
+
0.9.70 keeps hiding your credentials and agent settings, as it was approved). A
|
|
594
|
+
project hides more with Settings → Project → **Home folder**, or `toolroll
|
|
595
|
+
project sandbox --repo <p> --read-deny "~/.ssh,~/.aws"`; each approved scope
|
|
596
|
+
seals that list.
|
|
597
|
+
- **The network**: open, or only through the run's own proxy (below).
|
|
598
|
+
|
|
599
|
+
The runtime's limits are accepted as they are: rules are fixed when a launch
|
|
600
|
+
starts; on Linux a deny for a path that isn't there yet is the runtime's to handle;
|
|
601
|
+
concurrent builds share the system temp folder; another process's arguments and
|
|
602
|
+
environment can be read on macOS; and a folder or checkout made after a build
|
|
603
|
+
starts is writable to it unless something above denies it.
|
|
581
604
|
|
|
582
605
|
| | Auto | Full access |
|
|
583
606
|
|---|---|---|
|
|
584
|
-
| **
|
|
585
|
-
| **
|
|
607
|
+
| **Claude, Gemini, ACP agents** | Anthropic's sandbox-runtime (Seatbelt on macOS; bubblewrap and its seccomp filter on Linux, which also needs socat and ripgrep): reads your home folder except the folders the project lists, never Toolroll's folders; other tasks' work read, never written; writes only the checkout, its Git data, the project's caches, the run's temp folder and the agent's own session files (anywhere outside the fence, under a project set to write anywhere), never the runtime's protected names in the checkout (Git hooks and config, shell profiles, `.mcp.json`, editor settings); no Apple Events. Neither (Windows, or a Linux missing one of those): the build doesn't start, unless an instance operator allows unconfined agents | same |
|
|
608
|
+
| **Codex / OpenRouter** (any OS) | Codex's own sandbox, given the same fence: reads your home folder except the folders the project lists, never Toolroll's folders; other tasks' work read, never written; writes only the checkout, its Git data and the project's caches (anywhere outside the fence, under a project set to write anywhere); no network | the same reads and writes, with the fence; its commands reach the network only when the project's network is open |
|
|
586
609
|
|
|
587
610
|
**Where a build writes.** Each approved scope also seals where its build may
|
|
588
611
|
write. By default (Settings → Project → **Where builds write**, or `toolroll
|
|
@@ -618,8 +641,9 @@ mode** an agent's own provider key is in its environment, so its shell can
|
|
|
618
641
|
read it (subscription mode, the default, puts no key there); and a project's
|
|
619
642
|
own tool secrets reach that project's tools. They stay in a per-run broker
|
|
620
643
|
outside the fence: the agent gets only its address and a token that stops
|
|
621
|
-
working when the run ends
|
|
622
|
-
|
|
644
|
+
working when the run ends. A tool server's own environment (its secrets among it)
|
|
645
|
+
is readable to a build on macOS through the process list, a limit of the sandbox
|
|
646
|
+
runtime's baseline. Reviews and the lead chat run with no tools and are
|
|
623
647
|
confined to their own files. See [SECURITY.md](SECURITY.md).
|
|
624
648
|
|
|
625
649
|
### How build sessions are protected
|
|
@@ -1198,6 +1222,8 @@ about seven minutes, and writes `report.md`, screenshots and both logs to
|
|
|
1198
1222
|
|
|
1199
1223
|
## Credits
|
|
1200
1224
|
|
|
1225
|
+
Every sandbox Toolroll runs is [Anthropic's sandbox-runtime](https://github.com/anthropics/sandbox-runtime) (Apache-2.0), used as a dependency; see THIRD_PARTY_NOTICES.md.
|
|
1226
|
+
|
|
1201
1227
|
The workflow this formalizes comes from [Jason Ku's agentic engineering session](https://youtu.be/Ukju3maxbEQ) and his [`agents-md-snippets`](https://github.com/jasonku09/agents-md-snippets), plus Kun Chen's `treehouse`, `no-mistakes`, `gnhf`, `tasks-axi`, and `axi`. The design was reviewed adversarially by Codex; the appendix in `docs/DESIGN.md` lists every claim that review falsified, because the corrections are more useful than a clean spec would have been.
|
|
1202
1228
|
|
|
1203
1229
|
## License
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -487,4 +487,24 @@ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
487
487
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
488
488
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
489
489
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
490
|
-
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
490
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
491
|
+
|
|
492
|
+
## Anthropic sandbox-runtime — Apache-2.0
|
|
493
|
+
|
|
494
|
+
Copyright Anthropic PBC. https://github.com/anthropics/sandbox-runtime
|
|
495
|
+
|
|
496
|
+
`@anthropic-ai/sandbox-runtime` 0.0.79 is a dependency, installed whole from npm with its own `LICENSE` (and its
|
|
497
|
+
prebuilt Linux seccomp helpers, `vendor/seccomp/<arch>/apply-seccomp`). Toolroll calls it for every sandbox it runs
|
|
498
|
+
(`src/sandbox-runtime.ts`, `src/sandbox-launch.ts`) and copies or modifies none of its code.
|
|
499
|
+
|
|
500
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
501
|
+
you may not use this file except in compliance with the License.
|
|
502
|
+
You may obtain a copy of the License at
|
|
503
|
+
|
|
504
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
505
|
+
|
|
506
|
+
Unless required by applicable law or agreed to in writing, software
|
|
507
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
508
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
509
|
+
See the License for the specific language governing permissions and
|
|
510
|
+
limitations under the License.
|
package/dist/agent-fence.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LoopbackMode } from "./egress.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import type { ExecutionProfile } from "./scope.js";
|
|
3
|
+
import { type SrtTerms } from "./sandbox-runtime.js";
|
|
4
4
|
/** How a launch was fenced, as recorded on the run and said on the task. */
|
|
5
5
|
export type FenceMethod = "codex-profile" | "macos-sandbox" | "linux-bubblewrap" | "claude-rules" | "none";
|
|
6
6
|
/** Only the plane's recorded repository may supply an own-Git exemption. */
|
|
@@ -38,6 +38,9 @@ export type TaskBoundary = {
|
|
|
38
38
|
fileTools: string[];
|
|
39
39
|
readOnly: string[];
|
|
40
40
|
lookup: string[];
|
|
41
|
+
/** Every home configuration name the launch may not reach, by name, whether or not it exists now (macOS: Seatbelt
|
|
42
|
+
* denies a path that isn't there yet, so a build can't create it, or later replace it with a link). */
|
|
43
|
+
homeNames?: string[];
|
|
41
44
|
};
|
|
42
45
|
/**
|
|
43
46
|
* Everything outside a task's own folders that a build could otherwise
|
|
@@ -52,26 +55,30 @@ export declare function taskBoundary(options: {
|
|
|
52
55
|
provider: string;
|
|
53
56
|
home?: string;
|
|
54
57
|
binding?: WorktreeBinding | null;
|
|
58
|
+
policy?: FencePolicy;
|
|
55
59
|
}): TaskBoundary;
|
|
56
|
-
/** Where launches keep their private folders (the system temp folder)
|
|
60
|
+
/** Where launches keep their private folders: `root` (the system temp folder) and, on a Mac, `also` the short shared one
|
|
61
|
+
* a check's launch folder is made in (/private/tmp, so its unix sockets fit); `own` is this launch's. */
|
|
57
62
|
export type RunFolders = {
|
|
58
63
|
root: string;
|
|
59
64
|
own: string | null;
|
|
65
|
+
also?: string[];
|
|
60
66
|
};
|
|
61
|
-
/** The prefixes of every launch's private folders: its tools' config
|
|
62
|
-
* network bridge (egress-bridge.ts). */
|
|
67
|
+
/** The prefixes of every launch's private folders: its tools' config and a Codex build's own home (and a check's). */
|
|
63
68
|
export declare const RUN_FOLDER_PREFIXES: readonly ["so-tools-", "so-codex-", "so-net-", "so-cmd-"];
|
|
64
69
|
export declare function runFolders(own: string | null, root?: string): RunFolders;
|
|
70
|
+
/** Where a check's launch folder is made on a Mac: a short shared folder, so its TMPDIR leaves room for a unix socket
|
|
71
|
+
* path (about 104 bytes). */
|
|
72
|
+
export declare const CHECK_LAUNCH_ROOT_MACOS = "/private/tmp";
|
|
73
|
+
/** The folder a check's launch folder is made in: on a Mac the short one, elsewhere the system temp folder. */
|
|
74
|
+
export declare const checkLaunchRoot: () => string;
|
|
75
|
+
/** Every root launch folders are made under (the system temp folder, and on a Mac the short shared one). */
|
|
76
|
+
export declare const runRoots: (folders: RunFolders) => string[];
|
|
65
77
|
/**
|
|
66
|
-
* What a fence gives back beside its denied paths.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* listed or read (the deps root and the copy linked, other tasks' checkout
|
|
71
|
-
* roots); `runFolders` denies every launch folder in the temp folder but
|
|
72
|
-
* this launch's own (macOS: by pattern, so a later one is denied too);
|
|
73
|
-
* `pool` denies the plane's checkout pools whole and gives this build's own
|
|
74
|
-
* checkout back (CheckoutPool).
|
|
78
|
+
* What a fence gives back beside its denied paths. `readOnly` is read, never written (other tasks' git metadata, the
|
|
79
|
+
* primary checkout under v2); `readable` is read inside a denied folder (the shared dependencies this checkout links);
|
|
80
|
+
* `lookup` is kept for Codex (looked up, never listed); `runFolders` names this launch's own private folder and where
|
|
81
|
+
* the others are; `pool` denies the plane's checkout pools around this build's own checkout (CheckoutPool).
|
|
75
82
|
*/
|
|
76
83
|
export type FenceGrants = {
|
|
77
84
|
readOnly: string[];
|
|
@@ -81,6 +88,14 @@ export type FenceGrants = {
|
|
|
81
88
|
pool?: CheckoutPool;
|
|
82
89
|
network?: NetworkFence;
|
|
83
90
|
writes?: WriteAllowlist;
|
|
91
|
+
/** Policy v1: the home configuration names denied by name, present or not (TaskBoundary.homeNames). */
|
|
92
|
+
homeNames?: string[];
|
|
93
|
+
/** macOS: the project's sealed mach services beyond the runtime's baseline (its `allowMachLookup`). */
|
|
94
|
+
machLookup?: string[];
|
|
95
|
+
/** A `sign-ins` check's exact sign-in files, read again inside the home denies (check-access.ts, signInGrants). */
|
|
96
|
+
signIns?: SignInGrants;
|
|
97
|
+
/** Never written, wherever the launch writes (homeWriteDenies): what runs for the operator next or signs them in. */
|
|
98
|
+
writeDenies?: string[];
|
|
84
99
|
};
|
|
85
100
|
export { isWritesMode, WRITES_DEFAULT, type WritesMode } from "./writes-mode.js";
|
|
86
101
|
/**
|
|
@@ -136,13 +151,10 @@ export declare function buildCacheFolder(repo: string, home?: string, platform?:
|
|
|
136
151
|
/** The environment that points a build's package managers at its project's cache folder, not the user's own. */
|
|
137
152
|
export declare function buildCacheEnv(folder: string): Record<string, string>;
|
|
138
153
|
/**
|
|
139
|
-
* A launch's network confinement (egress.ts
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* `loopback: "any"
|
|
143
|
-
* under `socketRoots` (the run's own temp folder and checkout). On Linux the agent gets a network of its own and
|
|
144
|
-
* reaches the proxy and broker through `bridge` (a folder of unix sockets the plane serves, and the forwarder that
|
|
145
|
-
* carries them onto the namespace's own loopback).
|
|
154
|
+
* A launch's network confinement (egress.ts). `open`: the network as it always was. `allowlist`: nothing leaves the
|
|
155
|
+
* machine but through the run's own proxy (`proxyPort`, which admits only this run's `token`); the run's tool broker
|
|
156
|
+
* (`brokerPorts`, at most one) rides the sandbox runtime's second relay; this machine's other local services are
|
|
157
|
+
* reachable only under `loopback: "any"`; unix sockets only under `socketRoots` (macOS).
|
|
146
158
|
*/
|
|
147
159
|
export type NetworkFence = {
|
|
148
160
|
mode: "open";
|
|
@@ -152,21 +164,8 @@ export type NetworkFence = {
|
|
|
152
164
|
brokerPorts: number[];
|
|
153
165
|
loopback: LoopbackMode;
|
|
154
166
|
socketRoots: string[];
|
|
155
|
-
|
|
156
|
-
folder: string;
|
|
157
|
-
forwarder: string;
|
|
158
|
-
node: string;
|
|
159
|
-
};
|
|
160
|
-
/** Linux: the run's own folders a private /tmp gives back (its checkout, its launch folder), when they are there. */
|
|
161
|
-
keep?: string[];
|
|
167
|
+
token?: string;
|
|
162
168
|
};
|
|
163
|
-
/**
|
|
164
|
-
* Services a sandboxed process could ask to act outside the sandbox for it (macOS): open a URL in a browser (Launch
|
|
165
|
-
* Services), script another application (Apple Events), fetch in the background (nsurlsessiond), look a name up
|
|
166
|
-
* (the DNS service: a lookup is itself a message out), or read and write the clipboard. Denied when the network is
|
|
167
|
-
* confined; the run's proxy resolves names for it.
|
|
168
|
-
*/
|
|
169
|
-
export declare const DELEGATION_SERVICES: readonly string[];
|
|
170
169
|
export declare const NO_GRANTS: FenceGrants;
|
|
171
170
|
/**
|
|
172
171
|
* The plane's checkout pools, denied whole: every project's worktrees, and any made there after launch. `agentFence`
|
|
@@ -178,6 +177,11 @@ export type CheckoutPool = {
|
|
|
178
177
|
own: string | null;
|
|
179
178
|
lookup: string[];
|
|
180
179
|
gitPaths?: string[];
|
|
180
|
+
/** Policy v2 (owner Q-C): other tasks' checkouts are read, never written (agents can work together); v1 denies both. */
|
|
181
|
+
readable?: true;
|
|
182
|
+
/** Folders inside the pool that stay denied whole whatever the policy (flows' scratch folders hold their inputs and
|
|
183
|
+
* outputs), this launch's own folder given back. */
|
|
184
|
+
sealed?: string[];
|
|
181
185
|
};
|
|
182
186
|
/**
|
|
183
187
|
* The pool deny for one launch. `roots` come from the plane's own records (the pool beside the database, and the one
|
|
@@ -224,6 +228,13 @@ export declare function fencedGitEnv(base?: Readonly<Record<string, string | und
|
|
|
224
228
|
* `codexPool`, which denies them around the build's own checkout, written back.
|
|
225
229
|
*/
|
|
226
230
|
export declare function codexFenceArgv(fence: readonly string[], fullAccess: boolean, grants?: FenceGrants): string[];
|
|
231
|
+
/**
|
|
232
|
+
* The same permission profile as codexFenceArgv, as the config overrides a Codex app-server thread takes
|
|
233
|
+
* (`thread/start` and `thread/resume` `config`; checked against codex-cli 0.156: the thread reports
|
|
234
|
+
* `activePermissionProfile` standing-orders, and its commands are refused the denied paths). Approval policy is the
|
|
235
|
+
* caller's own (an interactive session asks).
|
|
236
|
+
*/
|
|
237
|
+
export declare function codexFenceConfig(fence: readonly string[], fullAccess: boolean, grants?: FenceGrants): Record<string, unknown>;
|
|
227
238
|
/**
|
|
228
239
|
* Codex cannot deny an ancestor and still traverse it to the own checkout. Deny existing
|
|
229
240
|
* siblings individually, from the trusted pool roots and a fresh listing, never through
|
|
@@ -256,39 +267,6 @@ export declare function flattened(fence: readonly string[], grants?: FenceGrants
|
|
|
256
267
|
/** Claude's own file tools refuse the fenced paths in every mode (its shell is fenced by the macOS sandbox), and
|
|
257
268
|
* never edit a read-only one. */
|
|
258
269
|
export declare function claudeFenceSettings(fence: readonly string[], readOnly?: readonly string[]): string;
|
|
259
|
-
/**
|
|
260
|
-
* The folders the user owns above each path (never `/`, nor the path itself), and each `roots` folder itself when the
|
|
261
|
-
* user owns it. A Seatbelt path rule follows the name, not the folder: renaming one of these would bring a fenced path
|
|
262
|
-
* back under a name no rule mentions. A folder the user doesn't own can't be renamed by a build anyway.
|
|
263
|
-
*/
|
|
264
|
-
export declare function ownedAncestors(paths: readonly string[], roots?: readonly string[], uid?: number | undefined): string[];
|
|
265
|
-
/**
|
|
266
|
-
* A Seatbelt profile that allows everything except reading or writing the
|
|
267
|
-
* fenced paths, less what `grants` gives back (a later Seatbelt rule
|
|
268
|
-
* outranks an earlier one). Whatever the paths, the process can't read
|
|
269
|
-
* another's arguments and environment (`kern.procargs`, which `ps -E` and
|
|
270
|
-
* KERN_PROCARGS2 use) or inspect a process outside its own sandbox:
|
|
271
|
-
* `process-info*` limited by target alone doesn't stop the sysctl. It can't
|
|
272
|
-
* take another process's task port either (`task_for_pid`, what lldb attaches
|
|
273
|
-
* with; a task name port too), so a tool server's or Toolroll's own memory
|
|
274
|
-
* and environment stay out of reach. Last, every folder of the user's above
|
|
275
|
-
* a fenced or read-only path and above the temp folder (and the temp folder
|
|
276
|
-
* itself) can't be renamed or removed, though what is inside them stays
|
|
277
|
-
* writable as before. The checkout pools are denied first, and the build's own
|
|
278
|
-
* checkout given back right after, so every later deny still holds inside it.
|
|
279
|
-
*/
|
|
280
|
-
export declare function macosFenceProfile(fence: readonly string[], grants?: FenceGrants): string;
|
|
281
|
-
/**
|
|
282
|
-
* The network half of the macOS fence. Every outbound connection is denied, then loopback is given back as the launch
|
|
283
|
-
* allows: every local port under `loopback: "any"`, else the run's proxy and broker alone. (Seatbelt admits a port an
|
|
284
|
-
* allow names whatever a deny after it says, so a local port can't be refused once all are admitted: the choice is all
|
|
285
|
-
* or none, plus the run's own.) Filters name the remote end only: a `local ip` filter would admit any outbound
|
|
286
|
-
* connection.
|
|
287
|
-
*/
|
|
288
|
-
export declare function macosNetworkRules(network: NetworkFence | undefined): string[];
|
|
289
|
-
export declare const SANDBOX_EXEC = "/usr/bin/sandbox-exec";
|
|
290
|
-
/** Where macOS's resolver (mDNSResponder) answers: every name lookup on a Mac is a message to this socket. */
|
|
291
|
-
export declare const MACOS_DNS_SOCKET = "/private/var/run/mDNSResponder";
|
|
292
270
|
/**
|
|
293
271
|
* Whether this machine can run an agent inside the macOS fence (checked once). Not from inside another
|
|
294
272
|
* sandbox: a Toolroll a fenced process starts (the end-to-end journeys a flow's check zone runs) can't nest
|
|
@@ -300,23 +278,24 @@ export declare function macosFenceAvailable(platform?: NodeJS.Platform, probe?:
|
|
|
300
278
|
status: number | null;
|
|
301
279
|
stderr: string;
|
|
302
280
|
}): boolean;
|
|
303
|
-
/** The spawn that runs `file args` inside the macOS fence (same process: sandbox-exec replaces itself with the agent). */
|
|
304
|
-
export declare function macosFenced(file: string, args: readonly string[], fence: readonly string[], grants?: FenceGrants): {
|
|
305
|
-
file: string;
|
|
306
|
-
args: string[];
|
|
307
|
-
};
|
|
308
281
|
export declare function linuxFenceAvailable(platform?: NodeJS.Platform, probe?: () => boolean): boolean;
|
|
309
|
-
/**
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
282
|
+
/** Thrown when no sandbox can be made for a launch: the spawn road refuses it. */
|
|
283
|
+
export declare class LinuxFenceUnavailable extends Error {
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* The runtime's config for a launch (sandbox-runtime.ts SrtTerms), from its fence: every fenced path denied to read and
|
|
287
|
+
* write (with the exact paths read again inside them), the pools' other checkouts write-denied (and, under v1, read-
|
|
288
|
+
* denied) around the build's own, the flows' scratch folders sealed, the launch's writes (its allowlist, or anywhere
|
|
289
|
+
* outside the denies), its mach services and its network.
|
|
290
|
+
*/
|
|
291
|
+
export declare function srtTermsOf(fence: readonly string[], grants?: FenceGrants): SrtTerms;
|
|
292
|
+
/** The spawn that runs `file args` from `cwd` inside the sandbox runtime's sandbox (sandbox-runtime.ts srtSpawn), and the
|
|
293
|
+
* environment it needs beside the caller's. */
|
|
294
|
+
export declare function sandboxedSpawn(file: string, args: readonly string[], fence: readonly string[], grants?: FenceGrants, cwd?: string, temp?: string): {
|
|
314
295
|
file: string;
|
|
315
296
|
args: string[];
|
|
297
|
+
env: Record<string, string>;
|
|
316
298
|
};
|
|
317
|
-
/** The host's own sockets a process in a mounted filesystem could still talk to (D-Bus, the display, container
|
|
318
|
-
* engines), wherever they exist here. */
|
|
319
|
-
export declare function linuxHostSockets(uid?: number | undefined, home?: string, tmp?: string, followLinks?: boolean, env?: Readonly<Record<string, string | undefined>>): string[];
|
|
320
299
|
/**
|
|
321
300
|
* What each agent's fence gives it, by permission mode: the one table README's "What an agent can reach" shows
|
|
322
301
|
* (fenceSummaryMarkdown; a test holds them equal).
|
|
@@ -328,3 +307,147 @@ export declare const FENCE_SUMMARY: readonly {
|
|
|
328
307
|
}[];
|
|
329
308
|
/** FENCE_SUMMARY as README's markdown table. */
|
|
330
309
|
export declare function fenceSummaryMarkdown(): string;
|
|
310
|
+
/** What a `sign-ins` check reads and writes beyond its fence (check-access.ts): its exact sign-in files, its own Claude
|
|
311
|
+
* transcript folder (macOS) and Codex's session and log folders. */
|
|
312
|
+
export type SignInGrants = {
|
|
313
|
+
read: string[];
|
|
314
|
+
transcripts: string[];
|
|
315
|
+
writes: string[];
|
|
316
|
+
/** macOS services the sign-ins need: certificate checking (trustd), without which gh (Go) can't verify GitHub's TLS
|
|
317
|
+
* and calls its own token invalid. Measured with gh 2.x on macOS 26. */
|
|
318
|
+
mach: string[];
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* A `sign-ins` check's grants (sandbox parity C10, srt-style): each sign-in file by its name, read by the check itself
|
|
322
|
+
* through its sandbox. Nothing privileged reads or copies one: the sandbox resolves the name in its own view, so a link
|
|
323
|
+
* planted there into Toolroll's state leads to a denied path (Seatbelt checks the resolved file; bubblewrap resolves
|
|
324
|
+
* inside its mount namespace, where the state is masked). On a Mac, the checkout's own Claude transcript folder (made
|
|
325
|
+
* when absent, only as the real folder it is), whose session files alone are written; and Codex's session and log
|
|
326
|
+
* folders (when ~/.codex is there), never its config.
|
|
327
|
+
*/
|
|
328
|
+
export declare function signInGrants(home: string, checkout: string, platform?: NodeJS.Platform): SignInGrants;
|
|
329
|
+
/** Whether a sign-in file is itself, now: every folder from the (real) home folder down a real folder, the file a
|
|
330
|
+
* regular file with one name (never a link, never a hard link to something else). Checked when granted and again right
|
|
331
|
+
* before the launch (invoke.ts); the sandbox resolves the name a third time. */
|
|
332
|
+
export declare function signInIsItself(base: string, path: string): boolean;
|
|
333
|
+
/**
|
|
334
|
+
* Which fence policy a launch runs under (docs/plans/sandbox-parity.md §1.5). v1: the fence as approvals sealed before
|
|
335
|
+
* 0.9.70 knew it (the home configuration, the repositories beside the project and its primary checkout denied to read).
|
|
336
|
+
* v2: those reads open, as in Anthropic's sandbox runtime, less what the project's sealed `readDeny` names. A widening,
|
|
337
|
+
* so only a scope sealed at v2 runs under it; every narrowing applies to both.
|
|
338
|
+
*/
|
|
339
|
+
export type FencePolicy = 1 | 2;
|
|
340
|
+
export declare const FENCE_POLICY_CURRENT: FencePolicy;
|
|
341
|
+
/** Who a launch is (docs/plans/sandbox-parity.md §3): a build's agent, its setup and check commands, a saved result's
|
|
342
|
+
* checks, a flow's setup, script or trigger, or a tool server. Each gets its fence from `fenceFor`, and only from it. */
|
|
343
|
+
export type FenceKind = "build" | "command" | "check" | "flow" | "tool-server";
|
|
344
|
+
/**
|
|
345
|
+
* What `fenceFor` needs. The boundary and the trusted binding are required, as values or an explicit null (a launch
|
|
346
|
+
* with no checkout), so no caller forgets one; every path comes from Toolroll's own records, never from the checkout.
|
|
347
|
+
*/
|
|
348
|
+
export type FenceContext = {
|
|
349
|
+
databaseFile: string | null;
|
|
350
|
+
worktree: string | null;
|
|
351
|
+
boundary: TaskBoundary | null;
|
|
352
|
+
binding: WorktreeBinding | null;
|
|
353
|
+
runFolders: RunFolders;
|
|
354
|
+
/** The checkout pools, from the plane's records (CheckoutPool). */
|
|
355
|
+
poolRoots: readonly (string | null)[];
|
|
356
|
+
home?: string;
|
|
357
|
+
/** Denied beside the fence (a provider's live session keys, say). */
|
|
358
|
+
extra?: readonly string[];
|
|
359
|
+
/** The shared dependencies this checkout links, as the plane recorded them. */
|
|
360
|
+
linked?: {
|
|
361
|
+
repo: string;
|
|
362
|
+
setupDigest: string | null;
|
|
363
|
+
} | null;
|
|
364
|
+
/** A workspace launch's write allowlist and what it adds read-only (writeAllowlist); absent: writes anywhere its
|
|
365
|
+
* fence allows. */
|
|
366
|
+
writes?: {
|
|
367
|
+
writes: WriteAllowlist;
|
|
368
|
+
readOnly: string[];
|
|
369
|
+
} | null;
|
|
370
|
+
/** The launch's network, when it has one of its own (a flow's: decided by its caller, never left unsaid). */
|
|
371
|
+
network?: NetworkFence;
|
|
372
|
+
/** What it may not read beside the fence, as absolute paths (project-sandbox.ts readDenyPath): denied whole. */
|
|
373
|
+
readDeny?: readonly string[];
|
|
374
|
+
/** macOS services it may also reach (project-sandbox.ts). */
|
|
375
|
+
machLookup?: readonly string[];
|
|
376
|
+
/** The fence policy it runs under: v2 reads other tasks' checkouts (never writes them). */
|
|
377
|
+
policy?: FencePolicy;
|
|
378
|
+
/** The agent the launch runs, whose own sign-in files stay its to write (PROVIDER_HOME). */
|
|
379
|
+
provider?: string;
|
|
380
|
+
/** Scratch folders (a flow's) that stay denied whole inside the pool, whatever the policy. */
|
|
381
|
+
scratchRoots?: readonly string[];
|
|
382
|
+
};
|
|
383
|
+
/** A launch's sealed sandbox terms (builder.ts launchSandboxOf): its policy version, read denies and mach services. A
|
|
384
|
+
* launch with none sealed is v1 with none of them. */
|
|
385
|
+
export type LaunchSandbox = {
|
|
386
|
+
policy: FencePolicy;
|
|
387
|
+
readDeny: readonly string[];
|
|
388
|
+
machLookup: readonly string[];
|
|
389
|
+
};
|
|
390
|
+
export declare const LEGACY_SANDBOX: LaunchSandbox;
|
|
391
|
+
/** A build's sandbox terms, exactly as its approval sealed them: an approval from before 0.9.70 is policy v1 (the home
|
|
392
|
+
* configuration denied) with nothing listed. */
|
|
393
|
+
export declare function launchSandboxOf(profile: ExecutionProfile): LaunchSandbox;
|
|
394
|
+
/** A launch's whole fence: its denied paths, what it gives back, and that it runs inside the OS fence or not at all.
|
|
395
|
+
* Spread into RunOptions as is. */
|
|
396
|
+
export type LaunchFence = {
|
|
397
|
+
kind: FenceKind;
|
|
398
|
+
fence: string[];
|
|
399
|
+
fenceGrants: FenceGrants;
|
|
400
|
+
confinement: "required";
|
|
401
|
+
};
|
|
402
|
+
/** The one place a launch's fence is made (docs/plans/sandbox-parity.md C1): its denies, its grants, and
|
|
403
|
+
* `confinement: "required"`, so the spawn road refuses it where no OS fence applies rather than running it bare. */
|
|
404
|
+
export declare function fenceFor(kind: FenceKind, ctx: FenceContext): LaunchFence;
|
|
405
|
+
/** The flows' scratch folders inside the checkout pools (`flow-checks` beside the checkouts) and any a caller names,
|
|
406
|
+
* each denied by name whether or not it is there yet, and where it is a link, where it leads too. */
|
|
407
|
+
export declare function sealedScratch(poolRoots: readonly (string | null)[], named: readonly string[]): string[];
|
|
408
|
+
/**
|
|
409
|
+
* Toolroll's own install as no launch may write it: this package's folder (its compiled code, the sandbox launch
|
|
410
|
+
* helper among it), the sandbox runtime's package wherever it was installed, and the Node running them with the
|
|
411
|
+
* folder it lives in (its npm and global packages beside it), as the real folders they are. Never a folder that holds
|
|
412
|
+
* the launch's own checkout or its repository (a build of Toolroll's own source, run from it: that repository's other
|
|
413
|
+
* files are the primary checkout's, write-denied already), the home folder or one above it, and nothing the user
|
|
414
|
+
* doesn't own (root's folders are out of a build's reach already).
|
|
415
|
+
*/
|
|
416
|
+
export declare function toolrollInstall(worktree: string | null, home: string, repository?: string | null): string[];
|
|
417
|
+
/** Names that run code or reconfigure tools for whoever opens the project next: the sandbox runtime's DANGEROUS_FILES
|
|
418
|
+
* (@anthropic-ai/sandbox-runtime sandbox-utils), and the names its mandatory denies can't hold when they are made
|
|
419
|
+
* after a launch starts (`.envrc`, package-manager and Claude settings). */
|
|
420
|
+
export declare const PROTECTED_FILES: readonly string[];
|
|
421
|
+
/** Folders protected whole, wherever they are (the runtime's dangerous directories, with Git's hooks). */
|
|
422
|
+
export declare const PROTECTED_DIRS: readonly string[];
|
|
423
|
+
/**
|
|
424
|
+
* The handoff's backstop (builder.ts commit): the changed paths (relative, from Git) that are protected names at any
|
|
425
|
+
* depth, in any case. The sandbox runtime denies these when a launch starts; one made after it started (a new nested
|
|
426
|
+
* folder's `.envrc`, say) gets through the sandbox, and a result that holds it is refused.
|
|
427
|
+
*/
|
|
428
|
+
export declare function protectedChanges(changed: readonly string[]): string[];
|
|
429
|
+
/**
|
|
430
|
+
* The handoff's backstop for links (builder.ts commit): a staged symbolic link (Git mode 120000) whose path is a
|
|
431
|
+
* protected name or a folder on the way to one (`pkg/.claude`, `.vscode`, `lib/.git`), at any depth, in any case. A
|
|
432
|
+
* link there would lead whoever opens the project to settings, hooks or commands kept anywhere.
|
|
433
|
+
*/
|
|
434
|
+
export declare function protectedLinks(links: readonly string[]): string[];
|
|
435
|
+
/** The home folder's own persistence points beyond the protected names (login items, systemd user units and autostart,
|
|
436
|
+
* Git's, gh's, pip's and fish's configuration, the shells' other startup files, SSH and GnuPG, and the folders on a
|
|
437
|
+
* login PATH). Under `.config/` each also stands under $XDG_CONFIG_HOME. */
|
|
438
|
+
export declare const HOME_PERSISTENCE: readonly string[];
|
|
439
|
+
/**
|
|
440
|
+
* What no launch writes, whatever its writes, as static write denies for the sandbox runtime (whose own protected names
|
|
441
|
+
* are anchored at the launch's folder only) and Codex's profile: the home folder's protected names (shell profiles, Git
|
|
442
|
+
* and tool configuration), its persistence points and sign-ins (but the launch's own agent's exact sign-in files), and
|
|
443
|
+
* the bound repository's own Git config, hooks and submodules (a linked worktree reaches them through its `.git`
|
|
444
|
+
* file). Each by its name and by where it leads now (a linked ~/.config or LaunchAgents is denied at both ends). On
|
|
445
|
+
* Linux only those there at launch: the runtime holds an absent name with a stand-in nothing can read for the launch's
|
|
446
|
+
* length, and the person's own git and shells then fail on their ~/.gitconfig and ~/.bashrc (measured). Under a write
|
|
447
|
+
* allowlist an absent one is out of reach anyway; under `anywhere` it can be made (an accepted limit).
|
|
448
|
+
*/
|
|
449
|
+
export declare function homeWriteDenies(home: string, binding: WorktreeBinding | null, provider: string | null, platform?: NodeJS.Platform, env?: Readonly<Record<string, string | undefined>>): string[];
|
|
450
|
+
/** A tool server's grants from its build's: never the agent's network or write allowlist (a server keeps its own state
|
|
451
|
+
* where it always has), and never the build's own checkout or launch folder read back (nothing a build wrote loads in a
|
|
452
|
+
* process that holds a tool's secrets). */
|
|
453
|
+
export declare function serverGrantsOf(grants: FenceGrants): FenceGrants;
|