Repository navigation
Conversation
…eation An acq sandbox is ephemeral: only host bind mounts survive teardown, so recreating one loses $PASEO_HOME (config.json, the projects registry, and session/agent records) and leaves the on-disk worktrees orphaned in Paseo. Add two host-side helpers that snapshot Paseo state before teardown and replay it into the new sandbox afterward, using only acq exec stream I/O (no new acq capability, no persistent-volume primitive, no coupling of state to a project repo, no extra mount arg): - scripts/paseo-backup: streams a tar of $PASEO_HOME (minus paseo.pid) over acq exec stdout to a host tar; dry-run by default, atomic temp+rename, validates a non-empty tar before promoting. - scripts/paseo-restore: streams a backup tar back into $PASEO_HOME and bounces the daemon (marker-scoped kill) so it re-reads restored config/projects; with no tar given it auto-selects the newest backup for the sandbox. Worktrees stay pinned under the project mount (they already survive recreate); the restored $PASEO_HOME is what makes Paseo re-recognize them, and worktrees.root self-corrects on the next acq run. Includes an ADR (rationale over persistent volumes, in/out of the snapshot, the worktree analysis, the filename contract), README + TROUBLESHOOTING sections, and verify assertions (offline dry-run/newest-select/name-guard checks + a live backup->clear->restore round-trip under RUN_ACQ). Co-authored-by: OpenCode Agent <agent@gsa.gov>
Live testing on an acq (msb) sandbox surfaced four issues in the initial
backup/restore scripts; fix all so the round-trip actually works end-to-end:
- Transport as base64 TEXT, not raw binary. acq exec hangs on a raw binary
stdin/stdout stream (guest never sees EOF); text reaches EOF normally. The
guest encodes on backup and the host encodes on restore, keeping the payload
in acq's safe text channel on both the msb and sbx backends (no reliance on
msb-specific --stream).
- Snapshot-in-guest then classify tar's exit code. The daemon writes under
$PASEO_HOME while tar reads it, so GNU tar exits 1/2 ("file changed as we read
it") while still producing a usable archive; only rc>=3 is a real failure.
- Clear the stale PID lock on the restore daemon bounce. Killing the daemon
leaves paseo.pid behind, which Paseo only treats as stale after 5 minutes, so
the 5s supervisor respawn loops forever on "Failed to acquire PID lock". The
bounce now waits for exit and removes paseo.pid so the relaunch is clean.
- Exclude re-downloadable/identity files so the archive stays small: models/
(a ~460MB speech-model cache that made base64 crawl), server-id, plus the
existing daemon.log / paseo.pid / daemon-keypair.json / cli-client-id.
- Also fix a BSD (macOS) mktemp incompatibility: templates must end in the
XXXXXX run, so the host-side temp names were reordered.
Verified live: backup produces a small valid tar with the right exclusions,
restore round-trips a sentinel back into $PASEO_HOME, and the daemon returns
HEALTH-OK after the bounce. Offline verify gate and unsafe-shell scan pass.
Co-authored-by: OpenCode Agent <agent@gsa.gov>
The verify script was tracked as 100644, so a fresh checkout had to chmod +x it before RUN_ACQ=1 would run. Set the executable bit to match paseo-backup / paseo-restore. Co-authored-by: OpenCode Agent <agent@gsa.gov>
Exclude rotated daemon logs from new backups, filter legacy backup logs before restore transport, and swap restored state into place only after a complete extract. Co-authored-by: OpenCode Agent <agent@gsa.gov>
mogul
marked this pull request as draft
September 21, 2026 17:09
Contributor
Author
|
Converted back to draft while I consider a lower-level acq subcommand for handling backup/restore in the general case... I'm likely to close this one if I make progress on that one. |
Contributor
Author
|
Confirmed: I'm closing this in favor of GSA-TTS/agentic-coding-quickstart#507 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Recreating an
acqsandbox loses all Paseo state:$PASEO_HOME(config.json,the projects registry, session/agent records, schedules) lives on the ephemeral
guest filesystem, so
acq rm+ a fresh create drops it — and the on-disk gitworktrees (which persist on the host mount) show up orphaned because Paseo no
longer has their records.
This adds two host-side helper scripts that snapshot Paseo state to the host
before teardown and replay it into a new sandbox afterward, using only
acq exec(no newacqcapability, no persistent-volume primitive, no couplingof state to a project repo, no extra mount argument):
scripts/paseo-backup [--apply] <sandbox> [dest-dir]— snapshots$PASEO_HOMEto~/.acq-paseo-backups/paseo-state-<sandbox>-<UTC>.tar.scripts/paseo-restore [--apply] <sandbox> [backup-tar]— replays a snapshotand restarts the daemon; with no tar it auto-selects the newest backup for the
sandbox.
Both default to dry-run;
--applyis required to write or mutate anything.Why backup/restore (not a persistent volume)
Three storage approaches were considered. Relocating
$PASEO_HOMEonto theproject mount couples state to a repo dir; a dedicated state mount forces an
extra
acq runargument; and thehybrid/v1volumesprimitive iscreation-time/unseeded/per-sandbox and cannot provide cross-recreate persistence
without an upstream
acq-side change. Backup/restore needs none of that andworks today. Full rationale in
integrations/isolation/acq-kits/paseo/docs/decisions/backup-restore-paseo-state.md.Worktrees
Worktrees stay pinned under
<project>/.paseo-worktrees(a host mount), so theirfiles already survive recreation; restoring
$PASEO_HOMEis what makes Paseore-recognize them.
worktrees.rootself-corrects on the nextacq run.What the snapshot excludes
paseo.pid(PID lock),daemon.log(live, churning log),daemon-keypair.json/
cli-client-id/server-id(per-install identity), andmodels/(re-downloadable caches — a speech model alone is ~460 MB).
Live testing found and fixed four bugs
Initial versions passed offline but broke on a real acq (msb) sandbox. Each was
diagnosed live and fixed:
acq exechangs on raw binary stdin/stdout (guest never sees EOF) →transport the tar as base64 text both directions. Backend-agnostic (msb
and sbx); no reliance on msb-specific
--stream.tarexits 1/2 on live-file churn while still producing a valid archive →snapshot in-guest, then classify tar's exit code (only
>=3aborts).paseo.pidcaused a 5s crash-loop after the restore bounce(
Failed to acquire PID lock; Paseo treats the lock as stale only after 5min) → the bounce waits for the daemon to exit, then removes
paseo.pidsothe supervisor relaunch is clean.
models/cache made base64 crawl → exclude it (andserver-id).Also fixed a BSD/macOS
mktempincompatibility (templates must end in theXXXXXXrun) and markedscripts/verifyexecutable.Verification
RUN_ACQ=1 scripts/verify— all checks passed on a real msb sandbox,including new step 11 (backup/restore round-trip): valid tar excluding
paseo.pid, sentinel round-tripped into$PASEO_HOME, daemon healed after therestore-triggered bounce. Offline gate (
scripts/verify) and the repo'sunsafe-shell scan (
scripts/scan_unsafe_shell.py) also pass.RUN_ACQ=1 step 11 transcript
Rollback
Revert this PR. The scripts are additive host-side tooling; removing them has no
effect on the kit's runtime behavior. Existing backups under
~/.acq-paseo-backupsare plain tars and can be deleted at will.Security impact
No change to auth/authz or attack surface. Scripts are dry-run by default,
read-only against the sandbox on backup, constrain the sandbox-name argument to
a safe charset (SI-10), and transport data only over the existing
acq execchannel to the user's own sandbox.
AI assistance
Authored with AI assistance (OpenCode). The four live-testing fixes were
diagnosed interactively against a real sandbox with the maintainer.