# Buildbox Scratch Bind Fleet

audience: AI coding agents first.

# Outcome
status: DONE
task IDs: #5 DONE; #7 DONE security hardening
source request: Main thread MUST build one safe script usable across Debian1/2/3 together. Configure recommended scratch-backed bind mounts. Preserve WIP. No subagents. Owner correction: bind mounts MUST NOT reuse broad `buildbox harden` or reboot. Dedicated live migration MUST stop writers, copy, update `/etc/fstab`, activate mounts, verify data/services, restart writers, and validate reboot persistence through `findmnt --verify` only. After migration, explicitly verify Debian2 and Debian3 mounts, root/scratch free space, and runner services. Owner delivery correction: after a rebase whose only resolved conflict is documentation, verify that conflict and changed-file scope; do not rerun an already-green unrelated full code suite. Owner correction on 2026-08-14: autonomy over Git and infrastructure mechanics applies within the requested work; it does not authorize silently expanding an unrelated disk-guard request into buildbox implementation. Report the security findings to the session that owns the buildbox change and leave repair/delivery to that session.
security review handoff: Reject unsafe runner basenames before any `/etc/fstab` line generation in both host-config paths. Replace predictable remote payload path with owner-only `mktemp` + guaranteed cleanup before privileged install. Preserve the isolated uncommitted candidate only as optional handoff material; do not install, land, deploy, or continue testing it in this session.

## Acceptance criteria

- Add one fleet command using canonical registry; default all reachable hosts.
- Preflight all hosts before first mutation.
- Apply hosts sequentially; stop on first failure.
- Use dedicated live mount migration. MUST NOT invoke `buildbox harden`, alter unrelated host config, arm reboot machinery, or reboot.
- Stop exact writer services, migrate with recoverable stashes, update and verify `/etc/fstab`, mount live, prove path completeness, then restart exact prior-active services.
- Add `~/runs`, `~/.cache/ms-playwright`, and discovered runner `_work` binds; keep durable state on root.
- Preserve source data through `<target>.migrated`; verify mount and path completeness.
- Make rerun idempotent.
- Install live, invoke real entrypoint, verify three hosts, then land/deploy.
- Runner directory basename MUST match `^actions-runner-[A-Za-z0-9._-]+$` before interpolation into `/etc/fstab`; whitespace/control characters MUST fail before mutation.
- Remote payload staging MUST use owner-only unpredictable `mktemp`, quote every path, clean on success/failure/signal, and pass `--` to privileged `install`.
- Existing focused tests MUST prove malicious names cannot add fields/lines and predictable-path replacement cannot occur.
- `buildbox-scratch-bind` MUST be declared in canonical `deckctl sync apply bin` ownership so standard deployments retain its PATH link to landed deploy source.

## Preserved WIP

- Existing stale-run cleanup workflow may touch `~/runs`; migration MUST preserve remaining blocked runs.
- Debian1 partial state from interrupted broad harden is preserved: `/home/user/runs`, Playwright cache, and several runner `_work` mounts migrated; deadman armed; migration stopped before reboot. Recover and verify this state before new mutations.
- Existing Projects and `.claude` untouched.
- Runner credentials/config remain on root.
- Security correction worktree: `/home/user/Projects/overdeck/.worktrees/scratch-bind-security`; branch `wt/scratch-bind-security`; base `origin/main` at `37700a9d8`.
- Existing live migrations and unrelated worktrees remain untouched until focused security tests pass.

## Constraints

- Main thread only. NEVER dispatch implementation or review subagents.
- Root changes only through canonical `buildbox harden`/`deck-sudo` paths.
- Headless-host safety: preflight fleet, mutate sequentially, stop on first failure.
- Read `docs/specs/2026-08-13-buildbox-scratch-bind-fleet-design.md` as design source.
- Factory behavior change requires `python3 -m pytest modules/harness/factory/tests/ -q` only if factory machinery touched; this task MUST avoid it.
- Treat runner names and remote temp paths as hostile local input at root boundaries. Fail before `/etc/fstab`, `sudo install`, mount, or service mutation.
- Preserve current live state until candidate tests pass; install only exact changed scripts with rollback.

## Execution steps

1. Recover Debian1 interrupted broad harden: inspect exact mounts/stashes/services/deadman; disarm safely; NEVER reboot.
2. Extract reusable mount-only migration into dedicated root payload; broad harden may call same payload, but fleet wrapper MUST call payload directly.
3. Add fleet preflight/apply wrapper and command surface.
4. Add focused tests for preflight barrier, live migration, service restoration, sequential execution, stop-on-failure, idempotency, and explicit no-harden/no-reboot contract.
4. Run syntax and focused module tests.
5. Wait for stale-run preservation/removal workflow; resolve overlap.
6. Install and execute real fleet entrypoint sequentially.
7. Verify mounts, source devices, free space, services, and rerun.
8. Commit, land, deploy.
9. Add RED cases to existing tests for whitespace/control-character runner names and predictable remote temp-path attacks.
10. Validate runner basename at shared trust boundary before fstab output in both code paths.
11. Stage remote payload through quoted owner-only `mktemp`; trap cleanup; privileged `install --` only.
12. Run focused security tests, syntax/static checks, and existing buildbox suite once.
13. Install exact scripts with rollback, prove real installed entrypoint, then land/deploy without owner questions.
14. Register `buildbox-scratch-bind` in canonical bin sync, prove two consecutive sync applies retain its landed deploy-source link, deploy, and re-prove the real PATH entrypoint.

## Current receipt

- 2026-08-14: Added dedicated `host-config/scratch-bind.sh`; fleet wrapper no longer invokes broad hardening, deadman, or reboot paths.
- 2026-08-14: Focused tests pass. Full buildbox suite passes: 20 total, 19 run, 19 passed, 0 failed, 1 rebooting dangerlab test intentionally not run.
- 2026-08-14: Debian1 preflight passed with 9,972,994,048 bytes pending and 80,482,209,792 bytes available. Dedicated live reconciliation is copying remaining data; Debian2/3 remain untouched.
- 2026-08-14: Installed `systemd-container` on Debian2/3 because routine persistence simulation was unavailable. `systemd-nspawn` mount-namespace simulations successfully mounted every declared scratch bind from a copied fstab on both hosts; no physical reboot occurred.
- 2026-08-14: Debian2 verified: fstab valid; 9/9 intended mounts active from scratch; root 33 GiB free (69% used); scratch 20 GiB free (82% used); all 4 enabled system runners and all 3 enabled user runners active; zero failed system services. Two pre-existing desktop portal user services are failed and unrelated to mount migration.
- 2026-08-14: Debian3 verified: fstab valid; 3/3 intended mounts active from scratch; root 19 GiB free (83% used); scratch 36 GiB free (68% used); its one enabled user runner active; no system runners declared; zero failed system services. Two pre-existing desktop portal user services are failed and unrelated to mount migration.

- 2026-08-14: Landed on `origin/main`. Standard deployment refused because the shared deploy clone contains another session's preserved deletions; they were not altered. Installed `~/.local/bin/buildbox-scratch-bind` as a source link to the clean landed worktree and proved the real PATH entrypoint against Debian1: zero pending bytes, already converged, all 12 intended mounts verified, root 40 GiB free, scratch 64 GiB free.
- 2026-08-14 security review handoff: automated review identified two `/etc/fstab` injection paths from unvalidated runner basenames and one predictable remote temp-file replacement path. This session incorrectly expanded the unrelated disk-guard request into implementation. Owner corrected scope. The broad buildbox test process and independent review were stopped; no runtime mutation, install, commit, land, or deploy occurred. Uncommitted candidate and regressions remain preserved in `/home/user/Projects/overdeck/.worktrees/scratch-bind-security` for the owning buildbox session to inspect or ignore.

- 2026-08-14 security hardening: owning session independently confirmed all three findings. Hardened exact scripts installed through the real PATH entrypoint and verified sequentially on Debian1/2/3; all hosts had zero pending bytes and remained converged. Full suite: 21 total, 20 run, 20 passed, 0 failed, 1 rebooting dangerlab test intentionally not run.
- 2026-08-14 deployment durability: `buildbox-scratch-bind` is now canonically owned by `deckctl sync apply bin` through landed deploy source. Focused sync suite passed 71/71; two consecutive installed sync applies retained the executable PATH link and hardened `mktemp` implementation. Standard deployment completed. Installed Debian1 run proved zero pending bytes, all 12 intended binds converged, root 38 GiB free (64% used), and scratch 65 GiB free (41% used). No reboot occurred.

## Next executable action

None. Migration, security hardening, durable installation, landing, deployment, and installed proof are complete.
