Repository navigation
feat(sandboxd): add node-local tenant managed volumes - #368
minatoaquaMK2 wants to merge 2 commits into
Conversation
Keep tenant data disks across claim release and template replacement. Add node-local management, SDK methods, and durable writer ownership.
Issue #300 scope and local verificationThis is a phase-one proposal. It does not close #300 or implement the capture sequence in the maintainer's update. The later status comment is the remaining-scope checklist. #339 already supplied live read-only catalog updates. This PR adds managed-volume integration, not that earlier API. Plan coverage
Live managed attach/detach and Kubernetes lifecycle/status integration are also outside this PR. A managed claim can still use #339 to change its mounted read-only catalog entries. Exact artifact
Observed runtime results
The same guest filesystem UUID was The additional acceptance check used real HTTP requests and VMs on the same PR binary. A t2 claim attached t2's own managed disk with status200. A t2 claim naming t1's disk and one naming a valid unknown disk both returned status404 with Commands and formal checksThe following source checks passed on the candidate tree, which is byte-identical to the committed PR tree. Python commands used an environment with pytest, Ruff, and mypy installed. On root, the Go run dropped DAC override/read-search capabilities so permission checks remained meaningful. The equivalent commands below use illustrative external output and cache paths. setpriv --bounding-set=-dac_override,-dac_read_search -- sh -c '
for module in protocol/wire sandboxd sdk/go e2e mcp; do
(cd "$module" && GOWORK=off go test -mod=readonly -race -count=1 ./...) || exit 1
done
'
(cd sdk/python && PYTHONDONTWRITEBYTECODE=1 python -B -m pytest -p no:cacheprovider -q)
ruff check --no-cache sdk/python
ruff format --check --no-cache sdk/python
(cd sdk/python && python -B -m mypy --cache-dir /tmp/sandbox-pr-mypy-cache)
(cd sandboxd && GOWORK=off GOOS=darwin go build -mod=readonly -o /tmp/sandboxd-pr-darwin .)All five Go-module runs exited0. Python reported279 passed. Ruff, strict mypy, changed-Go-file formatting, and the Darwin build passed. The run did not configure PostgreSQL, an external S3 endpoint, Caddy, so their opt-in tests skipped. The separate 15-case VM run above is the real-VM local qualification, not a claim that those skipped tests ran. Pinned golangci-lint2.13.2 exited1 in unchanged protocol/wire imports because its decoder supports export-data version4 while this Go1.27 toolchain produces version5. No Go lint pass is claimed. CI results remain separate from these local results. Cleanup and limitsCleanup confirmed zero owned claims, managed resources, holds, backing files, and VMs. Owned services stopped. The mount and loop were removed. All64 case-artifact file hashes matched before/after cleanup and after export; the complete retained export has71 matching regular files and preserves two fixture symlinks. The original checkout's716 file contents/permissions and three preexisting process generations were unchanged. No tools, audit records, logs, runtime tokens, or evidence directories are in this PR. Raw ownership/fixture files remain private because they contain credentials. This run proves a normal filesystem remount, not host reboot or abrupt power loss. MV-14 seeds quarantine state and refuses one exact runtime removal command; it does not inject a real firewall or failing fsync. Source tests cover persistence-failure boundaries with fake engines. Arbitrary external destruction of a claimed VM and every concurrent scheduling branch were not independently qualified. Host path guards assume trusted directories and do not defeat bind-mount aliases or concurrent malicious pathname swaps. One extra-check attempt initially used an invalid unknown-volume name and received the expected validation400. The test control was shortened to a valid name before the successful 404 comparison. No product code changed, and this failed control was not counted as passing proof. |
Preserve registry checks and native device identities across Linux and Darwin. Use the pinned CI toolchain to verify the full lint matrix.
CI repair and requalificationThe first sandboxd CI run failed on introduced lint findings, not infrastructure. CI uses Go 1.27.1, which can run the pinned golangci-lint 2.13.2. My earlier local Go 1.27.2 decoder failure had prevented that check from completing. Matching the CI toolchain reproduced 58 findings; later modules exposed 9 SDK findings and 2 e2e findings. Commit Local checksGOTOOLCHAIN=go1.27.1 GOWORK=off make go-lintAll five modules passed Linux and Darwin lint, ten runs with 0 issues, using the existing repository configuration. The run also passed each module's formatter check. A local SA4023 analyzer panic disappeared with a fresh isolated analysis cache on identical source, and the complete matrix then passed. All five Go modules passed Real VM proof on the repaired head
The #300 scope is unchanged. Hibernate/wake, online grow, managed node placement, archive export, live managed attachment changes, and Kubernetes integration remain follow-ups. This proof covers normal remount, not host reboot or power loss. Quarantine testing still uses seeded durable state and an exact Remove refusal, not real firewall/fsync failure. Tools, audit records, private runtime configs, and logs remain outside Git. GitHub CI is green for this exact repaired head. sandboxd and Python both passed. The sandboxd lint, Go tests, PostgreSQL meta-store tests, and Caddy TLS step all completed successfully. The watcher reports READY with merge state CLEAN. The PR remains open for review. |
Bounded formal verification on the current headQualified product revision The TLA+/TLC model checks four separate obligations for one fixed managed backing:
The startup gate uses observable inventory rather than knowledge of every physically live VM. Requested and effective modes are separate state. Registry publication, sticky write faults, process death, inventory absence, uncertain attach effects, retirement and daemon restart are separate transitions. Results
The new Go probes read Separate nonauthor reviewers checked model/source correspondence, the Go probes and the entry. An independent cold invocation completed all 14 planned checks on the same frozen artifacts. The model and harness themselves are retained outside Git, so these are local qualification results rather than a new repository CI gate. Artifact SHA-256 values:
LimitsThis is bounded model checking plus actual-code falsification, not a machine-checked refinement proof of Go. It assumes one registry writer, sound process-generation proof, complete old-or-new snapshots under daemon crashes, and trusted local tools. Claimant lifetimes are not recycled. Arbitrary scale, liveness, delete/recreate identity, multiple backings and aliases, catalog live changes, warm-specific branches, runtime/hypervisor internals, host reboot and power loss are not established by this formal run. The earlier physical-VM/remount evidence remains separate, and the partial #300 scope is unchanged. |
Why
Related to #300. Let a tenant keep its data disk when a claim is released or replaced with a new template.
This adds the node-local managed-volume part of that plan on top of #339.
What changed
Scope
Blast Radius
Managed storage is opt-in and requires a private persistent directory. Enabling it requires explicit management credentials for volume discovery.
Mixed catalog and managed claims use durable admission and teardown. Uncertain VM removal keeps the volume busy until recovery confirms removal.
Verification
Scope and original case matrix. CI repair and current-head verification. Bounded formal verification.