Governed package installation for kernels, on bounded network egress - #281
Draft
KB (KB-syntheticsciences) wants to merge 14 commits into
Draft
Governed package installation for kernels, on bounded network egress#281KB (KB-syntheticsciences) wants to merge 14 commits into
KB (KB-syntheticsciences) wants to merge 14 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
KB (KB-syntheticsciences)
added a commit
that referenced
this pull request
Aug 12, 2026
CodeQL flagged `source.includes("cran.r-project.org")` as
js/incomplete-url-substring-sanitization, high severity — the only check
blocking #281. A false positive: `source` is this repo's TypeScript, not a
URL, and nothing sanitizes anything.
Fixed by removing the reason to grep for a domain at all. The index is now an
exported constant, so the test asserts it by equality. That is the better
design regardless: one named value decides where R packages come from,
instead of a literal buried inside a generated R script.
Added the check that was actually missing — that the index host is in
Egress.DEFAULT_RULES. Changing REPO without updating the allowlist would make
every R install fail closed against the proxy, and nothing caught that
before.
5 tasks
KB (KB-syntheticsciences)
force-pushed
the
feat/kernel-package-install
branch
from
August 15, 2026 06:18
db92b78 to
3a75207
Compare
…cy sandbox
Squashed from 127 commits on feat/kernel-package-install. The individual
messages are preserved on backup/pre-rebase-20260815-1404; what follows is what
a reader of main needs.
A three-state sandbox network policy — `deny`, `allowlist`, `allow` — and a
`package_install` tool that provisions a managed Python or R environment, asks
the user, and installs into it with egress bounded to the package indexes.
Egress under `allowlist` goes through a host proxy that enforces the host
allowlist and a `Proxy-Authorization` secret. How the sandboxed process reaches
that proxy differs per platform, and each route was measured before it was
written:
bubblewrap a bind-mounted unix socket, with a loopback shim inside the
namespace, bundled so it resolves nothing from disk
seatbelt the proxy's TCP port directly; the secret travels as userinfo
appcontainer a named pipe whose DACL names the container's package SID,
relayed by a host broker into the same proxy
Linux and macOS are complete and proven in CI, including a live `pip install`
under `allowlist`.
Windows is a new backend built for this branch. Proven on a real kernel in CI:
`TokenIsAppContainer=1`, writes inside the workspace succeed, writes outside are
refused, `deny` severs the network, `allow`'s capability SIDs work, the broker
pipe is openable only by its own container, and egress carries real traffic —
pip resolves against pypi.org and follows the index to files.pythonhosted.org.
The install itself is NOT yet green; see "known open" below.
- MIC is evaluated before the DACL. Every AppContainer runs at Low, so a
granted workspace is unwritable until it is labelled Low as well. Several
rounds of shell-quoting fixes were chasing this.
- An ACE on a reparse point is not an ACE on its target. uv keeps a
patch-versioned interpreter directory and a stable name beside it, and
writes the stable name into pyvenv.cfg — so the grant landed, icacls exited
0, and the container still could not start Python.
- Windows can only grant paths the user owns. A machine-wide Python can never
be read by a sandboxed process without elevation, which is out of scope, so
the prerequisite is surfaced in settings and to the agent rather than
failing later as a broken interpreter.
- AppContainer isolation is bidirectional: the host cannot reach a listener
inside the container either, which is what makes the pipe necessary rather
than merely convenient.
- `launch` blocks the event loop on WaitForSingleObject through FFI, so any
relay living in the launcher process stops for the payload's lifetime.
- The Windows install is red: pip downloads, then cannot write into a
pre-created subdirectory of the granted workspace. Under investigation as
MIC label propagation to existing children.
- `tempDirs()` grants the container the shared host temp and labels it Low.
That is too broad and wants narrowing to a per-run directory.
- No readiness wait between the Windows shim and the payload; it currently
survives on pip's retries.
The rebase silently broke Linux egress, which was the one platform that fully
met the merge gate. Three separate causes, all the same shape - main's model
mounts nothing by accident, so anything that used to be visible has to be named.
- `bubblewrapArgs` stopped consuming `Policy.readBind`. main's rewritten body
never had that loop and taking it wholesale dropped it.
- `readBind` was still filtered to /tmp paths. Correct under `--ro-bind / /`,
where only what `--tmpfs /tmp` masked needed rebinding; fatal under an empty
root, where it discarded the shim's launcher, its bundle and the interpreter.
- The shim's LEXICAL path was never bound. Global.Path.bin reaches the data
root through a symlink, so the path interpolated into the shim script is not
the one that resolves on the host. main's mountAliases exists for exactly
this; the shim's paths are added after buildPolicy runs, so they have to be
aliased where they are added.
Nothing reported any of it. The shim is backgrounded to /dev/null, so the only
symptom was every allowlisted request returning "Could not connect to server".
`wrapArgv` also lost its shim composition entirely in the merge, which left
every notebook and R kernel with no egress under "allowlist" - the exact route a
package install from a kernel depends on. Restored, with the same aliasing.
egress-live is green again: a real allowlisted GET, 18MB byte-for-byte through
the proxy, and a real pip install that imports.
…rk default Two regressions this rebase introduced, both caught by main's own tests. `Sandbox.resolved()` still defaulted network to "allowlist" while Config.trustedSandbox now defaults to "deny". Every caller that omitted the value resolved to allowlist here and then threw in bubblewrapArgs for want of an egress socket nobody had asked for. The two defaults have to agree, and failing closed is the right one for a value that decides whether a sandboxed process reaches the network. Worse: dropping readBind's /tmp-only filter left it holding every readable root, and the bind loop runs AFTER the unreadable masks - so a --ro-bind-try of a directory shadowed the /dev/null mount inside it and re-exposed the file the mask had just covered. main's symlink-escape test found it by reading a masked file successfully. main already binds readable roots itself, before the masks, so readBind now carries only what plan()/wrapArgv() add: the shim launcher, its bundle and the interpreter. Those are individual files, so the shadowing has no trigger rather than an unlikely one.
…olver main brought its own `environment` concept — a NAME resolving to `.venv/<name>`, validated by KernelEnvironmentName and resolved in `pythonEnvironment`. This branch had its own — a managed DIRECTORY under the cache root that `package_install` provisions. Taking main's tool schema during the rebase left the managed side unwired: nothing bound a kernel to it, so the merge gate had no executable assertion left. They compose rather than compete. `pythonEnvironment` now consults the managed environment first, because it is the only one this product creates and installs into, then `.venv/<name>`, then the conventional fallbacks. Only the managed one is returned as `environment`, since that is the directory the sandbox has to be granted; a project's own .venv is already inside the workspace. The Instance lookup is guarded: `pythonEnvironment` is reachable with no project bound, and throwing there would turn "no managed environment" into a crash. R lost its environment parameter entirely when the conflict took main's side. Restored, using KernelEnvironmentName rather than the bare string this branch had — it bounds length and refuses path separators, so a name can never address a directory outside the two places the resolver looks. The binding tests now assert main's mechanism. It keys the kernel identity by environmentName, where this branch carried a separate `boundEnvironment`; both give "a different environment gets a different kernel", and main's is the one that is load-bearing now.
… a mutation
Two behaviour bugs the rebase exposed, plus the test updates main's changes
require.
`pyvenv.cfg`'s `home` is `<prefix>/bin` on POSIX, so granting it alone left
`<prefix>/lib` — the whole standard library — unreachable. A
python-build-standalone interpreter then falls back to its baked-in build
prefix and dies before it can say why:
sys.path = ['/install/lib/python312.zip', '/install/lib/python3.12', ...]
Fatal Python error: init_fs_encoding: failed to get the Python codec
ModuleNotFoundError: No module named 'encodings'
Invisible under `--ro-bind / /`, because the tree was there whether or not
anyone asked. `Installer.baseRoots()` now names both, on the install path and
the kernel path, and adds the parent only when the leaf is the POSIX `bin` —
on Windows `home` IS the prefix.
`boundEnvironment` was left null after the merge, so `restartEnvironment`
matched no kernels and a non-additive install kept every bound kernel alive
holding stale imports — while the tool's own output said they had restarted.
The merge gate now states its policy instead of reading the ambient default and
asserting it happened to be allowlist. That assertion broke the moment the
default became main's "deny", and would have passed vacuously if the default had
drifted the other way; the gate is "an install works UNDER allowlist", so the
policy belongs in the test. Its global config write is undone afterwards,
because that write outlives the test process-wide.
The Windows capability probe is restored to selfTest, gated to the AppContainer
backend: seatbelt and bubblewrap now deny every socket in every mode, so
asserting that "allow" reaches the network would demand a capability those
platforms deliberately do not offer.
… the rebase corrupted `network: "allow"` denies every socket on bubblewrap and seatbelt, and nothing told anyone. `sandbox status` printed "allow", the settings panel showed it selected, the schema said "whether sandboxed commands may reach the network" — and every connection failed to resolve with no path from the symptom back to the setting the user had deliberately turned on. The behaviour is right: neither backend can grant outbound access without also exposing everything bound to 127.0.0.1, so both fail closed. A config value that reports one thing and does another is still a bug. Disclosed on all three surfaces, through the one-time `warning` channel the Windows loopback gap already uses: a warning on first use, a note beside the status line, and honest copy in the settings panel and the docs. "Allow" is no longer labelled "unrestricted egress" — it is unrestricted only where the platform can express that, which today is Windows alone. Sandbox.tsx and sandbox.mdx were also rebuilt from main. Taking "ours" for their conflicts had concatenated both sides rather than merging them: the panel was 750 lines against main's 543 and this branch's 365, with duplicated Show blocks and a JSX tree that no longer closed, and the doc carried two copies of its own structure under different headings. Both now start from main's version with this branch's additions ported onto it — the three-state network control, the allowHosts field, and the prerequisite banner, which sits above every control because a green backend beside "installs will fail" reads as a contradiction.
…wallowed
`gh workflow run` refused the file outright:
failed to parse workflow: No steps defined in `steps` and no workflow
called in `uses` for the following jobs: macos-runtime
Resolving ci.yml's conflicts by concatenating both sides left main's
macos-runtime job with a `runs-on` and nothing under it — the `sandbox` job
this branch adds had taken its place. Same shortcut, same failure, third file:
Sandbox.tsx and sandbox.mdx were rebuilt for it in the previous commit.
Checked the rest rather than waiting to be told: every other file resolved that
way is genuinely merged, with no duplicated function or test names. Only these
three took a whole side without being read.
…g else
`readable` means two different things per backend, and the rebase let main's
meaning reach the one where it is dangerous.
On POSIX it is "what may be read" — derived from runtime roots, the workspace
and the writable set — and binding those into a namespace costs nothing. On
Windows every entry has an ACE written to it with `icacls`. The appcontainer
branch overrode it, but conditionally:
...(granted.length ? { readable: granted } : {}),
so whenever nothing explicit was named, main's derived set came through the
spread and the launcher set about rewriting the ACLs of every directory on
PATH:
could not grant sandbox access to C:\Windows\System32: Access is denied.
could not grant sandbox access to C:\Windows: Access is denied.
Unelevated it only failed, slowly - 117 seconds of icacls calls before a
trivial `exit 7` gave up, which is how CI found it. Elevated it would have
SUCCEEDED, and left an AppContainer holding standing access to the system
directories.
Now unconditional, so empty stays empty, with a test that builds a spec through
wrapArgv and asserts the count both ways.
The Windows install "passed" with `egress: <none>` — the test read the ambient policy, which now defaults to disabled/deny, so it ran UNSANDBOXED and proved nothing about the path the job exists to exercise. It states its policy now, the same fix the merge-gate test already got. And the real bug it was hiding is finally measured rather than inferred. A staged check proved it directly: a directory created BEFORE the launch is unwritable inside the container while one created inside it is fine. `icacls /setintegritylevel (OI)(CI)L` labels the granted root and does NOT propagate to subdirectories that already exist, and Mandatory Integrity Control is evaluated ahead of the DACL — so `<env>/.tmp`, which install() pre-created on the host for pip, could never be written to. pip downloaded a wheel and then failed unpacking it, which read as a transport fault for several rounds. So install() no longer creates or names a scratch directory. `spec.env` already carries TMPDIR/TMP/TEMP for the sandbox's own per-spawn temp root, which is granted and labelled as a root rather than inherited as a child. That mechanism came from main in this rebase; using it is both the fix and the reason it exists.
…t resolves to
With the policy finally stated rather than inherited, the Windows install ran
genuinely sandboxed for the first time and failed inside pip:
PermissionError: [WinError 5] Access is denied:
'...\uv\python\cpython-3.12-windows-x86_64-none'
The grant log shows why: only `cpython-3.12.14-windows-x86_64-none` carried an
ACE. uv keeps a patch-versioned interpreter directory with a stable name linked
beside it, and `pyvenv.cfg` - hence `sys.base_prefix` - names the stable one.
main's `dedupe` canonicalises policy paths, so the reparse point was resolved
away before `grant()` saw it, and `grant()`'s own resolve step then had only the
target to work with. The name Python actually uses never got an ACE.
On Linux the lexical spelling is restored by a mount alias, which is how the
same defect was fixed for the egress shim earlier in this rebase. Windows has no
such indirection: a name is reachable only if that exact name carries an ACE. So
the appcontainer branch keeps the caller's spelling and lets `grant()` add the
resolved form beside it.
Both halves are now needed and both are present, which is the whole lesson of
this reparse point: it has produced three separate failures - the trampoline, the
grant, and now the stat - each looking like something else.
…conclusion Two corrections and one diagnostic. The pre-existing-subdirectory check now PASSES, so the conclusion drawn from its earlier failure was wrong: the Low integrity label DOES reach a subdirectory that already existed. What actually broke was the over-broad `readable` list leaking main's derived read roots into the Windows grant list, which sent icacls at every directory on PATH and failed on the system ones. That was fixed in the previous commit; this check went green with it. The note in the test is amended rather than deleted, because the reasoning is still worth reading and being wrong about it once is the point. `Installer.install` now runs with a bounded signal. It buffers stderr and prints it only when it RETURNS, so the last run produced five minutes of silence and not one line about why it hung — the same blindness that already cost this feature a round. And the streamed test no longer pre-creates a TMPDIR. install() stopped doing that a commit ago; the test that reconstructs install() kept doing it, which is exactly how a reconstruction stops proving anything about the thing it stands in for. The sandbox's own per-spawn temp is granted and labelled as a root, and both paths use it now.
…ng past its abort
Two fixes. The second is why the first took so long to find.
Windows installs now go through `uv pip install`. Not a preference — a
workaround for an upstream defect with a name. CPython 3.12.4 changed
`os.mkdir(mode=0o700)` so `tempfile.mkdtemp()` creates a directory whose DACL
does not inherit from its parent; inside an AppContainer access comes from the
package SID, so a DACL naming only the owner grants the process nothing. pip
downloads a wheel and then cannot write into the `pip-unpack-*` directory it
just created:
[Errno 13] Permission denied: '...\pip-unpack-xxxx\six-1.17.0-...whl.metadata'
python/cpython#134587, fixed upstream but unreleased. uv is Rust and never
touches CPython's tempfile.
Windows only, deliberately. Linux and macOS install correctly today and are
verified doing so on every push; changing their installer inside a fix for
someone else's Windows bug would touch working, security-relevant code for no
reason. uv is not optional on Windows anyway - it is how a grantable interpreter
gets there at all. uv's binary joins the read set because an AppContainer can
execute nothing its SID is not granted, and UV_CACHE_DIR points at the same
writable wheel cache rather than %LOCALAPPDATA%.
And `install()` no longer hangs past an abort. `signal` kills the launcher, but
the sandboxed child inherits its stdio and is not a child of this process at all
- on Windows it is created by CreateProcessW inside the container - so it holds
the write end of both pipes open and `reader.read()` never reports done. A 150s
AbortSignal produced no return and the caller sat to its own 300s timeout,
twice, each time discarding the log that would have explained the failure. Now
it waits on the process and gives the drains a bounded moment to flush, keeping
whatever was collected.
Documented what this does NOT fix: agent-authored notebook code calling
tempfile.mkdtemp() still fails inside the Windows sandbox until upstream ships,
and Windows now resolves through uv while the others use pip, so the same
request can land different versions per platform.
The uv path reached the container and failed there:
uv.exe pip install --python ...\env\Scripts\python.exe --only-binary :all: six
error: Failed to inspect Python interpreter from provided path at `Scripts\python.exe`
Caused by: Access is denied. (os error 5)
The grants are right - the environment is writable, the base interpreter is
readable under both the stable and resolved spellings, uv.exe and the shim
binary are readable. So this is more specific than a missing grant, and every
time this feature has been debugged by inference about Windows the inference has
been wrong. uv now runs with `-v` under OPENSCIENCE_SANDBOX_DEBUG, which names
the path and the operation rather than reporting a bare errno.
The streamed test also stopped reconstructing something the product no longer
does. It spawned `python -m pip`, which on Windows now fails on precisely the
upstream CPython defect (python/cpython#134587) that the uv path exists to route
around - a test red for a reason the product deliberately avoids. It runs uv
now, with uv's own cache and interpreter-download policy, matching install().
…reter?
uv's verbose output named the operation, which the bare errno never did:
DEBUG Checking for Python interpreter at path `Scripts\python.exe`
error: Failed to query Python interpreter
Caused by: Access is denied. (os error 5)
"Query" means uv SPAWNS the interpreter, and uv is already inside the container
when it does. Our launcher spawning the same binary succeeds - but those are not
the same claim. CreateProcessW opens the image file in the CALLER's context, and
the launcher runs as the user and creates the process INTO the container, so its
success shows the launcher can open the binary rather than that the container
can execute it.
Which means "the environment's launcher can spawn its base inside the
container", cited as proof for several rounds, proves something weaker than it
reads. This test makes the distinction: a shell running IN the container spawns
the interpreter itself. Red here identifies execute-from-inside as the missing
right and retires the weaker claim; green rules it out and points at something
uv does that a direct spawn does not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Gives the agent a governed way to install packages — and closes the only ungoverned way.
A new
package_installtool installs into named, language-scoped environments behind an approvalcard. Kernels bind to an environment through an
environmentparameter besidekernel, and aninstall restarts them only when the change is not purely additive. Shell installers are refused
before they run.
Supersedes and closes #280. This branch contains every one of #280's 33 commits plus 16 more, so
merging this ships the complete feature — bounded network egress and governed installation — in one
step. #280 is closed rather than merged; its review record is summarised below so nothing is lost.
What a user gets
package_install, one approval cardinstall tqdm → default [pypi.org/simple], with a standinginstall*grantpip installin the agent shellinstall.packagesintoR_LIBS_USERWhy the refusal is part of this PR, not a follow-up
#280 made shell installs work. Measured on that branch, inside the agent's own sandbox, with no
tool and no card:
The workspace is writable and pypi is allowlisted, so read-only system site-packages stops nothing.
Before the proxy this died at DNS and the install contract held by accident; the proxy removed the
accident, not the intent. An approval card that an agent can walk around is decorative, so the
refusal and the tool ship together and are asserted in the same test.
It is a contract boundary, not a security boundary — the same egress can fetch a wheel by hand —
and the code says so rather than claiming more.
How did you verify your code works?
By running it. Six defects were found by tests the plan did not call for, every one invisible to the
suite as planned:
uv venvdoes not install pip. The ladder prefers uv, butinstall()shells out topython -m pip, so on any machine with uv it produced an environment the installer could not use.Every other test passed, because the one asserting "a venv has pip" forces the venv branch. Fixed
with
uv venv --seed.executeis reachable without zod defaults applied,so
languagewas undefined,JSON.stringifydropped the key, andread's parse rejected thefile. The environment existed on disk with packages in it and was invisible to the inventory.
Environment.writenow validates before writing.exists. Production would have worked by luck (
~/.cacheis under--ro-bind / /);--tmpfs /tmpmasks it wherever the cache root lives under /tmp.
wrapArgvdiscardedpolicy.readBindat two call sites, replacing it wholesale with theegress shim's paths. The environment bind was dropped on the floor with no error.
six==1.17.0over an installed 1.16.0 reported "already installed". The skip check comparednames only, so an upgrade became a silent no-op that also reported the change as additive —
leaving the old version in place while telling the agent it had the new one.
token()is undefined on Windows. The plan's reconcile rule would have marked every Windowsinstall
unknownforever and every environment permanently suspect.Also fixed
npm pack --json, which returns an object on npm 12 and an array before it — the testindexed
[0], so it failed on any current npm while passing on CI's older one.bun run typecheckpassesbun test(inbackend/cli) — 2111 pass / 6 skip / 0 failbunx prettier --check .cleanFollow-ups inherited from #280, none blocking
maintoday:--ro-bind / /exposes every host unix socket, so a sandboxedprocess reaches
docker.sockand can obtain host root — in every network mode includingdeny.This work bounds network egress; local IPC to host daemons is a separate and wider hole.
allowlist—sshignoresHTTP_PROXYand needsProxyCommand/SOCKS. This is the one regression a user could notice.egress-shim-dev-*artifacts accumulate inGlobal.Path.bin, never collected.test runs under
bun run, which takes the dev-bundle branch. Verified once by hand against abun run build --singlebuild — a realpip installsucceeded inside--unshare-netwith theboundary intact — but nothing keeps it that way.
test/global/data-dir.test.tsintermittently fails withEBUSYcascading into ENOENT. Observed twice here, green on re-run of the same commit both times.ones.
Merge gate
test/package/install-live.test.tsstates the condition as an assertion, and the CI sandbox job runstest/package/on both legs so it is a fact on each platform rather than a claim about one:allowlistWindows still blocks the gate — nothing is built there, kernels do not run at all, and the suite
skips rather than fails. But it is no longer blocked on an open question.
docs/specs/windows-appcontainer-probe.ps1was run on Windows 11 (10.0.26200), unelevated, withMpsSvc running and all three firewall profiles enabled. It falsified the design's central claim,
in the useful direction. The spec said an AppContainer with no network capability has no loopback
either, so no shim could exist and Windows had to be capability-mediated. Measured: loopback works
inside the container, including across two processes sharing the package SID — 8192 bytes echoed
and content-verified, with a real peer endpoint recorded. The named pipe to a broker carries 64 KiB
each way, content-verified, host end agreeing on the byte count.
So the full chain holds and Windows can be socket-transparent like the other two platforms:
Isolation still holds where it must — outbound denied, host-loopback denied, DNS denied, user profile
unreadable and unwritable — and those denials are trustworthy because the run carried controls: a
default-DACL pipe that was refused, System32 still readable, host DNS working, and a check that the
machine's two pre-existing loopback exemptions belonged to other software.
One consequence for whoever builds it: the named pipe is load-bearing on Windows in a way it is not
elsewhere, because the container cannot reach a host loopback listener at all. And the pipe must be
created with a custom DACL — a default-DACL pipe is refused — which
net.createServercannot express.Spec updated in
docs/specs/windows-sandbox-design.md; the compatibility table listing what Windowswould lose is deleted rather than corrected, since every row followed from the false premise.
What came from #280: bounded network egress
sandbox.networkbecame three-state —deny | allowlist | allow, defaulting toallowlist. In thatmode the sandbox keeps
--unshare-net, which blocks every host including the host's own loopback, anda bind-mounted unix socket is the only route out. A shim inside the namespace bridges loopback TCP to
it; a proxy on the host resolves names, checks the authority against an allowlist, and pipes bytes.
macOS has no namespace, so there the proxy listens on
127.0.0.1:<port>and the seatbelt profilenarrows
network-outboundto that one address, with a per-start secret in the proxy URL because everyprocess on the machine shares one loopback.
Before it,
denylocked kernels out of PyPI, NCBI, UniProt, PDB and EBI — most of what a research toolis for — and
allowwas unrestricted egress. Design recorded indocs/adr/0002-sandbox-network-policy.md.Nine defects were found across fourteen review rounds, every one invisible to a passing suite.
Notable ones:
Socket.write's return value with nodrainhandler, soevery transfer above a few KB lost bytes. 40 MB arrived as 2.6 MB;
pip download numpydied with anSSL error. Now pinned by a live test that pulls an 18 MB wheel through a real sandbox and checks its
sha256.
and
RangeError: Out of memoryin four seconds.unreadableentries, which carrykernelSensitivePaths().shimScriptpolled 150 times and called it 3s; amacOS runner measured 17.1s for the same loop. Now a real
date +%sdeadline.macOS was verified against a real
sandbox-execin CI, answering the three ways the seatbeltprofile could have shipped silently non-functional:
network-bind/network-inboundas emitted aresufficient, the
tcpfilter spelling works, and DNS does not slip past(deny network*)viamDNSResponder.Known gaps, stated rather than hidden
Rscriptis not installed on the development machine, sothe two live R tests skip. Everything else about the R path — the explicit
lib, thewarning-not-exit-code failure check, the message translation — is tested.
for a read-only
/, real repeat cost across a session. Whether to bind a writable per-projectcache is a deliberate open decision.
wait: falsepath has no notification channel; a later call is how the agent learns theoutcome, and the tool's output says so rather than implying otherwise.