The device tray now shows Sound Output and Microphone Input menus for
running VMs. Each direction can be toggled per-VM at runtime via the
vhost-device-sound control socket. Initial state comes from the existing
sound.playback/sound.capture NixOS options.
NixOS module passes --initial-streams and --control-socket to
vhost-device-sound. The vhost-device flake input is updated to include
the new control socket support.
Document the new vmsilo-vsock library crate, vsock-connect CLI tool,
vm-stop --all flag, and CID file pattern. Update SSH ProxyCommand
description for cloud-hypervisor VMs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add coverage-guided fuzzing for vmsilo-dbus-proxy with 5 targets:
- fuzz_deserialize: raw bytes → postcard deserialization
- fuzz_read_message: raw bytes → length-prefixed framing
- fuzz_sanitize_snapshot: structured input → sanitization invariants
- fuzz_sanitize_notification: structured input → sanitization invariants
- fuzz_tint_pixmap: structured input → pixel manipulation
Uses the arbitrary crate (behind a fuzz feature flag) for structured
input generation, with invariant assertion helpers that verify all
documented sanitization constraints.
Nix integration via rust-overlay nightly: `nix develop .#fuzz` for
interactive fuzzing, `nix run .#fuzz-dbus-proxy -- <target>` for
one-liners.
Also fixes a sanitization bug found by the fuzzer: sanitize_menu_tree
did not clamp negative IDs on the root menu node.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
virtiofsd has built-in sandboxing (--sandbox=namespace): it creates
mount/PID/network namespaces, does pivot_root, drops capabilities, and
applies its own seccomp filter. The systemd unit adds non-overlapping
hardening: IPC/UTS namespace isolation, seccomp-based protections, a
capability bounding set as defense-in-depth, and LimitNOFILE=1048576.
Per-instance runtime directories (/run/vmsilo/<vmname>/virtiofs-<tag>/)
replace the shared directory for better isolation.
New VM options: virtiofs.seccompPolicy and virtiofs.disableSandbox.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Harden CH VM services with a whitelist approach:
- TemporaryFileSystem=/ with explicit BindPaths/BindReadOnlyPaths
- PrivateUsers=identity (1:1 UID mapping, zero host capabilities)
- PrivatePIDs, PrivateIPC
- Empty CapabilityBoundingSet, NoNewPrivileges
- DevicePolicy=closed with specific DeviceAllow
- ProtectKernel{Tunables,Modules,Logs}, RestrictNamespaces, LockPersonality
- CH landlock enabled via --landlock flag
Launch flow restructured: ExecStart runs the CH binary directly inside
the sandbox. ExecStartPost=+ (privileged) handles VM creation, TAP FD
passing via vmsilo-tap-open + ch-remote add-net, boot, and socket chown.
ExecStartPre=+/ExecStopPost=+ handle ephemeral disk and socket cleanup.
Network config removed from CH JSON — TAP interfaces added via ch-remote
add-net with FD passing (SCM_RIGHTS). TAP and runtime directory ownership
changed to root. Wayland-seccontext socket moved to gpu/ subdirectory.
Ephemeral disk moved to per-VM /var/lib/vmsilo/{name}/ directory.
New option: cloud-hypervisor.disableSandbox (default false) disables all
hardening except seccomp.
PrivateNetwork=true is not yet enabled — TAP FD passing works but CH
gets EIO writing to the TAP from inside a private network namespace.
To be investigated separately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Default hypervisor: crosvm → cloud-hypervisor
- Default GPU backend: crosvm → vhost-device-gpu
- GPU option: either bool submodule → pure submodule (disable via gpu.wayland = false)
- Remove global programs.vmsilo.{crosvm,cloud-hypervisor,vmsilo-dbus-proxy} options
- Per-VM crosvm/cloud-hypervisor options now non-nullable with direct defaults
- Add per-VM dbus.logLevel (passed to guest via kernel param, keeping rootfs uniform)
- Delete normalizeGpuConfig, gpuConfig, and all global fallback resolution code
- Simplify GPU assertion to use vm.gpu.* directly (no mkVmConfig needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenGL and Vulkan worked in some simple test programs when using crosvm
with built-in GPU emulation. I never got them working in any real-world
application, I suspect because wayland dmabuf is not working. After
switching to vhost-user gpu, even the testcases don't work, but I
think it's worthwhile to be able to sandbox the GPU process better.
Maps logLevel to wayland-proxy-virtwl's --log-level flag (replaces
hardcoded -v). Ignored for sommelier.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add gpu.backend option to select between crosvm's built-in GPU device
emulation ("crosvm", default) and vhost-device-gpu ("vhost-device").
Both backends use the same vhost-user socket, sandboxing, and
wayland-seccontext service.
Also adds gpu.vulkan option (venus capset) and makes gpu.logLevel
independent of crosvm.logLevel (default "info").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove vmsilo-start-* user-facing symlinks from package.nix (internal
VM launcher scripts are only used by systemd ExecStart, not by users)
- Rename vmsilo-usb to vm-usb to match the vm-* naming convention
- Increase socat -t timeout in vm-run from default 0.5s to 5s to fix
missing output from console commands (cloud-hypervisor proxy startup
latency exceeded the default timeout window)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace crosvm xhci-based USB passthrough with usbip-rs over vsock,
enabling USB passthrough for both crosvm and cloud-hypervisor VMs.
Guest runs a persistent usbip-rs client listener on vsock port 5002.
Host runs one sandboxed usbip-rs host connect process per attached
device as a systemd template service (vmsilo-<vm>-usb@<devpath>).
Eliminates the JSON state file, file locking, and crosvm-specific
shell helper library in favor of systemd as the source of truth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
screen -dmS forks to background (daemon mode), which should work
without a controlling terminal. Type=forking tells systemd to expect
the fork.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
screen requires a controlling terminal which systemd services don't
provide. tmux works without a terminal via new-session without -d.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set RUST_BACKTRACE=full on VM, GPU, sound, dbus-proxy, balloond, and
wayland-seccontext services for better crash diagnostics. Add per-VM
sound.logLevel option (default "info") that sets RUST_LOG on the
vhost-device-sound service.
Also document previously undocumented options in README: cloud-hypervisor
hugepages, netvmRange, sound.logLevel, sound.seccompPolicy,
cloudHypervisor.hugepages, cloudHypervisor.seccompPolicy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tray proxy was unconditionally enabled for all crosvm VMs. Add
tray.enable so it must be opted into per VM. When disabled, neither
the host-side tray service nor the guest-side tray daemon are created.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove vulkan/venus references (option was removed in cloud-hypervisor
refactor) and document gpu.allowWX, gpu.logLevel, gpu.seccompPolicy
per-VM options that were missing from the options table and GPU section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a `mountPath` option to `sharedDirectories.<name>` that, when set,
automatically mounts the virtiofs share at the specified path inside the
guest via systemd.mount-extra kernel parameter. The implicit sharedHome
entry now uses this same code path (mountPath = "/home/user") instead of
a hardcoded special case.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
xdg-desktop-portal 1.20.3's Realtime portal intercepts PipeWire's RT
scheduling requests but fails silently: it calls fstatat(pidfd, "ns/pid")
to check the caller's PID namespace, which returns ENOTDIR because pidfds
don't support being used as directory FDs on current kernels (6.18/6.19).
PipeWire uses fire-and-forget D-Bus and never sees the error.
Fix by granting the @audio group PAM limits (rtprio=95, nice=-19,
memlock=unlimited) so PipeWire's module-rt can call sched_setscheduler
directly, bypassing both the broken portal and rtkit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full isolation has too much impact to be a default. Even on an almost
unloaded machine with a couple of VMs running it results in audio buffer
underruns due to the significant scheduling latency.
This change is fine because with vmsilo, the trust domain is the VM. There
isn't much reason to protect apps from other apps running in the same VM.
Better to run those apps in separate VMs in that case.
qcow2 causes O_DIRECT failures on ext4 due to crosvm doing unaligned
access when parsing the qcow2 header. Since we don't use any qcow2
features (the disk is created fresh and deleted on stop), a raw sparse
file via truncate works just as well and also removes the qemu package
dependency from the VM service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route VM traffic through the host directly instead of requiring a
separate netvm VM. Uses the same nftables NAT and forward firewall
rules as VM-based netvms, applied on the host using TAP interface
names. Removes the hostNetworking.nat options in favor of the
unified netvm approach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds network.netvm / network.isNetvm options that auto-configure
point-to-point VM networking (host bridge, TAP interfaces, guest IPs,
default routes, masquerade NAT, and forward firewall rules) without
manual interface configuration.
New options:
programs.vmsilo.netvmRange — IP pool for /31 auto-allocation (default 10.200.0.0/16)
vm.network.isNetvm — mark VM as a network gateway
vm.network.netvm — route this VM through a named netvm
vm.network.netvmSubnet — override auto-allocated /31 (pin specific address)
Architecture:
modules/netvm.nix computes all (netvm, client) pairs and writes to
_internal.netvmInjections to avoid infinite recursion in the module
system. networking.nix, scripts.nix, and services.nix each have a
getEffectiveInterfaces helper that merges user-configured and
injected interfaces transparently.
Guest nftables config (masquerade NAT, forward isolation between
clients, ip_forward sysctl) is injected via _generatedGuestConfig
and merged into the rootfs build in scripts.nix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GPU VMs need a Wayland socket, so starting them at multi-user.target
(boot) fails. The session-bind user service now also starts autoStart
GPU VMs when the graphical session begins. Non-GPU VMs still start at
boot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add libvulkan.so to crosvm LD_LIBRARY_PATH. Along with the earlier fix
commits, this now enables full guest-side vulkan (tested with vkcube
and vulkaninfo - they're using the host GPU). Huzzah!
Was hoping enabling vulkan and not opengl would make mesa fall back to
zink, so we could skip virgl2 and just translate opengl to vulkan. But
it still ends up using llvmpipe. Forcing zink gives errors, getting that
working will be the next goal.
Replace the low-level gpu attrset (mapped directly to --gpu args) with a
submodule of supported features: wayland (cross-domain), opengl (virgl2),
and vulkan (venus). Vulkan automatically adds --gpu-render-server.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nvidia GPU drivers require RWX memory pages, which crosvm's seccomp
sandbox blocks. This option switches to a crosvm-nvidia build with
a relaxed W+X memory policy, keeping the default secure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of passing raw crosvm attrsets, sound is now configured
with two booleans: sound.playback (default true) and sound.capture
(default false, implies playback).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch shared directories from crosvm's built-in --shared-dir to
external virtiofsd processes connected via --vhost-user. Each shared
directory gets a dedicated virtiofsd systemd service that starts
before the VM and stops when the VM stops.
The sharedDirectories option is now an attrsOf submodule (keyed by
fs tag) with typed options for all virtiofsd flags instead of the
previous freeform list passed directly to crosvm.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also add crosvm options to hint to the scheduler to put it on the performance
cores, enable vCPU stop on guest suspend and disable some legacy peripheral
emulation.
Instead of using host buffer, use a global vmsilo buffer. Host buffer is
treated just like VM buffers - copying to/from it needs explicit action. This
is more like the Qubes behavior, only difference is that copy/paste to/from
the host clipboard is allowed.