# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview vmsilo is a lightweight virtualization system inspired by Qubes OS. It runs isolated VMs using crosvm (Chrome OS VMM) with different security domains (banking, shopping, untrusted, etc.). VMs are configured declaratively via a NixOS module. ## Environment You are running under NixOS. If you need any tools not in the environment, use nix-shell. ## Development Rules - Update CLAUDE.md along with code. Keep this concise. *Only add information that is not obvious and can not be easily looked up in code.* - Always update README.md for any user-visible changes (NixOS options, CLI flags, etc). - There are no tests for the Nix modules. - `nix build` uses the git index for source filtering. New untracked files must be `git add`'d before `nix build` will see them. ## Build Commands ```bash # Build the default rootfs image nix build .# ``` ## Code Style This project uses treefmt-nix with nixfmt for formatting. Run before committing: ```bash nix fmt ``` ## Architecture ### VM Launch Flow (NixOS module) VMs run as system services (root) for PCI passthrough and sandboxing support. crosvm drops privileges before starting the guest. VMs start automatically when first accessed via socket activation: 1. `vm-run banking firefox` connects to `/run/vmsilo/banking/command.socket` 2. Socket activation triggers `vmsilo-banking@.service` (proxy template) 3. Proxy requires `vmsilo-banking-vm.service`, which starts crosvm 4. Proxy waits for guest vsock:5000, then forwards command The configured user can manage VM services via polkit (no sudo required for `vm-start`/`vm-stop`). ### Key Files **NixOS module** (`modules/`): Split across many files — there is no single `config.nix`. - `default.nix` — entry point (imports all submodules) - `options.nix` — option declarations (`programs.vmsilo`; includes `vm..hypervisor` for VMM selection: `crosvm` or `cloud-hypervisor`) - `scripts.nix` — VM launcher scripts (crosvm/cloud-hypervisor) and user-facing CLI scripts (`vm-run`, `vm-start`, `vm-stop`, `vm-shell`) - `services.nix` — systemd units (VM service, proxy, console relay, session bind, GPU device backend, wayland-seccontext) - `usb.nix` — `vm-usb` CLI script (USB passthrough via usbip-rs) - `desktop.nix` — .desktop file generation and icon copying - `networking.nix` — TAP interfaces, host nftables for `netvm = "host"`, guest network config - `netvm.nix` — auto VM-to-VM and VM-to-host network links - `pci.nix` — VFIO PCI passthrough setup - `overlay.nix` — root overlay (raw/tmpfs) setup - `assertions.nix` — validation checks - `package.nix` — final package derivation - `lib/helpers.nix` — crosvm argument building helpers - `css-colors.nix` — CSS named color lookup table **Guest rootfs** (`rootfs-nixos/`): - `default.nix` — NixOS-based rootfs builder (outputs `{ erofs, kernel, initrd }`) - `configuration.nix` — entry point (imports all guest submodules) - `guest/boot.nix` — boot config, initrd, overlayfs root - `guest/users.nix` — user accounts, sudo, SSH - `guest/networking.nix` — network config from kernel params - `guest/wayland.nix` — wayland proxy and session setup - `guest/command.nix` — vsock command listener, dbus proxy, idle watchdog - `guest/system.nix` — packages, graphics, fonts, misc - `guest/kernel-param-helper.nix` — shared shell helper for parsing kernel cmdline - `guest/usb.nix` — usbip-rs client listener service, vhci_hcd module - Self-contained erofs image with overlayfs root; no host /nix sharing **Rust crates:** - `vmsilo-balloond/` — dynamic balloon memory management daemon (equalizes host/guest free memory via virtio-balloon; run `--help` for CLI options) - `vmsilo-dbus-proxy/` — D-Bus proxy for system tray and notification forwarding between guest and host over vsock:5001 - `vmsilo-wayland-seccontext/` — creates Wayland security context socket (wp_security_context_v1); run by per-VM systemd service before the GPU device service **Other:** - `patches/` — KWin/Plasma patches for VM window decoration colors and clipboard isolation - `flake.nix` — exposes `nixosModules.default` and `lib.makeRootfsNixos` ### Generated Scripts - `vm-run ` — run command in VM (starts VM on-demand via socket activation) - `vm-start ` — start VM via systemd (polkit, no sudo) - `vm-stop ` — stop VM via systemd (polkit, no sudo) - `vm-shell ` — connect to VM serial console (default) or SSH with `--ssh` - `vm-usb [attach|detach] [ ]` — list USB devices, attach/detach USB devices to VMs See README.md for full usage details and options. ### Dependencies - Custom crosvm fork: `git.dsg.is/dsg/crosvm.git` - wayland-proxy-virtwl: Wayland/X11 proxying between host and guests - NixOS 25.11 base - usbip-rs: `git.dsg.is/dsg/usbip-rs.git` — USB/IP over vsock for USB passthrough ### Key Patterns - **Socket activation chain**: command socket → proxy template service → VM service → crosvm - **Offline-by-default networking**: VMs have no network unless `network.interfaces` or `network.netvm` is configured. - **Host netvm**: `network.netvm = "host"` routes VM traffic through the host directly (TAP with hostAddress, host nftables NAT). No bridge or netvm VM needed. - **Root overlay**: raw disk-backed (default) or tmpfs-backed overlayfs upper layer. Ephemeral — created at VM start, deleted at stop. - **Session bind**: GPU-enabled VMs (default) are tied to the desktop session via per-VM systemd user services bound to `graphical-session.target`. For `autoStart` GPU VMs, the session-bind service also starts the VM on login. Non-GPU `autoStart` VMs start at `multi-user.target` (boot). - **Automatic DNS**: All VMs have `systemd-resolved` enabled by default (guest rootfs). Netvm VMs get `unbound` as a recursive resolver via `guestConfig` injection. Downstream VMs get nameserver kernel params pointing at their netvm's IP via `netvmInjections.nameservers`. VMs with `netvm = "host"` or no netvm need manual DNS config. - **GPU device backend**: `vmsilo--gpu` service runs `crosvm device gpu` sandboxed; both crosvm and cloud-hypervisor attach via vhost-user. `vmsilo--wayland-seccontext` must start first. - **Per-VM runtime dirs**: all sockets under `/run/vmsilo//` subdirectories (not flat). - **USB passthrough**: usbip-over-vsock on port 5002. Guest runs `usbip-rs client listen`, host runs one `usbip-rs host connect` per device as `vmsilo--usb@.service`. Works with both crosvm and cloud-hypervisor. ### Gotchas - **`nix build` git index filtering**: new files must be `git add`'d before `nix build` sees them (applies to all `src = ./` references in flake.nix). - **Explicit UID required**: the configured user must have `users.users..uid` set explicitly. - **O_DIRECT on root disk**: intentional — bypasses host page cache to avoid double-caching (erofs is read-only, guest caches internally). - **Module split**: there is no `modules/config.nix`. Implementation is split across `scripts.nix`, `services.nix`, `desktop.nix`, `networking.nix`, `pci.nix`, `overlay.nix`, `assertions.nix`, `package.nix`. - **cloud-hypervisor package**: sourced from a custom flake input (`git.dsg.is/dsg/cloud-hypervisor.git`), not nixpkgs. ### NixOS Module Usage The configured user must have an explicit UID set (`users.users..uid = `). See README.md for the full configuration reference and examples.