Skip to content

feat(sandboxd): reconcile live read-only volume attachments - #339

Merged
CMGS merged 57 commits into
cocoonstack:mainfrom
minatoaquaMK2:codex/live-readonly-volumes
Oct 17, 2026
Merged

CMGS merged 57 commits into
cocoonstack:mainfrom
minatoaquaMK2:codex/live-readonly-volumes

Conversation

@minatoaquaMK2

@minatoaquaMK2 minatoaquaMK2 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

A running sandbox can replace its read-only catalog datasets while keeping its VM, processes, and sessions. GET reads the current view, PUT converges a full desired set, and PATCH adds or removes names. The Go and Python SDK method signatures stay the same and retain the complete failure view.

Volume management uses the tenant API token and ownership check, matching /env. New admissions alone require current tenant and catalog authorization. Held repair and removal remain available after ACL changes. Settled retained mounts make no device calls. Busy cwd, bind mounts, and foreign mounts return 409 with safe guest paths. The response distinguishes an active worker from an interrupted or failed change.

Confirmed and desired sets remain the persistent authority. Release, reap, and startup quarantine transfer their holds into a bounded retirement record before removing a VM. Restart adopts those holds, and cleanup releases them only after confirmed VM removal and a committed retirement deletion. Device calls remain outside Transition. Capture rejects held volumes before trim.

This stays within #339. Live changes cover read-only mounted catalog entries. Existing writable and attach-only entries remain immutable. The runtime floor is Cocoon v0.7.2. Journal format v2 requires draining claims and retirement work before downgrade. Catalog names must keep the same backing image while held. A sandbox whose VMM dies with volume holds remains release-only.

Local validation passed on Linux.

  • All five Go modules passed race tests, Linux and Darwin lint, and format checks. Python SDK and adapter tests passed 247, 10, and 15 cases, plus Ruff and strict mypy.
  • PostgreSQL store and multi-node integration passed without skipped tests. Caddy TLS passed through both SDKs.
  • Official Cocoon v0.7.2 with real Cloud Hypervisor v54 passed same-path replacement, EROFS, identity continuity, busy/retry, concurrent activity, cancellation, daemon restart, durable retirement, and eventual writable backing reuse. Final test resources were empty.

The validation results in the PR discussion contain exact revisions, reproducible commands, raw logs, failed attempts, and cleanup evidence. Missing eject ACK was not established by the pause experiment. Separate hardware crashes at every unmount, detach, or fsync boundary and physical power loss remain untested. The process-restart smoke used sync_claims: false.


Update after maintainer revisions (2026-10-17)

The original description above is retained as history. This section describes the changes from fea3531 through 37253a6 and supersedes the earlier statements where they differ.

  • Journal and recovery. claims.json returns to the flat claim map. The v2 envelope, volume_retirements, and unknownVolumeVMs fence are removed. volumes_want is stored only when it differs from volumes; an absent field means they are equal. In-process teardown still retains holds until VM removal is confirmed. After restart, conflicting attaches rely on the Cloud Hypervisor hotplug image lock. The persistent retirement guarantee described above no longer applies. The maintainer leaves the existing restart gap to separate work.
  • Ordinary claims and runtime versions. Fresh claims use the original mount path. Mountinfo checks apply only to live repair. Cocoon version queries are cached. The ordinary startup floor is v0.6.9; live changes require v0.7.2. Development builds with a non-semver version now pass the live version gate.
  • Live updates. Journal writes run outside Transition. A settled, unchanged target skips journal writes. Failed writes preserve the in-memory state and retry persistence in the background, including after a device change, rather than restoring fields that no longer match the guest.
  • Correctness fixes. Claim rollback takes over the live run's volume holds. Activity is recorded after the running-state check and before device calls. Symlinked mount parents resolve correctly, and mountinfo has a 1 MiB limit. Refusals report their actual cause. Store outages use fixed public messages and retain Retry-After. Claim removal also clears volume diagnostics.
  • Python SDK. Volume inputs use list annotations and reject bare strings at runtime. Non-positive timeouts are rejected. The shared HTTP failure rule remains status >= 400.
  • Tests and maintenance. Live volume smoke tests require -live or VOLUME_LIVE=1. The continuity program and Python fault hooks are removed. Fixtures are shared, ordering tests use synctest, and documentation and code style follow the existing project conventions.

The earlier validation results apply to their recorded revisions, including the retirement implementation that has now been removed. The maintainer reports hardware and e2e results at 9236121. Three later commits update activity handling, Python string validation, and test cleanup, so that report does not establish complete hardware validation of 37253a6. The maintainer's rationale explains the compatibility and recovery tradeoffs.

@CMGS

CMGS commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR and the detailed write-up. Attaching a dataset to a running claim is a use case we want, and most of the pieces are already in place: claim-time volumes are hot-attached today (cocoon vm disk attach into a running warm clone, then a guest mount through silkd), so this mainly adds a live detach and a second entry point into the same path. Reviewed against main 7b35478.

What we want to keep

  • Reusing the existing catalog, tenant ACL, path/count validation, dirty-image check and reader/writer admission.
  • Journaling intent before touching the device, keeping the admission hold on failure, and converging by retry.
  • Moving the capture check inside the Transition critical section (captureAllowed). It also closes a check-then-use gap that exists on main today.
  • Refusing busy or foreign mounts and never using lazy or forced unmount.

