Fleet P2b: node sandbox-readiness check (hardened workload on a node)
ci / gates (push) Failing after 5s
ci / rust (push) Has been skipped
ci / sandbox-k8s (push) Has been skipped
ci / frontend (push) Has been skipped
ci / e2e (push) Has been skipped

Proves a connected node can host hardened agent workloads end-to-end, without
touching the agent run loop (zero blast radius on existing agents).

- Daemon: typed `sb_check` op — pulls a tiny image and runs it fully locked down
  (cap-drop ALL, no-new-privileges, no network, read-only rootfs, non-root,
  memory/pids caps), then tears it down. Fixed command; nothing caller-supplied
  runs (preserves the exec-hardening invariant).
- cm-api: NodeHub.sandbox_check + POST /api/nodes/{id}/sandbox-check.
- UI: a shield "sandbox check" button on each online node card streams the
  result (✓ SANDBOX READY + container id/uname).

This validates the full provision→run→destroy mechanism on nodes. The remaining
P2 work — wiring real agent deploys to auto-place onto nodes — is its own
subsystem (a RemoteDriver reusing the local DockerDriver for security parity,
agent-image distribution to nodes, and node-routing in SandboxManager) and is
best done as a focused pass; it is intentionally NOT bundled here to keep the
core agent path untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-24 12:31:50 -07:00
co-authored by Claude Opus 4.8
parent f5f96508eb
commit 33aa9c0693
5 changed files with 87 additions and 2 deletions
+17
View File
@@ -117,6 +117,23 @@ pub async fn exec_test(
}
}
/// `POST /api/nodes/{id}/sandbox-check` — run a hardened throwaway container on
/// the node to confirm it can host agent workloads.
pub async fn sandbox_check(
State(state): State<AppState>,
Authed(user): Authed,
Path(id): Path<Uuid>,
) -> Result<Json<Value>, ApiError> {
let node_id = NodeId::from(id);
nodes::get(&state.pool, node_id, user.workspace_id)
.await?
.ok_or(ApiError::NotFound)?;
match state.node_hub.sandbox_check(node_id).await {
Ok(out) => Ok(Json(json!({ "ok": out.ok, "output": out.output }))),
Err(e) => Ok(Json(json!({ "ok": false, "output": e }))),
}
}
/// `DELETE /api/nodes/{id}` — deregister a node (workspace-scoped).
pub async fn remove(
State(state): State<AppState>,