Skip to content

Commit 021a21c

Browse files
committed
fix(agentenv): review hardening, SmolVM default, managed egress
Addresses the bot review findings that verified against current code. SmolVM is the default everywhere again; AgentENV is strictly opt-in via PRELOOP_VM_BACKEND. Auto-selection is gone along with the PATH probe it needed. Debug attach: claim() resumes the guest before writing ACTIVE or spawning the heartbeat, so a failed resume can no longer strand a ghost heartbeat; `preloop debug --export` holds the same attach guard as verdict, REPL, and shell instead of reaching suspended sandboxes unguarded. Provider lifecycle: start() records the sandbox id before readiness probes run so provisioning-error cleanup can actually delete the VM (with an ensure_volumes helper that also closes the resume path skipping unmaterialized volumes); fork() rejects duplicate clone names instead of orphaning; stop() cancels the TTL keepalive only on success; the keepalive retries transient failures before giving up; Restricted/DNS/zero-storage specs are rejected at create time instead of silently substituted. Pool: on-demand slots apply the packed-disabled fallback like warm slots, and a loopback PRELOOP_RUNNER_URL with the AgentENV backend fails pool startup with the fix (loopback is the guest itself there). The engine manages its own egress exception (crates/preloop-cli/src/ aenv_egress.rs): with the AgentENV backend selected, pool startup reconciles the surgical node deny-list complement and the ordered host firewall rules from the runner URL — verify first, mutate only on drift, restart `aenv` only on config change. Narrow sudoers scope, dry-run and opt-out modes, ENGINE_IP/CONFIG overrides, and unit-tested complement math; operator docs in docs/vm-substrates.md. Also corrects a sudoless-base test to assert the best-effort chown the wrapper actually emits (the /var/lib path it named exists nowhere in the codebase — flagged as a follow-up, not silently "fixed").
1 parent 0db191d commit 021a21c

8 files changed

Lines changed: 804 additions & 59 deletions

File tree

‎AGENTS.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ just dogfood # E2E with real runner
3636
- **Wire compatibility**: `/_apis/…` is the source of truth. Validate protocol changes against the **official runner**, not only unit tests.
3737
- **Broker path only**: all work targets the modern broker + Twirp results-service protocol (v2.329.0+).
3838
- **VM substrates**: two backends behind `preloop_vm::VmProvider` — SmolVM
39-
(libkrun; the only option on macOS/Apple Silicon) and AgentENV (Firecracker
40-
over `/dev/kvm`; the default on a Linux host that has `aenv`). Selected by
41-
`PRELOOP_VM_BACKEND`; capability differences are declared by
39+
(libkrun; the default everywhere) and AgentENV (Firecracker over `/dev/kvm`;
40+
opt-in via `PRELOOP_VM_BACKEND=agentenv`). Selected by `PRELOOP_VM_BACKEND`;
41+
capability differences are declared by
4242
`VmProvider::capabilities()` and the orchestrator branches on them. See
4343
`docs/vm-substrates.md` before touching pool or provider code.
4444
- **Store backends**: the `Store` trait (`store.rs`, async, object-safe) is the only surface the server sees; backends are SQLite (`store.rs`, default, `<state_dir>/preloop.db`) and Postgres (`store_pg.rs`), selected via `PRELOOP_STORE_URL` (`sqlite://<path>` / bare path / `postgres://…`). Both are single-writer: one connection behind a mutex. Two servers on the same SQLite file (or same PG database) still diverge in-memory — the DB is a restart source, not a shared bus.

‎CHANGELOG.md‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ Releases before v0.27.0 predate the changelog.
1111

1212
### Added
1313

14-
- **AgentENV is now a first-class VM substrate, and the default on KVM hosts.**
14+
- **AgentENV is now a first-class VM substrate, opt-in on KVM hosts.**
1515
`crates/preloop-vm/src/agentenv.rs` implements `VmProvider` over the `aenv`
16-
CLI (Firecracker + overlaybd + ublk), so any host with Linux ≥ 6.8,
17-
`/dev/kvm`, and `aenv` on `PATH` selects it automatically;
16+
CLI (Firecracker + overlaybd + ublk), selected with
17+
`PRELOOP_VM_BACKEND=agentenv` on a host with Linux ≥ 6.8 and `/dev/kvm`;
1818
`PRELOOP_VM_BACKEND=smolvm|agentenv` overrides, and an unrecognized value is
1919
a hard error rather than a silent fallback. macOS is unchanged (SmolVM is the
2020
only libkrun-capable runtime there). AgentENV is faster at every VM lifecycle
@@ -28,6 +28,12 @@ Releases before v0.27.0 predate the changelog.
2828
it instead of assuming: a backend whose packs are not host files no longer
2929
builds, downloads, or relocates an artifact — it prepares its golden directly
3030
from the base image and forks per job.
31+
- The engine manages its own AgentENV egress exception: with the AgentENV
32+
backend selected, pool startup reconciles the surgical node deny-list
33+
complement and the ordered host firewall rules from `PRELOOP_RUNNER_URL`
34+
(verify-first, mutate only on drift, restart `aenv` only on config change).
35+
Narrow `sudoers` scope, `PRELOOP_AENV_MANAGE_EGRESS=0|dry-run` escape hatches,
36+
and `PRELOOP_AENV_ENGINE_IP` override in `docs/vm-substrates.md`.
3137

3238
### Changed
3339

@@ -101,6 +107,19 @@ Releases before v0.27.0 predate the changelog.
101107
run no longer appends a duplicate deny-list complement, and the engine-port
102108
`ACCEPT` rules are re-inserted at the head of `INPUT` after the service
103109
re-adds its blanket veth `REJECT`.
110+
- Debug attach no longer strands a live heartbeat when the guest resume fails:
111+
`DebugAttach::claim` resumes before writing ACTIVE, and `preloop debug
112+
--export` holds the same attach guard as every other controller path.
113+
- The AgentENV provider no longer leaks sandboxes on partial startup (the id
114+
is recorded before readiness probes run), orphans clones on retried forks
115+
(duplicate names are rejected), or lets a failed pause cancel the TTL
116+
keepalive out from under a live session (cancelled only on success; transient
117+
keepalive failures retry). Unenforceable specs (`Restricted` network,
118+
per-sandbox `dns`, zero storage) are rejected at create time instead of
119+
silently substituted.
120+
- On-demand pool slots honor the packed-golden fallback like warm slots, and a
121+
loopback `PRELOOP_RUNNER_URL` with the AgentENV backend fails pool startup
122+
with the fix — loopback is the guest itself there, so every job would starve.
104123

105124
## [0.32.7] - 2026-09-10
106125

0 commit comments

Comments
 (0)