What we'd like changed

  • Do not hold Transition across device calls. AttachVolumes/DetachVolumes hold it for the whole engine operation (up to the 30 s bound). Every new relay dial takes the same lock in WakeAgentSocket, so exec, file and port calls to that sandbox stall while a volume changes. We just removed the same kind of stall from checkpoint export (pool: a capture of a running sandbox releases its transition lock once the fork snapshot is saved #323). Take Transition only to record the change and for the capture check, and run the device calls outside it.
  • No new VolumeMu. It adds a lock that Renew, release and reap must now take, and Renew can wait up to 30 s behind a volume call. A release should cancel an in-flight convergence and tear down what is held instead.
  • A declarative desired set instead of two imperative endpoints. We'd prefer the shape of the env API: PUT /v1/sandboxes/{id}/volumes with the full desired set, PATCH to add or remove by name, GET to read it. The claim journals the desired set (VolumesWant) next to the applied one (Volumes), holds cover their union, and capture is refused while either is non-empty. A request converges synchronously; on failure the response reports each volume's state, and a retry is the same PUT. This replaces PendingVolume, its one-operation-at-a-time rule and the partial-batch recovery instructions.
  • Idempotency belongs in cocoon, not in output parsing. attachedVolume reads vm inspect's attached_devices, which cocoon drops silently when the VMM query fails, and DetachVolume matches the disk %q not attached error text. We will make cocoon vm disk attach succeed for an identical name/path/mode and cocoon vm disk detach succeed (or return a distinct exit code) when the disk is absent. With that, the engine side reduces to the existing applyVolume (attach + mount) for adds and umount + vm disk detach for removals.
  • Python SDK parity. New sandbox verbs ship in both SDKs.

Next steps on our side
We will prepare the small cocoon change first (idempotent vm disk attach / vm disk detach) and link it here once it is merged, so this PR can build on it. Once the PR is revised, we will run the hardware checks you listed as unverified on our testbed: real hotplug and unplug on CH, a busy guest unmount, and a daemon restart with a change in flight.

@CMGS

CMGS commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The cocoon side is merged: cocoonstack/cocoon#282 (6d76309). cocoon vm disk attach and cocoon vm disk detach are now idempotent on both Cloud Hypervisor and Firecracker (--pci):

  • Attach, identical disk: attaching a disk that is already attached under the same name, path and mode succeeds and returns the existing id. No second vm.add-disk is sent.
  • Attach, conflicting disk: the same name with a different path or mode is refused, and so is a path or serial that another disk already holds.
  • Detach, absent disk: detaching a name that is not attached succeeds, and cocoon logs a warning. We chose plain success over a distinct exit code.
  • Detach retry on CH: CH drops a disk from its config as soon as vm.remove-device returns, but it frees the slot and the backing file only after the guest acks the eject. If a detach times out waiting for that ack and is retried, the retry waits for the pending eject. It does not report success early. Firecracker removes drives synchronously, so it has no pending state.

For this PR, that means:

  • attachedVolume (the vm inspect attached_devices parse) and the disk %q not attached error match can go.
  • A retry simply calls attach or detach again.
  • The engine side becomes the existing applyVolume for adds, and umount + vm disk detach for removals.

The change is on cocoon master and not yet in a tagged release. We will move sandboxd's cocoon floor when this PR is ready to merge. Until then, testbed runs will use a master build.

As before, once the PR is revised along the lines above, we will run the hardware checks on our testbed: real CH hotplug and unplug, a busy guest unmount, and a daemon restart with a change in flight.

@minatoaquaMK2 minatoaquaMK2 changed the title feat(sandboxd): mount and unmount read-only volumes on running sandboxes feat(sandboxd): reconcile live read-only volume attachments Oct 16, 2026

@CMGS CMGS left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, this is close. the shape is right now: no Transition across device calls, no new lock, a declarative GET/PUT/PATCH /volumes with volumes_want and the union held, idempotency left to cocoon, and python parity. local go test -race on pool, engine and server passes, and go vet on e2e is clean.

must fix (inline, plus one here):

  • the cocoon floor. cocoon v0.7.2 is out and carries cocoonstack/cocoon#282. every put re-attaches kept volumes, so a cocoon without #282 likely fails on the second call. set RequiredCocoon in engine/engine.go to v0.7.2. it only warns today, so also refuse put and patch with a clear error below the floor.
  • auth on the volume endpoints, removal after tenant removal, a hold leak on release, and the ci failure: see inline.

should fix: inline. the biggest one is the re-apply of every kept volume on each change.

style: inline. asl and the comment budget, plus the python layout.

once these are in, we run it on our testbed with cocoon v0.7.2: real ch hotplug and unplug, a busy guest unmount, and a sandboxd restart mid-change.

Comment thread sandboxd/server/volume_mutation.go Outdated
Comment thread sandboxd/pool/volume_mutation.go
Comment thread sandboxd/pool/volume_mutation.go
Comment thread sandboxd/pool/volume_mutation_test.go
Comment thread sandboxd/pool/volume_mutation.go Outdated
Comment thread sandboxd/pool/claims.go Outdated
Comment thread sdk/go/volumes.go Outdated
Comment thread sdk/python/cocoonsandbox/volumes.py Outdated
Comment thread sdk/python/cocoonsandbox/sandbox.py
Comment thread sdk/python/cocoonsandbox/__init__.py Outdated
@minatoaquaMK2

minatoaquaMK2 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Local validation for PR #339 on 2026-10-16. The report and one-off result files are kept out of the PR file diff. The caller tutorial and reusable hardware program remain in the source.

Tested source Coverage
ceda20dd4ad944e354e6f300b72b610b3d2422d6 Initial complete software suite
cc444095b3ba000de9ac11d7ef0674e386153804 Final production hardware smoke and real invalid-PUT probe
da56205bb31b489c15af3ea670e1cbe87905bf01 Affected race, lint, and format after the diagnostic regression fix
b87115f70f24de5823a5600da6086b213e51e7ed Sandboxd race, Linux/Darwin lint, and format after the final test-helper cleanup

The production code after the hardware revision is unchanged. The later source changes rename two test-local variables and group the test helpers with one shared held-run setup. The final documentation cleanup does not change runtime behavior.

All 29 distinct software checks have passing latest results.

Software check Result
Race tests in protocol/wire, sandboxd, sdk/go, e2e, and mcp PASS
golangci-lint v2.13.2 for Linux and Darwin in all five Go modules PASS
Formatter checks in all five Go modules PASS
Ruff format and lint, plus strict Python SDK mypy PASS
Python SDK, OpenAI adapter, and LangChain adapter 247, 10, and 15 tests passed
PostgreSQL tenant/pool-set stores and multi-node integration PASS, no skipped PostgreSQL tests
Caddy v2.11.4 TLS through Go and Python SDKs PASS
Final helper cleanup, sandboxd race/Linux lint/Darwin lint/format PASS, all four at the helper revision above

Go used the repository-pinned 1.27.1 toolchain. Root permission tests dropped CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH. Each lint run used a fresh cache because the stock analyzer panicked with a reused cache. No analyzer or rule was disabled. The folded software output preserves the six earlier lint failures and their successful reruns. The diagnostic regression also failed for the predicted 400/GET disagreement before its fix, then passed afterward.

The real node used the official Cocoon v0.7.2 release, Git b3b40f3bdedc384c482eb81dcdc60a774dd8d560, with Cloud Hypervisor v54.0.0 and Linux 6.18.37. The Cocoon archive matched the published checksum. All three candidate binaries were rebuilt from the clean hardware revision. Two disposable ext4 images supplied different /version contents.

Real hardware scenario Result
Shared dataset reads and write refusal with EROFS PASS
Same-path v1 to v2 replacement, repeated target, then explicit empty target PASS
Same sandbox, session, boot ID, PID, process start time, and session environment PASS
Busy cwd, bind mount, or foreign mount returns 409 with a guest path; close reference and retry PASS
Repeat a settled target while it has a bind mount PASS, zero attach/detach calls
Independent exec, file read/write, and HTTP activity during a blocked real attach result PASS
Kill sandboxd after real attach succeeds but before its result returns; restart and retry PASS, original VMM PID survives
Cancel the caller at that same boundary; retry the saved desired target PASS
Fail VM removal; refuse a conflicting writer before and after daemon restart PASS
Permit official VM removal; a new writer mounts and writes the backing PASS
Reject duplicate names with an explanatory 400 PASS, unchanged GET/data and zero disk calls
Final resource cleanup PASS, zero claims, retirements, VMs, sockets, owned TAPs, or fault markers

The fault observer forwards ordinary commands to official Cocoon. It blocks only after a successful real attach, or injects exit 73 before VM removal. It does not emulate version support, device idempotence, or successful removal. The original VMM PID 1082008 survives daemon restart. Retiring PID 1084550 survives failed deletion and restart until real cleanup. Both and the separate invalid-request VM PID 1087371 are gone at final cleanup. The read-only v2 backing checksum is unchanged; the v1 image receives the intentional writable reuse probe. Existing local k3s services were untouched.

The baseline hardware smoke returned HTTP 500 for busy cwd. The candidate returns 409. Its repeated-target observation window contains no disk call. The invalid PUT preserves the exact settled GET view and guest v1 contents.

Reproduce the software checks from the sandboxd workflow and Python workflow. The hardware instructions describe the existing program and isolated fault hooks. The caller tutorial explains replacement and retry.

Missing guest eject ACK remains unverified. The pause experiment did not establish ACK absence because official Cocoon resumed the guest before detach. Separate real crashes at every guest umount, host detach, journal, or fsync boundary and physical host power loss were not tested. The real daemon used sync_claims: false, so this is process-restart evidence. Controlled tests cover additional cuts separately. The literal Python tutorial was not run on the VMM; its SDK behavior has unit and Caddy transport coverage.

Catalog names must keep the same immutable backing image while held. The journal cannot detect an operator repointing a name after restart. Journal v2 requires draining live claims and retirement work before downgrade. A VMM that dies while holding volumes leaves the sandbox release-only.

These are local results. Remote CI is not claimed as passed. The earlier GitHub workflows at a06af70 reported action_required with no checks, and the authenticated account has only upstream pull permission. Maintainer workflow approval and a renewed formal review remain separate from resolving the addressed discussion threads.

Complete software suite attempts and final helper rerun

verification-1/race-protocol-wire.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/protocol/wire
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/protocol/wire	1.131s

verification-1/race-sandboxd.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/sandboxd	1.075s
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.103s
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	5.634s
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	4.869s
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	2.713s
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.717s
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	3.372s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.038s
ok  	github.com/cocoonstack/sandbox/sandboxd/server	3.370s
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.111s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.668s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	2.045s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	5.777s
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.054s
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.046s
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.050s
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.031s

verification-1/race-sdk-go.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/go
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/sdk/go	2.570s
ok  	github.com/cocoonstack/sandbox/sdk/go/silkd	1.229s
?   	github.com/cocoonstack/sandbox/sdk/go/silkd/silkdtest	[no test files]

verification-1/race-e2e.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/e2e	3.509s
?   	github.com/cocoonstack/sandbox/e2e/cmd/androidsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/browsersmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/coldproof	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/credsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/crossnode	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/demo	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/egresssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/guestserver	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/interceptsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/lifecycle	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/meshsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/portsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pullbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pushbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/qaab	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/ringsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/rpcbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/smoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/sockssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/volumesmoke	[no test files]
ok  	github.com/cocoonstack/sandbox/e2e/internal/harness	1.033s

verification-1/race-mcp.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/mcp
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/mcp	1.771s

verification-1/lint-protocol-wire-linux.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/protocol/wire
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/lint-protocol-wire-darwin.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/protocol/wire
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/format-protocol-wire.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/protocol/wire
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-1/lint-sandboxd-linux.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
sandboxd/pool/volume.go:145:3: shadow: declaration of "resolvedVolume" shadows declaration at line 33 (govet)
		resolvedVolume, resolveErr := m.resolveCatalogVolume(ctx, volume)
		^
1 issues:
* govet: 1

verification-1/lint-sandboxd-darwin.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
sandboxd/pool/volume.go:145:3: shadow: declaration of "resolvedVolume" shadows declaration at line 33 (govet)
		resolvedVolume, resolveErr := m.resolveCatalogVolume(ctx, volume)
		^
1 issues:
* govet: 1

verification-1/format-sandboxd.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-1/lint-sdk-go-linux.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/go
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/lint-sdk-go-darwin.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/go
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/format-sdk-go.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/go
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-1/lint-e2e-linux.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/lint-e2e-darwin.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/format-e2e.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-1/lint-mcp-linux.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/mcp
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/lint-mcp-darwin.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/mcp
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-1/format-mcp.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/mcp
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-1/python-format.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo
command=['/tmp/pr339-design/python-env/bin/ruff', 'format', '--check', '.']
38 files already formatted

verification-1/python-lint.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo
command=['/tmp/pr339-design/python-env/bin/ruff', 'check', '.']
All checks passed!

verification-1/python-types.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/python
command=['/tmp/pr339-design/python-env/bin/python', '-m', 'mypy']
Success: no issues found in 10 source files

verification-1/python-test-python.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/python
command=['/tmp/pr339-design/python-env/bin/python', '-m', 'pytest', '-q']
........................................................................ [ 29%]
........................................................................ [ 58%]
........................................................................ [ 87%]
...............................                                          [100%]
247 passed in 35.10s

verification-1/python-test-openai.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/openai
command=['/tmp/pr339-design/python-env/bin/python', '-m', 'pytest', '-q']
..........                                                               [100%]
10 passed in 6.26s

verification-1/python-test-langchain.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sdk/langchain
command=['/tmp/pr339-design/python-env/bin/python', '-m', 'pytest', '-q']
...............                                                          [100%]
15 passed in 0.35s

verification-1/pg-stores.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['go', 'test', '-race', '-count=1', '-v', './tenants/...', './poolset/...']
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
=== RUN   TestContract
=== RUN   TestContract/put_resolve_peek
=== RUN   TestContract/keep_and_rotate_tokens
=== RUN   TestContract/list_pages_by_name
=== RUN   TestContract/delete_and_replace
=== RUN   TestContract/replace_swaps_tokens
=== RUN   TestContract/lookup
--- PASS: TestContract (0.01s)
    --- PASS: TestContract/put_resolve_peek (0.00s)
    --- PASS: TestContract/keep_and_rotate_tokens (0.00s)
    --- PASS: TestContract/list_pages_by_name (0.00s)
    --- PASS: TestContract/delete_and_replace (0.00s)
    --- PASS: TestContract/replace_swaps_tokens (0.00s)
    --- PASS: TestContract/lookup (0.00s)
=== RUN   TestTheSetSurvivesAReopen
--- PASS: TestTheSetSurvivesAReopen (0.00s)
=== RUN   TestATenantsFileNeedsTheRootToken
--- PASS: TestATenantsFileNeedsTheRootToken (0.00s)
PASS
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.043s
=== RUN   TestContract
=== RUN   TestContract/put_resolve_peek
=== RUN   TestContract/keep_and_rotate_tokens
=== RUN   TestContract/list_pages_by_name
=== RUN   TestContract/delete_and_replace
=== RUN   TestContract/replace_swaps_tokens
=== RUN   TestContract/lookup
--- PASS: TestContract (1.09s)
    --- PASS: TestContract/put_resolve_peek (0.26s)
    --- PASS: TestContract/keep_and_rotate_tokens (0.16s)
    --- PASS: TestContract/list_pages_by_name (0.15s)
    --- PASS: TestContract/delete_and_replace (0.25s)
    --- PASS: TestContract/replace_swaps_tokens (0.15s)
    --- PASS: TestContract/lookup (0.14s)
=== RUN   TestAWriteOnOneNodeReachesTheOtherNodesCache
--- PASS: TestAWriteOnOneNodeReachesTheOtherNodesCache (0.40s)
=== RUN   TestACachedTenantOutlivesTheDatabase
--- PASS: TestACachedTenantOutlivesTheDatabase (0.13s)
=== RUN   TestARoleWithoutCreateServesAPreCreatedTable
--- PASS: TestARoleWithoutCreateServesAPreCreatedTable (0.23s)
=== RUN   TestAnUnrelatedEvictionKeepsALookupCacheable
--- PASS: TestAnUnrelatedEvictionKeepsALookupCacheable (0.00s)
PASS
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	2.901s
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
=== RUN   TestStoreVersionsTheCellsSet
--- PASS: TestStoreVersionsTheCellsSet (0.19s)
=== RUN   TestWatchFiresOnConnectAndOnItsCellsWrites
--- PASS: TestWatchFiresOnConnectAndOnItsCellsWrites (0.42s)
=== RUN   TestADownDatabaseIsUnavailable
--- PASS: TestADownDatabaseIsUnavailable (0.12s)
PASS
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.766s

verification-1/pg-pool.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['go', 'test', '-race', '-count=1', '-v', '-run', 'TestTwoNodesOnOneMetaStore|TestNodesOfACell|TestAMetaStoreNodeWithoutACell', './pool/']
=== RUN   TestNodesOfACellShareThePoolSet
--- PASS: TestNodesOfACellShareThePoolSet (0.50s)
=== RUN   TestAMetaStoreNodeWithoutACellKeepsItsOwnPools
--- PASS: TestAMetaStoreNodeWithoutACellKeepsItsOwnPools (0.19s)
=== RUN   TestTwoNodesOnOneMetaStoreShareTheTenantSet
--- PASS: TestTwoNodesOnOneMetaStoreShareTheTenantSet (0.25s)
PASS
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	2.120s

verification-1/tls-caddy.log
head=ceda20dd4ad944e354e6f300b72b610b3d2422d6
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['go', 'test', '-race', '-count=1', '-run', 'TestCaddyTLSCluster', '-timeout', '60s', '-v', '.']
=== RUN   TestCaddyTLSCluster
=== RUN   TestCaddyTLSCluster/Go
=== RUN   TestCaddyTLSCluster/Python
--- PASS: TestCaddyTLSCluster (0.42s)
    --- PASS: TestCaddyTLSCluster/Go (0.10s)
    --- PASS: TestCaddyTLSCluster/Python (0.19s)
PASS
ok  	github.com/cocoonstack/sandbox/e2e	1.477s

verification-2/race-sandboxd.log
head=b249bd2813602eab44e498ecd6e106652e81940a
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/sandboxd	1.080s
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.083s
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	5.628s
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	4.841s
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	2.633s
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.704s
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	3.430s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.039s
ok  	github.com/cocoonstack/sandbox/sandboxd/server	3.385s
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.112s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.657s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	2.040s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	9.072s
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.059s
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.043s
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.079s
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.032s

verification-2/lint-sandboxd-linux.log
head=b249bd2813602eab44e498ecd6e106652e81940a
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-2/lint-sandboxd-darwin.log
head=b249bd2813602eab44e498ecd6e106652e81940a
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-2/format-sandboxd.log
head=b249bd2813602eab44e498ecd6e106652e81940a
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-3/race-sandboxd.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/sandboxd	1.069s
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.118s
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	5.686s
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	4.838s
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	2.705s
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.711s
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	3.520s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.036s
ok  	github.com/cocoonstack/sandbox/sandboxd/server	3.435s
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.098s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.608s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	2.035s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	4.268s
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.068s
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.053s
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.071s
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.045s

verification-3/race-e2e.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/e2e	3.528s
?   	github.com/cocoonstack/sandbox/e2e/cmd/androidsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/browsersmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/coldproof	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/credsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/crossnode	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/demo	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/egresssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/guestserver	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/interceptsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/lifecycle	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/meshsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/portsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pullbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pushbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/qaab	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/ringsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/rpcbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/smoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/sockssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/volumesmoke	[no test files]
ok  	github.com/cocoonstack/sandbox/e2e/internal/harness	1.032s

verification-3/lint-sandboxd-linux.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
sandboxd/pool/volume_convergence_test.go:206:10: shadow: declaration of "err" shadows declaration at line 202 (govet)
			if _, err := m.SetVolumes(t.Context(), sb.ID, sb.Tenant, []types.Volume{{Name: "data"}, {Name: "data"}}); !errors.Is(err, ErrBadVolume) {
			      ^
1 issues:
* govet: 1

verification-3/lint-sandboxd-darwin.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
sandboxd/pool/volume_convergence_test.go:206:10: shadow: declaration of "err" shadows declaration at line 202 (govet)
			if _, err := m.SetVolumes(t.Context(), sb.ID, sb.Tenant, []types.Volume{{Name: "data"}, {Name: "data"}}); !errors.Is(err, ErrBadVolume) {
			      ^
1 issues:
* govet: 1

verification-3/format-sandboxd.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-3/lint-e2e-linux.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
e2e/volume_mutation_test.go:51:8: shadow: declaration of "err" shadows declaration at line 46 (govet)
	if _, err := sb.SetVolumes(t.Context(), []sandbox.Volume{{Name: "dataset"}}); err != nil {
	      ^
1 issues:
* govet: 1

verification-3/lint-e2e-darwin.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
e2e/volume_mutation_test.go:51:8: shadow: declaration of "err" shadows declaration at line 46 (govet)
	if _, err := sb.SetVolumes(t.Context(), []sandbox.Volume{{Name: "dataset"}}); err != nil {
	      ^
1 issues:
* govet: 1

verification-3/format-e2e.log
head=cc444095b3ba000de9ac11d7ef0674e386153804
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-4/race-sandboxd.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/sandboxd	1.061s
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.104s
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	5.614s
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	4.864s
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	2.717s
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.707s
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	3.521s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.047s
ok  	github.com/cocoonstack/sandbox/sandboxd/server	3.392s
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.102s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.668s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	2.046s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	5.678s
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.043s
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.039s
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.085s
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.031s

verification-4/race-e2e.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/e2e	3.519s
?   	github.com/cocoonstack/sandbox/e2e/cmd/androidsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/browsersmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/coldproof	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/credsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/crossnode	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/demo	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/egresssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/guestserver	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/interceptsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/lifecycle	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/meshsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/portsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pullbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pushbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/qaab	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/ringsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/rpcbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/smoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/sockssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/volumesmoke	[no test files]
ok  	github.com/cocoonstack/sandbox/e2e/internal/harness	1.033s

verification-4/lint-sandboxd-linux.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-4/lint-sandboxd-darwin.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-4/format-sandboxd.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

verification-4/lint-e2e-linux.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-4/lint-e2e-darwin.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

verification-4/format-e2e.log
head=da56205bb31b489c15af3ea670e1cbe87905bf01
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/e2e
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']

head=b87115f70f24de5823a5600da6086b213e51e7ed
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['setpriv', '--bounding-set=-dac_override,-dac_read_search', 'go', 'test', '-race', '-count=1', './...']
ok  	github.com/cocoonstack/sandbox/sandboxd	1.071s
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.090s
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	5.628s
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	4.876s
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	2.328s
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.701s
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	3.423s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.047s
ok  	github.com/cocoonstack/sandbox/sandboxd/server	3.399s
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.122s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.642s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	2.047s
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	5.217s
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.049s
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.040s
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.073s
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.035s

head=b87115f70f24de5823a5600da6086b213e51e7ed
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

head=b87115f70f24de5823a5600da6086b213e51e7ed
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'run', './...']
0 issues.

head=b87115f70f24de5823a5600da6086b213e51e7ed
cwd=/root/eric/composable-computer/.audit/pr339-implementation/repo/sandboxd
command=['/root/eric/composable-computer/.audit/pr339-implementation/repo/bin/golangci-lint-v2.13.2/golangci-lint', 'fmt', '--diff', './...']
Diagnostic regression before and after its fix

review-fix/sandboxd-red.log
ok  	github.com/cocoonstack/sandbox/sandboxd	1.071s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.037s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	1.036s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	1.030s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	1.041s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.041s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
--- FAIL: TestRejectedVolumeTargetPreservesConvergenceDiagnostics (0.01s)
    --- FAIL: TestRejectedVolumeTargetPreservesConvergenceDiagnostics/settled (0.01s)
        volume_convergence_test.go:211: rejected request changed convergence view: before={VolumesWant:[{Name:data Mount:/volumes/data Mode: AttachOnly:false}] Volumes:[{Name:data Mount:/volumes/data Mode: AttachOnly:false}] States:[{Name:data State:attached Error:}] Converging:false Error:} after={VolumesWant:[{Name:data Mount:/volumes/data Mode: AttachOnly:false}] Volumes:[{Name:data Mount:/volumes/data Mode: AttachOnly:false}] States:[{Name:data State:attached Error:}] Converging:false Error:volume change failed; retry the desired target} err=<nil>
    --- FAIL: TestRejectedVolumeTargetPreservesConvergenceDiagnostics/conflict (0.00s)
        volume_convergence_test.go:211: rejected request changed convergence view: before={VolumesWant:[] Volumes:[{Name:data Mount:/volumes/data Mode: AttachOnly:false}] States:[{Name:data State:detaching Error:cannot unmount "/volumes/data"; close guest references and retry}] Converging:false Error:cannot unmount "/volumes/data"; close guest references and retry} after={VolumesWant:[] Volumes:[{Name:data Mount:/volumes/data Mode: AttachOnly:false}] States:[{Name:data State:detaching Error:cannot unmount "/volumes/data"; close guest references and retry}] Converging:false Error:volume change failed; retry the desired target} err=<nil>
FAIL
FAIL	github.com/cocoonstack/sandbox/sandboxd/pool	0.094s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.047s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/server	1.052s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.036s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.031s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	1.038s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	1.039s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.039s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.042s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.038s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.031s [no tests to run]
FAIL

review-fix/e2e-red.log
--- FAIL: TestRejectedVolumeTargetExplainsRefusalWithoutChangingState (0.03s)
    volume_mutation_test.go:57: invalid target explanation: set volumes: volume change failed; retry the desired target (http 400)
    volume_mutation_test.go:61: rejected request changed settled state: {VolumesWant:[{Name:dataset Mount:/volumes/dataset Mode:}] Volumes:[{Name:dataset Mount:/volumes/dataset Mode:}] States:[{Name:dataset State:attached Error:}] Converging:false Error:volume change failed; retry the desired target} err=<nil>
FAIL
FAIL	github.com/cocoonstack/sandbox/e2e	0.088s
?   	github.com/cocoonstack/sandbox/e2e/cmd/androidsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/browsersmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/coldproof	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/credsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/crossnode	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/demo	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/egresssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/guestserver	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/interceptsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/lifecycle	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/meshsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/portsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pullbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pushbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/qaab	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/ringsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/rpcbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/smoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/sockssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/volumesmoke	[no test files]
ok  	github.com/cocoonstack/sandbox/e2e/internal/harness	1.031s [no tests to run]
FAIL

review-fix/sandboxd-green.log
ok  	github.com/cocoonstack/sandbox/sandboxd	1.093s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/config	1.039s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/egress	1.066s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/engine	1.070s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/mesh	1.044s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/metastore/metastoretest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/netfilter	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/outbound	1.079s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/outbound/outboundtest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/pool	1.204s
?   	github.com/cocoonstack/sandbox/sandboxd/poolset	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/poolset/pg	1.042s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/server	1.106s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store	1.035s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store/dir	1.042s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store/peer	1.061s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/store/s3	1.049s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/store/storetest	[no test files]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/file	1.041s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/tenants/pg	1.041s [no tests to run]
?   	github.com/cocoonstack/sandbox/sandboxd/tenants/tenantstest	[no test files]
ok  	github.com/cocoonstack/sandbox/sandboxd/types	1.041s [no tests to run]
ok  	github.com/cocoonstack/sandbox/sandboxd/utils	1.035s [no tests to run]

review-fix/e2e-green.log
ok  	github.com/cocoonstack/sandbox/e2e	1.129s
?   	github.com/cocoonstack/sandbox/e2e/cmd/androidsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/browsersmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/coldproof	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/credsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/crossnode	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/demo	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/egresssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/guestserver	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/interceptsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/lifecycle	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/meshsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/portsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pullbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/pushbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/qaab	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/ringsmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/rpcbench	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/smoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/sockssmoke	[no test files]
?   	github.com/cocoonstack/sandbox/e2e/cmd/volumesmoke	[no test files]
ok  	github.com/cocoonstack/sandbox/e2e/internal/harness	1.033s [no tests to run]
Real hardware smoke output
volume=dataset-v1 claim_a=396.7ms claim_b=394.1ms
VOLUME PASS concurrent_read_bytes=3 read_only=true released=true
CONTINUITY before={"Sandbox":"sb_13c00446eaf0a810","Session":"sess-68-1","Boot":"cd7dbea1-cbb8-4cbb-aba5-97f4e5aeab02","PID":181,"Start":"237088"}
BUSY PASS reference=cwd view={"volumes_want":[{"name":"dataset-v2","mount":"/datasets/current"}],"volumes":[{"name":"dataset-v1","mount":"/datasets/current"}],"states":[{"name":"dataset-v1","state":"detaching","error":"cannot unmount \"/datasets/current\"; close guest references and retry"},{"name":"dataset-v2","state":"pending","error":"volume change interrupted; retry the desired target"}],"converging":false,"error":"cannot unmount \"/datasets/current\"; close guest references and retry"}
RETAINED begin=1792161535537904360
RETAINED end=1792161535607563767
BUSY PASS reference=bind view={"volumes_want":[{"name":"dataset-v2","mount":"/datasets/current"}],"volumes":[{"name":"dataset-v1","mount":"/datasets/current"}],"states":[{"name":"dataset-v1","state":"detaching","error":"volume device has another mount at \"/tmp/volume-bind\"; remove guest mount references before retrying"},{"name":"dataset-v2","state":"pending","error":"volume change interrupted; retry the desired target"}],"converging":false,"error":"volume device has another mount at \"/tmp/volume-bind\"; remove guest mount references before retrying"}
BUSY PASS reference=foreign view={"volumes_want":[{"name":"dataset-v1","mount":"/datasets/current"}],"volumes":[{"name":"dataset-v2","mount":"/datasets/current"}],"states":[{"name":"dataset-v2","state":"detaching","error":"volume mount \"/datasets/current\" has a different device, root, or mode; restore the expected mount before retrying"},{"name":"dataset-v1","state":"pending","error":"volume change interrupted; retry the desired target"}],"converging":false,"error":"volume mount \"/datasets/current\" has a different device, root, or mode; restore the expected mount before retrying"}
HOOK runtime_event={"time": 1792161537.4404385, "pid": 1083422, "args": ["vm", "disk", "attach", "sbx-8afde47472091862474d5edf567e6f55-dd724c346a53", "--path", "/tmp/pr339-live/v1.raw", "--name", "dataset-v1", "--readonly", "--directio", "off"]}
HOOK real_vm=[{"id": "3SMJN3MBILXGIJ2FUF4LLABHYZ", "name": "sbx-8afde47472091862474d5edf567e6f55-dd724c346a53", "pid": 1082008, "pid_exists": true, "disks": [{"path": "/tmp/pr339-live/runtime/oci/blobs/1e6452dcc4be48895222b1c62534f2f8135780f9c680764bedc5a3a9876004d7.erofs", "ro": true, "serial": "cocoon-layer0", "role": "layer"}, {"path": "/tmp/pr339-live/runtime/oci/blobs/8a2859c3087cbe7e9a649816b8d5afa4cbd55532de7982a3b6a753786a0b1a4b.erofs", "ro": true, "serial": "cocoon-layer1", "role": "layer"}, {"path": "/tmp/pr339-live/run/cloudhypervisor/3SMJN3MBILXGIJ2FUF4LLABHYZ/cow.raw", "ro": false, "serial": "cocoon-cow", "role": "cow"}]}]
HOOK gated_view={"volumes_want": [{"name": "dataset-v1", "mount": "/datasets/current"}], "volumes": [], "states": [{"name": "dataset-v1", "state": "pending"}], "converging": true}
HOOK daemon_restart old=1081212 new=1083505
ACTIVITY PASS exec=true file=true port=true
INTERRUPTION request_error=Put "http://127.0.0.1:17777/v1/sandboxes/sb_13c00446eaf0a810/volumes": EOF
INTERRUPTION recovered_view={"volumes_want":[{"name":"dataset-v1","mount":"/datasets/current"}],"volumes":[],"states":[{"name":"dataset-v1","state":"pending","error":"volume change interrupted; retry the desired target"}],"converging":false}
INTERRUPTION PASS sandbox=sb_13c00446eaf0a810
HOOK runtime_event={"time": 1792161539.141075, "pid": 1084011, "args": ["vm", "disk", "attach", "sbx-8afde47472091862474d5edf567e6f55-dd724c346a53", "--path", "/tmp/pr339-live/v1.raw", "--name", "dataset-v1", "--readonly", "--directio", "off"]}
HOOK real_vm=[{"id": "3SMJN3MBILXGIJ2FUF4LLABHYZ", "name": "sbx-8afde47472091862474d5edf567e6f55-dd724c346a53", "pid": 1082008, "pid_exists": true, "disks": [{"path": "/tmp/pr339-live/runtime/oci/blobs/1e6452dcc4be48895222b1c62534f2f8135780f9c680764bedc5a3a9876004d7.erofs", "ro": true, "serial": "cocoon-layer0", "role": "layer"}, {"path": "/tmp/pr339-live/runtime/oci/blobs/8a2859c3087cbe7e9a649816b8d5afa4cbd55532de7982a3b6a753786a0b1a4b.erofs", "ro": true, "serial": "cocoon-layer1", "role": "layer"}, {"path": "/tmp/pr339-live/run/cloudhypervisor/3SMJN3MBILXGIJ2FUF4LLABHYZ/cow.raw", "ro": false, "serial": "cocoon-cow", "role": "cow"}]}]
HOOK gated_view={"volumes_want": [{"name": "dataset-v1", "mount": "/datasets/current"}], "volumes": [], "states": [{"name": "dataset-v1", "state": "pending"}], "converging": true}
HOOK canceled_view={"volumes_want": [{"name": "dataset-v1", "mount": "/datasets/current"}], "volumes": [], "states": [{"name": "dataset-v1", "state": "pending", "error": "volume change failed; retry the desired target"}], "converging": false, "error": "volume change failed; retry the desired target"}
ACTIVITY PASS exec=true file=true port=true
INTERRUPTION request_error=Put "http://127.0.0.1:17777/v1/sandboxes/sb_13c00446eaf0a810/volumes": context canceled
INTERRUPTION recovered_view={"volumes_want":[{"name":"dataset-v1","mount":"/datasets/current"}],"volumes":[],"states":[{"name":"dataset-v1","state":"pending","error":"volume change failed; retry the desired target"}],"converging":false,"error":"volume change failed; retry the desired target"}
INTERRUPTION PASS sandbox=sb_13c00446eaf0a810
CONTINUITY PASS after={"Sandbox":"sb_13c00446eaf0a810","Session":"sess-68-1","Boot":"cd7dbea1-cbb8-4cbb-aba5-97f4e5aeab02","PID":181,"Start":"237088"} v1="v1" v2="v2" cwd_409=true bind_409=true foreign_409=true retained_bind=true erofs=true cleared=true
LIVE VOLUME PASS read_bytes=3 repeated_put=true repeated_patch=true cleared=true
RETIREMENT release_error=release: release failed (http 500)
RETIREMENT writer_refused=claim: volume is held by another claim: volume "dataset-v1" (http 409)
HOOK runtime_event={"time": 1792161541.4820101, "pid": 1084709, "args": ["vm", "rm", "--force", "sbx-8afde47472091862474d5edf567e6f55-89e580237e51"]}
HOOK real_vm=[{"id": "OHF3UXRBAEFIHIABIJ7BNYWL2P", "name": "sbx-8afde47472091862474d5edf567e6f55-89e580237e51", "pid": 1084550, "pid_exists": true, "disks": [{"path": "/tmp/pr339-live/runtime/oci/blobs/1e6452dcc4be48895222b1c62534f2f8135780f9c680764bedc5a3a9876004d7.erofs", "ro": true, "serial": "cocoon-layer0", "role": "layer"}, {"path": "/tmp/pr339-live/runtime/oci/blobs/8a2859c3087cbe7e9a649816b8d5afa4cbd55532de7982a3b6a753786a0b1a4b.erofs", "ro": true, "serial": "cocoon-layer1", "role": "layer"}, {"path": "/tmp/pr339-live/run/cloudhypervisor/OHF3UXRBAEFIHIABIJ7BNYWL2P/cow.raw", "ro": false, "serial": "cocoon-cow", "role": "cow"}]}]
HOOK daemon_restart old=1083505 new=1084870
HOOK real_vm=[{"id": "OHF3UXRBAEFIHIABIJ7BNYWL2P", "name": "sbx-8afde47472091862474d5edf567e6f55-89e580237e51", "pid": 1084550, "pid_exists": true, "disks": [{"path": "/tmp/pr339-live/runtime/oci/blobs/1e6452dcc4be48895222b1c62534f2f8135780f9c680764bedc5a3a9876004d7.erofs", "ro": true, "serial": "cocoon-layer0", "role": "layer"}, {"path": "/tmp/pr339-live/runtime/oci/blobs/8a2859c3087cbe7e9a649816b8d5afa4cbd55532de7982a3b6a753786a0b1a4b.erofs", "ro": true, "serial": "cocoon-layer1", "role": "layer"}, {"path": "/tmp/pr339-live/run/cloudhypervisor/OHF3UXRBAEFIHIABIJ7BNYWL2P/cow.raw", "ro": false, "serial": "cocoon-cow", "role": "cow"}]}]
RETIREMENT writer_refused=claim: volume is held by another claim: volume "dataset-v1" (http 409)
RETIREMENT PASS writer_blocked_before_restart=true writer_blocked_after_restart=true backing_reused=true
Zero retained disk calls and final cleanup
{
  "start_ns": 1792161535537904360,
  "end_ns": 1792161535607563767,
  "disk_calls": []
}

{
  "candidate": "cc444095b3ba000de9ac11d7ef0674e386153804",
  "claims": 0,
  "volume_retirements": 0,
  "vms": [],
  "runtime_sockets": [],
  "owned_taps": [],
  "vm_pids_gone": {
    "1082008": true,
    "1084550": true,
    "1087371": true
  },
  "isolated_daemon_stopped": true,
  "existing_services_stopped": [],
  "retained_device_calls": 0,
  "fault_markers_remaining": []
}
Actual invalid PUT, unchanged GET, and guest data
{
  "sandbox": "sb_d578a0cb4762e49f",
  "status": 400,
  "failure": {
    "volumes_want": [
      {
        "name": "dataset-v1",
        "mount": "/datasets/invalid"
      }
    ],
    "volumes": [
      {
        "name": "dataset-v1",
        "mount": "/datasets/invalid"
      }
    ],
    "states": [
      {
        "name": "dataset-v1",
        "state": "attached"
      }
    ],
    "converging": false,
    "error": "invalid volume request: duplicate volume \"dataset-v1\""
  },
  "before": {
    "volumes_want": [
      {
        "name": "dataset-v1",
        "mount": "/datasets/invalid"
      }
    ],
    "volumes": [
      {
        "name": "dataset-v1",
        "mount": "/datasets/invalid"
      }
    ],
    "states": [
      {
        "name": "dataset-v1",
        "state": "attached"
      }
    ],
    "converging": false
  },
  "after": {
    "volumes_want": [
      {
        "name": "dataset-v1",
        "mount": "/datasets/invalid"
      }
    ],
    "volumes": [
      {
        "name": "dataset-v1",
        "mount": "/datasets/invalid"
      }
    ],
    "states": [
      {
        "name": "dataset-v1",
        "state": "attached"
      }
    ],
    "converging": false
  },
  "marker_before": "v1\n",
  "marker_after": "v1\n",
  "vm_pid": 1087371,
  "vm_pid_exists": true,
  "invalid_put_disk_calls": [],
  "PASS": true,
  "release_status": 204
}
Baseline busy-cwd result
volume=dataset-v1 claim_a=389.0ms claim_b=371.8ms
VOLUME PASS concurrent_read_bytes=3 read_only=true released=true
CONTINUITY before={"Sandbox":"sb_7d252ce9de32e3e7","Session":"sess-69-0","Boot":"4cd81a72-ea89-4282-a36d-2e3e4965755e","PID":177,"Start":"1738"}
volumesmoke: cwd busy removal must return 409 and guest path: view={VolumesWant:[{Name:dataset-v2 Mount:/datasets/current Mode:}] Volumes:[{Name:dataset-v1 Mount:/datasets/current Mode:}] States:[{Name:dataset-v1 State:detaching Error:volume convergence failed; retry the desired set} {Name:dataset-v2 State:pending Error:}] Error:volume convergence failed; retry the desired set} error=set volumes: volume convergence failed; retry the desired set (http 500)

@minatoaquaMK2
minatoaquaMK2 marked this pull request as ready for review October 16, 2026 14:45
@CMGS

CMGS commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

thanks for round 3. every must-fix and should-fix from the last review is in, and it holds up on our testbed: official cocoon v0.7.2 + CH v54, real hotplug/unplug, busy cwd and bind mount → 409, kill -9 of sandboxd mid-change → restart → retry converges with the same VMM pid, tenant/sandbox token scoping, the dev-build 503, capture refused without a trim, and the full single-node e2e twice, no regressions.

the rest is on us, and we'll do it on this branch so main never sees an intermediate state. maintainer edits are enabled, so we'll push a few commits on top of yours and squash-merge with you as the author.

what we'll change and why:

  • drop the claims.json v2 journal, volume_retirements and the unknownVolumeVMs fence (6c4721c). in-process, holds already stay in pendingRemovals until vm rm confirms. after a restart, CH's hotplug image lock refuses a conflicting attach. that restart gap also predates this PR and covers claim-time rw volumes, so if it needs closing it belongs in its own issue. meanwhile the envelope breaks rolling sandboxd back (an older binary can't parse format_version), adds a clone + marshal to every claims commit, and turns a release with volumes into 2-3 full rewrites. the fence also blocks every volume claim on the node with a 400.
  • zero cost on the claim, commit and release paths.
    • volumes_want is persisted only when it differs from volumes; absent means equal, so old files and old binaries both read it.
    • the mountinfo check runs only on the live path, not on a fresh claim clone.
    • the post-mount re-check goes.
    • the cocoon floor is checked once at startup instead of a cocoon version exec per PUT/PATCH (≈26 ms on a 384-core host, nearly all of a no-op PUT).
    • the duplicate captureAllowed / hasVolumeHolds lock round trips go.
  • fixes:
    • ErrVolumeBusy / ErrVolumeCapture currently come back as 409 "retry the desired target", which never succeeds; the real message wins now.
    • volumeErrors is dropped on every path that drops a claim (the archive purge missed it).
    • the Python client keeps >= 400 as the failure test for every other verb.
    • quarantineClaim keeps main's egress handling.
    • timeout=0 is rejected like in run().
  • volumesmoke: keep runLive behind a flag so the regular e2e leg doesn't require v0.7.2; drop continuity.go and the Python hooks. they did their job for your hardware report, and we cover fault injection on our side.
  • style: the two asl topdecl hits, errors.AsType, maps.DeleteFunc, the comment budget, and test helpers deduplicated (shared store-break / gated-engine setup, doReq, synctest for the timing asserts).

we'll re-run the same hardware matrix after the changes and post the numbers here before merging. thanks again, this was a lot of careful work.

CMGS added 2 commits October 17, 2026 02:41
…ence

In-process, a teardown already keeps its holds in pendingRemovals until
cocoon confirms the VM gone; after a restart, the hotplug image lock refuses
a conflicting attach. The v2 envelope broke rolling sandboxd back, added a
map clone and marshal to every claims commit and 2-3 full rewrites to a
release with volumes, and the fence turned one stuck orphan into a node-wide
400 for every volume claim. quarantineClaim keeps main's egress handling.
An absent field reads back as equal to Volumes, so a claims commit and a
list row cost the same as before live volumes, a legacy journal loads
without a second decode, and an older daemon still parses the file. The
claim path stores one slice for both sets instead of cloning it.
CMGS added 11 commits October 17, 2026 03:07
…rror

ErrTenantStoreDown and ErrPoolStoreDown wrap the pgx error, whose text names
the PostgreSQL host and user, and poolErrHTTP fell back to err.Error() for
them on every route that maps pool errors.
…ll mountinfo

mountinfo records the kernel-resolved path, so a mount under a symlinked
parent read as a bind of its own device: a repeated PUT answered 409 and a
live removal stayed refused until release. The check now resolves the
requested path with realpath only when the device sits elsewhere. The
mountinfo read had the 64 KiB sysfs cap, which a few dozen overlay mounts in
the guest exceed; it gets its own 1 MiB cap. In the same functions:
silkdOutput carries an exec's stdout, mountMode is the one ro/rw mapping,
refuseOtherMounts names what volumeUnmounted did, and the parameter and field
names match the rest of the package.
…and keeps Retry-After

The handler hand-rolled writePoolErr's loop and dropped its Retry-After, so
a tenant or pool-set store outage on the volume routes answered 503 without
the hint every other route sends.
rollbackClaim dropped a published claim without cancelling a live volume run
and freed heldVolumes(sb), which misses additions the run reserved before
its target commit and removals committed while the claim was failing. The
leaked reader kept every later writable claim of that volume busy until
restart; it now takes the run's holds as Release and reap do.
The run touched the sandbox only after converging, so an idle sweep could
pick a sandbox that was attaching its first volume before prepare reserved
any hold, hibernate it, and fail the request with ErrPaused.
…anged target

stageVolumeTarget holds Transition only for the bookkeeping; the claims
write (an fsync under sync_claims) happens after it, so exec and port dials,
which take Transition to wake, no longer wait on it. An identical PUT or
PATCH reserves nothing, changes nothing and writes no journal. A volume step
builds its applied set inside the one locked section that commits it, a run
that lost its claim answers ErrUnknownSandbox rather than a cancellation, a
failed target commit is not filed under the last precleaned volume, and the
two retry hints are constants. The claims DTO carries volumes_want as a
plain omitzero slice.
…en volume test tables

Write-only fakeManager fields and the never-set snapSaveErr go. The server
tests build every node through serveTestServer and newMeshTestServer, so all
of them close their relays, and requests with a bearer token use doReq. The
pool tests share waitPoolSettled and newTenantVolumeManager, the restart
budget test reuses its assertion helper, and shadowed names, verb-less
Errorf calls and helpers split around types are fixed.
ac70e41 touched the sandbox before validation, so a rejected or paused
request kept refreshing LastSeen: a client retrying PUT on a hibernated
claim held off archive_after forever. The stamp now follows the running
check and still precedes every device call.
2726e7f only narrowed the type hints, so an untyped caller's
set_volumes("data") or patch_volumes(remove="old") still sent one volume per
character.
@CMGS

CMGS commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

pushed 24 commits on top of fea3531 (head 4f4ef60). @minatoaquaMK2 please take a look, especially at the behavior changes in the second list.

what changed:

  • journal: the v2 envelope, volume_retirements and the unknownVolumeVMs fence are gone. claims.json is the flat map again and volumes_want is written only while it differs from volumes (absent = equal), so an older sandboxd still reads the file.
  • claim path: MountVolume is back to main's two guest calls. the idempotent mountinfo check lives in MountLiveVolume, used only by live repair, and the post-mount re-read is gone.
  • version floor: LiveVolumeCocoon (v0.7.2) gates PUT/PATCH; the startup warning stays at RequiredCocoon v0.6.9. the version is read once and cached.
  • fixes:
    • a busy or capture refusal reports itself instead of "retry".
    • diagnostics are dropped with the claim.
    • a rolled-back claim takes over a live run's holds.
    • a live run stamps activity only once the sandbox is known to be running.
    • a mount under a symlinked parent no longer reads as a bind of itself (realpath, only when the device sits elsewhere).
    • mountinfo is read with a 1 MiB cap.
    • store-down errors no longer echo the backend error text.
    • the volume handler keeps Retry-After.
    • the Python client keeps >= 400 for every verb and rejects a bare string.
  • simplification:
    • one commit path for a run; failed writes keep memory and recommit, like commitIfLive.
    • the journal write happens outside Transition.
    • an identical PUT/PATCH writes nothing.
    • mount conflicts carry only their message.
  • tests/e2e: -live gates the volumesmoke live leg (VOLUME_LIVE=1 in the e2e script); continuity.go and the Python hooks are removed; shared fixtures; synctest for the ordering tests.

behavior worth a second pair of eyes:

  1. a dev (non-semver) cocoon is now accepted for live changes, matching the startup rule; a release below v0.7.2 still gets 503.
  2. a failed claims write after a device change keeps the in-memory state and recommits in the background, instead of rolling memory back to a state the guest no longer has.
  3. 409/400 refusals after a run starts carry the full view; malformed bodies, auth and unknown sandbox carry only error (docs updated).

hardware, official cocoon v0.7.2 + CH v54, at 9236121:

  • functional checks:
    • add/repeat/swap/clear, busy cwd and bind mount → 409, kill -9 mid-change → restart → retry with the same VMM pid, and the auth matrix all pass.
    • capture is refused without a trim.
    • a symlinked mount parent: was a permanent 409, now 200.
    • a 238 KB mountinfo: was 500, now 200.
    • main started on the PR's claims.json and released every claim.
    • the full single-node e2e passes twice with the live leg on.
  • timings: no-op PUT 0.4 ms (was 26.4 ms), add 58.5 ms (was 85.3), remove 138.2 ms (was 165.2). claim latency with a claim-time volume is equal to main's on a shared golden.

CMGS added 13 commits October 17, 2026 09:00
…alls

A failed intent write keeps the target in memory for recommit, so a retry
of the same PUT matched the unchanged-target shortcut, skipped the commit
and attached while the journal still lacked the intent; a restart then lost
the attachment's hold. The shortcut now requires a journal in step with
memory, and otherwise commits first and fails before any device call.
The realpath lookup ran only when our device already sat at another path,
so a new volume aimed at a symlinked alias of an occupied mount point saw
no mount, stacked on top of the other device, and could not be removed
later. Every live mount check now resolves the requested path first; it
costs one guest exec on the live path only.
A journal write that failed after a device change filed "volume change
failed" on that volume; the retry that converged it neither found it
pending nor cleared it, so GET kept showing the stale error on an attached
volume.
…s a guest without realpath -m

09924f6 ran `realpath -m` on every check and trimmed all whitespace: a mount
path ending in a space read back as a bind of its own device, and a busybox
or toybox-without-PATH guest failed every live add and removal. A path that
mountinfo already lists is canonical and needs no lookup, only the trailing
newline is trimmed, and a failed lookup checks the path as given, as before
09924f6, with a warning.
Mount targets are validated as absolute paths, so -- guards nothing, and a missing target already falls back to the literal path; plain realpath runs on every guest toolchain.
…s realpath lookups

The trailing-space case now sits behind a symlink so TrimSpace fails it, and every case pins its realpath call count so dropping the listed-path shortcut fails too.
Guest images are Ubuntu with coreutils, so realpath fails only on a path with a missing component, and no mount can sit behind a path that does not exist.
…path

After a mount refused because another device occupies the path, removing the pending volume hit the same conflict, so it could not be dropped until the guest unmounted the other device. Detach now unmounts only its own device at the path.
umount removes the top mount at a path, so with a guest mount stacked on the volume it removed the guest's mount and then refused. Detach now refuses before unmounting when another mount shares the volume's path.
…e path

A guest bind of the volume's own subdirectory stacked on the volume shares its device, so the device-only check still unmounted the top mount, which was the guest's. Detach now unmounts only when the path holds exactly one mount and it is the volume's root.
@CMGS

CMGS commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

@minatoaquaMK2 thanks again. Since the last round we fixed most of the remaining issues on this branch (head 4c8d53b). All of them are covered by regression tests and verified on our testbed. The main ones:

  • a retried PUT no longer attaches before its intent is durable
  • a symlinked alias of an occupied mount point is refused with a 409 that names the resolved guest path
  • a mount path with a trailing space works
  • a converged run clears stale volume diagnostics
  • a pending volume refused at mount time can still be removed
  • a detach never unmounts a guest mount stacked on the volume

Squash-merging now with you as the author.

The claim issue that your v2 journal covered, where a VM's volume holds are lost across a sandboxd restart while a removal is pending, is tracked in #348. We'll fix it in the next PR.

@CMGS
CMGS merged commit e4e8acb into cocoonstack:main Oct 17, 2026
3 checks passed
@minatoaquaMK2

Copy link
Copy Markdown
Contributor Author

@minatoaquaMK2 thanks again. Since the last round we fixed most of the remaining issues on this branch (head 4c8d53b). All of them are covered by regression tests and verified on our testbed. The main ones:

  • a retried PUT no longer attaches before its intent is durable
  • a symlinked alias of an occupied mount point is refused with a 409 that names the resolved guest path
  • a mount path with a trailing space works
  • a converged run clears stale volume diagnostics
  • a pending volume refused at mount time can still be removed
  • a detach never unmounts a guest mount stacked on the volume

Squash-merging now with you as the author.

The claim issue that your v2 journal covered, where a VM's volume holds are lost across a sandboxd restart while a removal is pending, is tracked in #348. We'll fix it in the next PR.

Thanks for the thoughtful reviews and all the hands-on work getting this over the finish line! I really appreciate the time you put into the implementation and hardware testing. Learned a lot from this collaboration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants