Skip to content

Add microVM isolation mode for project labs via Kata Containers#25

Merged
justsml merged 2 commits into
mainfrom
claude/sandbox-microvm-isolation-orb17k
Jul 16, 2026
Merged

Add microVM isolation mode for project labs via Kata Containers#25
justsml merged 2 commits into
mainfrom
claude/sandbox-microvm-isolation-orb17k

Conversation

@justsml

@justsml justsml commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Description

The project lab runtime (src/server/labs/) already provisions Kali lab containers with strong Linux-namespace hardening (--cap-drop ALL, no-new-privileges, resource limits, egress iptables). That still shares the host kernel with every lab, so a container escape lands on the host. This adds a microvm LabIsolationMode alongside the existing container mode: the lab container runs under a Kata Containers OCI runtime class (kata-fc by default, or kata-qemu/kata-clh), giving each lab its own guest kernel via hardware virtualization (Firecracker/QEMU/Cloud Hypervisor), on top of the existing capability/network/resource hardening.

  • hardening.ts: LabIsolationMode resolution helpers, Kata runtime class constants, and a safe-name validation regex.
  • docker-plan.ts: buildRunLabCommand adds --runtime <class> and exploit-hunter.isolation/exploit-hunter.microvm-runtime-class labels when microVM isolation is requested; a new buildMicrovmRuntimeCheckCommand builds a docker info availability check.
  • runtime.ts: ProjectLabRuntime.start() runs that availability check before provisioning and fails closed — throws MicrovmRuntimeUnavailableError rather than silently falling back to weaker container isolation — if the requested Kata runtime class isn't registered with the Docker daemon. Dry-run mode surfaces the check command in the plan without executing/enforcing it.
  • service.ts: threads isolation/microvmRuntimeClass through create/start/restart, mirroring the existing networkProfile pattern (per-request override, persisted in lab metadata for restarts).
  • types.ts: adds the LabIsolationMode type and isolation/microvmRuntimeClass fields to LabCreateInput/LabContainerOptions.
  • docs/lab-runtime-hardening.md: new "MicroVM Isolation (Kata Containers)" section covering enablement (per-lab input or PROJECT_LAB_ISOLATION_MODE/PROJECT_LAB_MICROVM_RUNTIME_CLASS env vars), fail-closed behavior, and host prerequisites (KVM/nested virtualization + a Kata Containers install) — cross-referenced from the existing Limitations section.

Default behavior is unchanged: isolation defaults to "container", so existing labs and callers are unaffected unless they opt in.

Note on testing environment: this sandbox has no /dev/kvm and no Kata Containers install, so I could not do an end-to-end run of an actual microVM lab. The change is scoped so the code paths are fully exercised via the existing dry-run/fake-runner test harness (see below); real end-to-end verification needs a KVM-capable Docker host with Kata Containers installed, per the new docs section.

Type of change

  • New feature (non-breaking change which adds functionality)
  • Documentation update

How Has This Been Tested?

  • pnpm typecheck — no new errors in touched files (ran targeted tsc --noEmit; this sandbox has pre-existing unrelated @types/node/native-module gaps from a constrained, script-skipped pnpm install)
  • pnpm test (targeted) — tests/security-chat/lab-runtime.test.ts, project-lab.test.ts, docker-lab-target.test.ts all pass (37 tests), including 7 new tests covering: default stays on container isolation with no --runtime flag, --runtime/labels added for microvm, custom runtime class override, unsafe runtime class name rejection, dry-run plan includes the availability check, fail-closed when the Docker daemon doesn't have the runtime registered, and successful start once it is registered.
  • Manual testing steps: not possible in this sandbox (no KVM/Kata install available); needs verification on a KVM-capable host with Kata Containers installed.

Checklist

  • My code follows the style guidelines of this project (see AGENTS.md)
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation (docs/lab-runtime-hardening.md)
  • I have added tests that prove my feature works
  • New and existing unit tests pass locally with my changes

Security Considerations

This directly affects sandboxing. The key security property is fail-closed: if microvm isolation is requested but the Docker daemon doesn't have the corresponding Kata runtime class registered, lab startup is refused with MicrovmRuntimeUnavailableError rather than silently running with weaker container isolation. Default behavior (container isolation) is unchanged, so this is opt-in and additive to the existing capability-drop/network/resource hardening — it does not replace or weaken any of it.


Generated by Claude Code

justsml commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

Pushed 5916346: fixes the Run Playwright tests CI failure (ConnectionFailed(...code: 14) opening .data/mastra.sqlite). Root cause is repo-wide, not specific to this PR — .data/ is gitignored so LibSQLStore can't open its default sqlite file's parent directory on a fresh checkout (reproduced this in isolation, and confirmed the same failure on the last several main runs too). createMastraStorageAdapter's libsql case now creates the parent directory for local file: URIs before constructing the store.


Generated by Claude Code

justsml commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

Update: the .data fix (5916346) worked as intended — all 1093 unit tests pass and Playwright's webServer now boots successfully (previously it never got that far in CI).

That unblocking revealed a separate, pre-existing failure underneath, unrelated to this PR's changes:

tests/playwright/docker-lab-idor-flow.spec.ts:478 › Docker lab IDOR browser eval flow ›
  shows approval rejection and approval-gated tool evidence through the browser

Error: expect(locator).toBeVisible() failed
Locator: getByRole('region', { name: 'Investigation Plan' }).first()

This fails consistently across all 3 attempts (2 configured CI retries). The test fully mocks its API calls via page.route() — it doesn't depend on real Docker or a real LLM — so this points to the chat UI's plan-panel (WorkspacePlanPanel/WorkspaceDashboard) not rendering from the mocked fixture data, not an infra/timing issue. (A second test, thread-scan-flow.spec.ts, failed once but passed on retry — flaky, not blocking.)

This is the first time this Playwright suite has run to completion in CI — every previous run failed earlier at the .data bootstrap step this PR fixes — so there's no prior green baseline to compare against; it may be a latent bug nobody has observed before. It's unrelated to the microVM isolation feature or the storage fix in this PR (no overlap in the code paths), so I'm not chasing it here — flagging it for separate follow-up investigation into the chat UI plan-panel rendering.


Generated by Claude Code

claude added 2 commits July 15, 2026 18:54
Adds a `microvm` LabIsolationMode alongside the existing container
boundary: docker-plan.ts registers a `--runtime <kata-class>` OCI
runtime and audit labels, service.ts threads the choice through
create/start/restart (persisted in lab metadata like networkProfile),
and runtime.ts fails closed with MicrovmRuntimeUnavailableError rather
than silently downgrading isolation when the requested Kata runtime
class is not registered with the Docker daemon. Documents setup
requirements and limitations in docs/lab-runtime-hardening.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYzvg68ntwkoSEQJ1NkC6a
LibSQLStore fails with ConnectionFailed(...code: 14) when the parent
directory of a local sqlite file (e.g. the default .data/mastra.sqlite,
which is gitignored) doesn't exist yet, breaking Playwright's webServer
boot on a fresh checkout/CI runner. Create the directory first for
file: URIs before constructing the store.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYzvg68ntwkoSEQJ1NkC6a
@justsml justsml force-pushed the claude/sandbox-microvm-isolation-orb17k branch from 5916346 to 8f6713e Compare July 16, 2026 00:55
@justsml justsml marked this pull request as ready for review July 16, 2026 01:00
@justsml justsml merged commit e37a770 into main Jul 16, 2026
7 checks passed
@justsml justsml deleted the claude/sandbox-microvm-isolation-orb17k branch July 16, 2026 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants