Address the codex review on #398:
- P1: inject_guest_boot no longer follows a symlink at bb-init/bb-dropbear.
A committed snapshot is guest-controlled and could plant those paths as
symlinks aimed at a host file (e.g. bb-init -> ~/.bashrc); write_text /
copy2 would then overwrite the target as the host user during resume.
Replace any pre-existing entry and create the files with
O_EXCL|O_NOFOLLOW so the write stays inside the staging tree.
- P2: write the snapshot tar owner-only (0600). It can contain the bottle's
private workspace; it was being created world-readable (0644).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoEZHDjv84ChoZbozQERhJ
The last host-Docker dependency in the Firecracker launch path. Freeze
and resume no longer touch the docker daemon, so the backend needs
firecracker + KVM only — completing #348.
Freeze: stream the guest rootfs over SSH straight into a persistent
committed-rootfs.tar (the resumable/migratable artifact) instead of
round-tripping through `docker build` from a scratch image. Written to
a .partial sibling and atomically renamed so a failed freeze leaves no
truncated artifact.
Resume: extract the snapshot tar into a cached base dir and feed it to
the existing rootless `mke2fs -d` pipeline, replacing the
`docker create` + `docker export | tar` path. Recreate the
proc/sys/dev/run mount points the freezer excludes so the guest init
can mount them.
`util.build_base_rootfs_dir` / `docker_image_id` stay — they still back
the opt-in BOT_BOTTLE_INFRA_BUILD=local dev path and off-host
publish_infra, which are out of scope.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoEZHDjv84ChoZbozQERhJ
The orchestrator/gateway VM is trusted infra, not an isolated agent: it
builds agent images in-VM (buildah must FROM-pull + apt/npm) and, in the
Stage B cutover, forwards agent egress upstream. Give it a dedicated TAP
(`bborch0`) on a /31 at the top of the IP_BASE /16 (clear of the bbfc*
agent pool at the bottom), NAT'd out the host uplink — while agent VMs
keep their fail-closed, gateway-only isolation table.
- netpool.defaults.env / netpool.py: new BOT_BOTTLE_FC_ORCH_IFACE +
`orch_slot()` (index -1 sentinel; host x.y.255.0 / guest x.y.255.1).
- scripts/firecracker-netpool.sh: create + address the orchestrator TAP;
`bot_bottle_fc_nat` table masquerades its /31 out the uplink and
accepts its forward path. Because bootstrap still runs Docker (whose
FORWARD policy is DROP), a best-effort, guarded, idempotent DOCKER-USER
ACCEPT is added too (skipped once Docker is gone). down/status updated.
- nix/firecracker-netpool.nix: mirror the option, pass it via the unit
Environment= (the store-copied script can't read the defaults file),
and add iptables to the unit path for the DOCKER-USER step.
Agent isolation is unchanged: the new rules only ever accept/masquerade
the orchestrator link and never drop, so they can't weaken the bbfc*
drops. Applied by re-running `sudo ./scripts/firecracker-netpool.sh up`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
Cleanup orphan-enumeration + removal, the backend setup/status/teardown/
cleanup classmethod delegates, guest-IP parsing, the unprivileged nft/TAP
probes, console-tail, and require_firecracker preflight branches. Lifts
the unit-only diff-coverage on the firecracker backend substantially;
the deep VM boot/SSH orchestration remains integration-covered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck