Files
radiantwave/nixos/vm.nix
T
2026-08-07 14:51:41 -05:00

145 lines
5.6 KiB
Nix

# NixOS test VM for RadiantWave — mirrors the production kiosk setup.
#
# Build & run:
# nixos-rebuild build-vm --flake .#test-vm
# ./result/bin/run-test-vm-vm
#
# - cage as the Wayland compositor (fullscreen kiosk)
# - PipeWire audio (with a virtual sink for device-switch testing)
# - NetworkManager for the WiFi provisioning page
# - Auto-login as the kiosk user, app started via loginShellInit
{
config,
lib,
pkgs,
...
}:
let
radiantWaveApp = config.services.radiantwave.package;
in
{
# ── Hostname ──────────────────────────────────────────────────────────────
networking.hostName = "radiantwave-test";
# ── RadiantWave module ────────────────────────────────────────────────────
services.radiantwave.enable = true;
# ── Boot ──────────────────────────────────────────────────────────────────
boot.loader.systemd-boot.enable = lib.mkForce false;
boot.loader.efi.canTouchEfiVariables = lib.mkForce false;
# ── Packages ──────────────────────────────────────────────────────────────
environment.systemPackages = [
pkgs.cage
pkgs.rsync
pkgs.dbus
pkgs.mesa
pkgs.git
pkgs.sqlite
];
# ── Audio (PipeWire + virtual sink for device-switch testing) ─────────────
services.pipewire = {
enable = true;
alsa.enable = true;
pulse.enable = true;
extraConfig.pipewire."30-virtual-sink" = {
"context.modules" = [
{
name = "libpipewire-module-loopback";
args = {
"node.name" = "virtual-speaker";
"node.description" = "Virtual Speaker";
"audio.position" = [ "FL" "FR" ];
};
}
];
};
};
# ── Seat ──────────────────────────────────────────────────────────────────
services.seatd.enable = true;
hardware.graphics.enable = true;
# ── Networking ────────────────────────────────────────────────────────────
services.resolved.enable = false;
networking.networkmanager.enable = true;
systemd.services.NetworkManager-wait-online.enable = false;
systemd.network.wait-online.enable = false;
# ── SSH ───────────────────────────────────────────────────────────────────
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = true;
};
};
# ── User ──────────────────────────────────────────────────────────────────
users.users.kiosk = {
isNormalUser = true;
uid = 1000;
extraGroups = [ "audio" "video" "input" "networkmanager" "wheel" ];
password = "kiosk";
};
services.getty.autologinUser = "kiosk";
# ── Data directories (matches the app's first-run paths) ──────────────────
systemd.tmpfiles.rules = [
"d /home/kiosk/.local/radiantwave 0770 kiosk kiosk -"
"d /home/kiosk/.local/radiantwave/audio 0755 kiosk kiosk -"
];
# ── RadiantWave service (via loginShellInit, same as production) ──────────
environment.loginShellInit = ''
if [ "$(tty)" = "/dev/tty1" ]; then
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
# Smart Network Wait
for i in {1..10}; do
if ${pkgs.iputils}/bin/ping -c 1 8.8.8.8 &> /dev/null; then
break
fi
sleep 1
done
# Graphics fixes (matches production)
export SDL_VIDEODRIVER=wayland
export XDG_SESSION_TYPE=wayland
export WLR_RENDERER=pixman
export WLR_NO_HARDWARE_CURSORS=1
cage -- ${radiantWaveApp}/bin/radiantwave > /tmp/kiosk.log 2>&1
fi
'';
# ── VM settings ───────────────────────────────────────────────────────────
virtualisation = {
memorySize = 4096;
cores = 4;
graphics = true;
forwardPorts = [
{ from = "host"; host.port = 2222; guest.port = 22; }
];
qemu.options = [
"-device intel-hda"
"-device hda-duplex"
"-vga virtio"
];
};
services.logrotate.enable = false;
# ── Nix settings ──────────────────────────────────────────────────────────
nix.settings.experimental-features = [ "nix-command" "flakes" ];
nix.settings.accept-flake-config = true;
# ── Firewall (open for local dev) ─────────────────────────────────────────
networking.firewall.enable = false;
system.stateVersion = "24.11";
}