Skip to content

feat(sandbox): add operator-only persistent pins - #46

Merged
jmgilman merged 2 commits into
masterfrom
feat/45-pinned-sandboxes
Sep 16, 2026
Merged

jmgilman merged 2 commits into
masterfrom
feat/45-pinned-sandboxes

Conversation

@jmgilman

@jmgilman jmgilman commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Closes #45.

Changes

  • Add sandbox.pin_identities (empty by default), sandbox.create(pinned=True), and sandbox.pin(name, pinned) with exact authenticated-subject authorization.
  • Persist pin state, operator attribution, and timestamp in Incus project config and Lume sidecars. Re-pinning is idempotent; unpinning clears attribution without extending expiry.
  • Skip pins on every reaper scan and after the mutation-gate reread; emit INFO attribution logs. Pinned instances and screenshots remain usable after TTL expiry. Explicit deletion clears pins so failed resource cleanup remains retryable.
  • Expose pinned/pinned_by on create/get/list/pin, document TTL semantics and recipe-only reusable images, and regenerate interface mocks.

Companion operator documentation: GilmanLab/root#38 (documentation CI passed).

Verification

  • mise exec -- go test -race ./... passed.
  • mise exec -- golangci-lint fmt and golangci-lint run ./... --show-stats=false passed.
  • Backend restart/persistence, allowlist/disabled policy, repeated reaper logging, under-gate reread, unpin cleanup, partial delete retry, and screenshot retention regressions passed.
  • mise exec -- moon run docs:build root:smoke passed; companion central documentation build passed.
  • Bounded review: authentication boundary, mutation gates, TTL consumers, persistence, and delete/reaper interaction. Added the expired-pinned screenshot path and the existing-sandbox pin transition regression. Parallel subagents could not run because their provider returned HTTP 403; implementation/review proceeded inline.
  • GitHub checks passed on fe7b62c: CI, GitHub Pages build, and Images Validate. Release/deployment jobs were skipped by workflow conditions.

Live acceptance — agentcompute01, 2026-09-16 UTC

Ran a temporary candidate HTTP service using the existing service identity and Incus/Lume backends, with explicit operator approval. Managed production binary/config were not replaced.

  • At 17:10:05–07, created pin45-incus and pin45-mac with ttl_minutes=1, pinned=True; create/get/list returned pinned: true, pinned_by: "omp". Incus contained a running Linux probe; the Mac fixture exercised the live sidecar without launching a Mac VM.
  • Expiry was 17:11:05/07. Both remained present at 17:12:39 after three expired 30-second scans; the Linux probe still executed printf pin45-alive successfully.
  • Unauthorized ordinary identity: create-pinned, pin, and unpin returned exactly capability failed: pinning requires an operator identity; before/after get results were identical and no denied sandbox was created.
  • Restart at 17:12:41 rediscovered both expired pins with unchanged expiry and attribution.
  • Restart with pin_identities: [] rejected the operator's create-pinned and unpin calls while preserving existing pins.
  • After restoring the allowlist, unpin triggered deletion on the next scan; both sandboxes, including the Linux instance, were absent after 32.46 seconds (scan interval plus backend teardown). The preexisting sandbox set was preserved.
  • At 17:13:34, the original service was restored and its binary/config SHA-256 values matched their pre-check values.
  • Focused existing-sandbox transition check, 17:14:34–17:16:05: created unpinned Incus/Mac sandboxes, pinned each via sandbox.pin(..., pinned=True), waited past TTL, then unpinned. Both were absent within 20.44 seconds, under one reaper interval. The original service and unchanged binary/config hashes were verified again.

Three consecutive expired-scan log excerpts:

time=2026-09-16T17:11:34.944Z level=INFO msg="sandbox pinned; expires_at ignored" sandbox=pin45-incus pinned_by=omp since=2026-09-16T17:10:05.722Z
time=2026-09-16T17:12:04.952Z level=INFO msg="sandbox pinned; expires_at ignored" sandbox=pin45-incus pinned_by=omp since=2026-09-16T17:10:05.722Z
time=2026-09-16T17:12:34.965Z level=INFO msg="sandbox pinned; expires_at ignored" sandbox=pin45-incus pinned_by=omp since=2026-09-16T17:10:05.722Z
time=2026-09-16T17:11:34.944Z level=INFO msg="sandbox pinned; expires_at ignored" sandbox=pin45-mac pinned_by=omp since=2026-09-16T17:10:07.477Z
time=2026-09-16T17:12:04.952Z level=INFO msg="sandbox pinned; expires_at ignored" sandbox=pin45-mac pinned_by=omp since=2026-09-16T17:10:07.477Z
time=2026-09-16T17:12:34.965Z level=INFO msg="sandbox pinned; expires_at ignored" sandbox=pin45-mac pinned_by=omp since=2026-09-16T17:10:07.477Z

Verifier note: an earlier cleanup observation using sandbox.list() raced with deletion between sandbox enumeration and instance enumeration. The successful run checked each fixture's disappearance via sandbox.get() instead. No production-code workaround was added.

Rollout

No production binary or managed configuration is replaced by this PR. After installing the release, enable sandbox.pin_identities: [omp] through the fleet-managed configuration. Existing pins stay effective if the allowlist is later emptied. Pins are not backups and do not promote sandbox-local images into the catalog.

@jmgilman

Copy link
Copy Markdown
Contributor Author

Live verification cleanup complete: original agentcompute.service is active; production binary/config hashes are unchanged. Disposable Incus/Mac fixtures are gone and the preexisting sandbox set was preserved. Removed the temporary candidate unit, test credentials/config, binaries, verification scripts, and screenshot scratch from agentcompute01, plus local throwaway files. Final head fe7b62c has passing CI, GitHub Pages build, and Images Validate checks; companion root#38 documentation CI also passed. PR is ready for review; nothing was merged or permanently deployed.

@jmgilman
jmgilman merged commit 544c0b3 into master Sep 16, 2026
7 checks passed
@jmgilman
jmgilman deleted the feat/45-pinned-sandboxes branch September 16, 2026 17:19
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.

Operator-only pinned sandboxes that the reaper skips

1 participant