# hack-house → VirtualBox Sandbox Backend — Spec > **Status:** Draft v1 · **Date:** 2026-06-03 > **Scope:** Add VirtualBox as a sandbox backend, in two complementary modes: > **(A)** a headless, owner-hosted VM driven through the existing shared PTY > (drops into the current `Backend` abstraction), and **(B)** a *portable VM > appliance* the room can hand out so each member boots the **actual GUI locally** > on their own machine — including detecting and (with consent) installing > VirtualBox if it's missing. > **Baseline reviewed:** `hh/src/sbx.rs`, `hh/src/app.rs` @ `feat/ai-context`. --- ## 0. Decisions to lock | # | Decision | Proposal | |---|----------|----------| | A | VirtualBox transport into the guest | **SSH** (NAT port-forward) as primary; `VBoxManage guestcontrol` as a no-SSH fallback. SSH gives a clean PTY and reuses the multipass provisioning model verbatim. | | B | Single shared instance vs. per-user local copies | **Both, as two modes.** Mode A = one owner-hosted headless VM, shared PTY (zero-knowledge preserved). Mode B = export the VM as an `.ova`, distribute over the *existing* `/send` channel, each member imports + launches the GUI locally. | | C | GUI sharing | **No live framebuffer relay.** Sharing the *desktop* = sharing the *appliance*, not the pixels. Sidesteps the zero-knowledge problem entirely (the image rides the encrypted file transfer). | | D | Installation | **Detect-first, then opt-in install.** `ensure-vbox.sh` mirrors `ensure-docker.sh`: never installs silently; prints what it would do and requires an explicit `--yes` (or the `/sbx ... --install` flag). | --- ## 1. Why VirtualBox, and what's genuinely new The existing backends (`Backend::{Local,Docker,Multipass}` in `hh/src/sbx.rs:51`) are all **headless and text-only**. The owner hosts the box and runs a local PTY into it (`command_for`, `sbx.rs:278`); the PTY bytes are encrypted with the room key and relayed as `_sbx` frames, so the server only ever sees ciphertext. VirtualBox adds two things the others can't: 1. **Arbitrary guest OSes** — Windows, BSD, old kernels, purpose-built malware-analysis or CTF images — with a mature snapshot tree. 2. **A real graphical desktop.** This is the part that doesn't fit the PTY relay, and it's the part you explicitly want: *people share a VM and each launches it locally with the GUI.* So the integration is deliberately split so each mode keeps the project's trust model intact: - **Mode A (shared shell):** one VM, owner-hosted, driven collaboratively through the shared PTY — identical trust story to multipass. - **Mode B (shared appliance):** the VM *image* is the shared artifact. It travels over the existing E2E `/send` transfer; each member runs their **own local copy** in the VirtualBox GUI. No pixels cross the wire — only the (encrypted) disk image. --- ## 2. Mode A — headless VirtualBox as a 4th backend ### 2.1 Enum + labels (`hh/src/sbx.rs`) ```rust pub enum Backend { Local, Docker, Multipass, VirtualBox } // new variant ``` - `Backend::parse`: add `"virtualbox" | "vbox" => Some(Backend::VirtualBox)`. - `label()`: `"virtualbox"`. - `default_image()`: a named base appliance, e.g. `"hh-base"` (an Ubuntu image we pre-register), since VirtualBox has no "pull by release string" like multipass. ### 2.2 Mapping every existing fn to `VBoxManage` Each function in `sbx.rs` gets one new match arm. The transport (how a command reaches *inside* the guest) is SSH over a NAT port-forward. | Fn (`sbx.rs`) | Multipass today | VirtualBox arm | |---|---|---| | `prepare` (`:86`) | `multipass launch` | import appliance if absent (`VBoxManage import --vsys 0 --vmname `), set forward (`modifyvm --natpf1 "ssh,tcp,127.0.0.1,,,22"`), then `startvm --type headless`; if it already exists just `startvm`. Idempotent like the multipass arm. | | `command_for` (`:278`) | `multipass exec … bash` | `ssh -tt -p -o StrictHostKeyChecking=no @127.0.0.1` (login shell). `run_user` empty ⇒ default account. | | `provision` (`:355`) | `useradd` via `mp()` | identical `useradd`/sudoers scripts, run through an SSH helper `vbx()` (mirrors `mp()`/`dk()` at `sbx.rs:319`). Owner gets passwordless sudo via the same `mp_grant_sudo` script. | | `set_sudo` (`:387`) | sudoers drop-in | same script over SSH; gate on `backend == VirtualBox`. | | `save_state` (`:208`) | `multipass snapshot` | `VBoxManage snapshot take