Repository navigation
review: stop during a capture, reader leak, tenant cap, egress audit, silkd hang-ups, tests that fail - #444
Merged
Merged
Conversation
Fourteen tests passed with the production check they cover removed: the egress wake guards, the NIC lock error, the untar path check, symlink skipping at the source, the plain-publish digest removal, the device probe bound, the frame cap, the zero-ttl renew default, the single teardown, the kept archive intent, the warm claim counter, the closeBoxes wait, sparse holes on Linux, and a mid-handshake SOCKS close that a deadline satisfied. The cell-isolation check now runs after the cell has settled, and a fake detach error real cocoon never returns is gone.
… leak check reads its whole stream
A stop that lands during a hibernate closed cocoon's stderr pipe. cocoon died on its next log line, after the snapshot persisted and before the VMM stopped, leaving the guest paused while its record read running; with the pipe kept, the next start's reconcile raced the capture and cold-booted the guest. On test2 both shapes reproduced, and with this change the stop waited for the capture and the restarted node woke the claim with its memory.
A client that stopped reading and then closed left the router blocked writing to the reader, which never got closed, so the reader VM, the goroutine and shutdown's relay wait were held forever.
#440 dropped the per-name check, so a tenant write anywhere in the cell during a cold resolve left the tenant uncached, and Peek then skipped its max_claims.
…reports no transfer The guard's refusal was audited as allow and skipped the refusal limiter, and the forward path wrote a 0/0 egress_bytes event for every request whose dial failed, unthrottled.
…e claim The S3 delete ran under the transition lock, so every exec and dial to a replicated claim waited on it once per interval.
An even split gave eight owners 225 s each, so a large heal failed on every owner though the first ones refused at once.
A power loss after the heal left a durable meta over zeroed data, and that local copy then shadowed the good owner.
…plica rows after a wake
…s concurrent checkpoints
…equest The feeder dropped it silently, so a client on a reused connection waited for a reply that never came; a fresh connection already answered bad_request.
Attaching to a quiet detached process and dropping the connection held the task, its vsock fd, its buffer and a broadcast receiver until the process wrote or exited.
run joined the stdin pump after the exit frame, so a large stdin outlived the timeout and a guest that stopped reading hung the call; it now aborts the connection and closes it instead of parking it, as the Go SDK does.
run keeps _lease and only aborts a pump still sending; the refused-dial egress tests share one table; the heal test reuses sleepyPuller; the engine tests share waitFile; silkd names the feeder's parse result.
CMGS
added a commit
that referenced
this pull request
Nov 4, 2026
…tart Since #444 a cocoon command asked for after Close answers an error. A target stopping between its commit and its vm start read that as a guest that cannot start, and failed a claim whose guest was intact, so its next start cold-booted it without its memory. The journal now keeps the claim and the restart starts the guest.
This was referenced Nov 4, 2026
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.
Whole-repo audit after #441. This round read what earlier rounds had not: every test file for whether it can fail, all eight cross-file flows again, every doc against the code, and the shell scripts, Dockerfiles, CI, Makefile and packaging.
Bugs
Each fix is its own commit with a test that fails on the old code, unless noted.
Paused, its record readrunning, and every exec got 502 untilvm start. With stderr kept alive, the next start's reconcile ran while the capture was still going and later cold-booted the guest, losing its memory. sandboxd now starts no cocoon command once stopping and waits for the running ones; the unit getsTimeoutStopSec=27min(15 minute budget plus the 11 minute grace). Hardware below.max_claimscould be skipped. review: pty stdin wedge, unfence channel, mesh bind, cuts and docs #440 removed the per-name eviction check, so a tenant write anywhere in the cell during a cold resolve left the tenant uncached, andPeekthen answeredUnknown. Restored with its test.allowand skipped the refusal limiter. The forward path wrote a 0/0egress_bytesevent for every request whose dial failed, at request rate.bad_request.attachnever noticed a hang-up. Attaching to a quiet detached process and dropping the connection held the task, its vsock fd, its buffer and a broadcast receiver until the process wrote or exited.runfed all of stdin after the command exited. A large stdin outlived the timeout, and a guest that stopped reading hung the call. It now aborts the connection once the exit frame arrives, as the Go SDK does.Tests that could not fail
Fourteen tests would pass with the production check they cover removed, by reading: the egress wake guards, the NIC lock error, the untar path check, symlink skipping, the plain-publish digest removal, the device probe bound, the frame cap, the zero-ttl renew default, the single teardown, the kept archive intent, the warm claim counter, the closeBoxes wait, sparse holes on Linux, and a SOCKS close that a read deadline satisfied. Ten of these were checked by removing the check: the tightened test now fails (NIC lock, untar path check, symlink skip, digest removal, both egress wake guards, IPv4 preference, probe bound, frame cap, zero-ttl default). The rest were tightened by reading. Separately, the cell-isolation check now runs after the cell has settled, and a fake detach error real cocoon never returns is gone.
Build, CI and docs
make lintandmake testrun cargo with--locked, like CI.make extractno longer deletesdist/sandboxd.credentials-e2e.sh:grep -qunderpipefailcould let a leaked secret pass the check.python.ymlgets a read-only token like the other workflows.live-volumespage dropped a Go composite literal into Liquid; the example also checked a field that is never set on success.digestguard holds per node, that the SOCKS door is settled at claim time, when a replicated claim has no row after a wake, and that a publish holds up to 192 MiB with concurrency capped by the layer above.Kept, with reasons
python-lsp-serverstays unpinned like apt.sbx-*VM, need a maintainer acting outside the contract.Hardware
Bare metal, cocoon v0.7.6, a 4 GiB guest with 3.5 GiB dirtied in
/dev/shm, sandboxd stopped duringvm hibernateand restarted:Paused, exec 502,vm startneededNo orphan snapshot was left in any run on this branch.
Under a real systemd unit, with the capture held and
systemctl stopat 15 s:TimeoutStopSecsuccesstimeout, SIGKILLPaused, exec 502 untilvm startEverything below is main against this branch on the same hosts. Two nodes, cocoon v0.7.6.
exec, in a real guestbad_requestat once*ruleallowdenyegress_bytesevents for those requests, a failed lookup and one real requestexecduring replica rounds, with S3 delete and list slowed by 2 sOn this branch: all five egress scripts pass, on both lanes. Peer heal passes, including an owner that answers the probe and then refuses the transfer. The S3 contract and archive-to-S3 pass against a real endpoint. A lost node's claim is adopted by a peer and wakes from its latest replica with memory and processes intact.
Not exercised on hardware: the heal budget difference itself, which needs a transfer longer than the old 15 minute slice (unit test only), and an actual host loss (the node loss was a clean stop with its VM left running).
Gates
make go-lint: 100 issues.lines (5 modules × linux and darwin).make go-test: 27 packages ok.aslon all five modules, linux and darwin: no findings. The four advisory forwarder findings are kept in the ledger.rust:1.99.1arm64 container and on macOS: fmt and clippy clean. silkd 162 tests passed on Linux and 161 on macOS, 3 ignored. boot/init 17 passed on Linux and 15 on macOS.shellcheckon every tracked script: clean.Size
Production +144 −58 (net +86); tests +318 −63; docs, CI and scripts +39 −28. Comment lines in Go and Rust: +2 −2, so no file gains a comment. The production growth is the eleven fixes above: the engine stop +28 (a mutex, a flag, a WaitGroup and the var block that now holds the stop sentinel), the heal fsync +18, silkd +14, the restored tenant check +10, egress +8, and under +3 for each of the rest.