270 lines
9.5 KiB
Nix
270 lines
9.5 KiB
Nix
{ pkgs, radiantwave, upgradeFlakeRef, radiantwavePlymouth, ... }:
|
|
|
|
let
|
|
radiantApp = radiantwave.packages.${pkgs.system}.default;
|
|
myKey = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL4qWNZnYU5JaqR9c2qh6oFZktzVNBOx42PfDaGofCL6 cnclack@pm.me macbook" ];
|
|
in
|
|
{
|
|
############################################################
|
|
# System configuration
|
|
############################################################
|
|
|
|
# Included packages
|
|
environment.systemPackages = [
|
|
pkgs.cage pkgs.rsync pkgs.dbus pkgs.mesa pkgs.git pkgs.tailscale pkgs.sqlite pkgs.wireplumber
|
|
radiantApp
|
|
];
|
|
|
|
# This populates the EFI partition and tells the NUC how to start NixOS.
|
|
boot.loader.systemd-boot.enable = true;
|
|
boot.loader.efi.canTouchEfiVariables = true;
|
|
# Boot straight into the splash screen without waiting on the bootloader menu.
|
|
boot.loader.timeout = 0;
|
|
|
|
# Hardware-specific: NUC 14 needs these for the NVMe and Graphics
|
|
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" "i915" "drm" "drm_kms_helper" ];
|
|
|
|
# --- RadiantWave splash: Plymouth from boot until the app's first frame ---
|
|
boot.plymouth.enable = true;
|
|
boot.plymouth.theme = "radiantwave";
|
|
boot.plymouth.themePackages = [ radiantwavePlymouth ];
|
|
|
|
# Silence the kernel/initrd output behind the splash. The module adds
|
|
# "splash" itself; we keep "quiet" plus show_status=false and loglevel 0.
|
|
boot.consoleLogLevel = 0;
|
|
boot.initrd.verbose = false;
|
|
boot.kernelParams = [ "quiet" "splash" "systemd.show_status=false" ];
|
|
|
|
# Hold the splash until the app is ready to take over the display, instead of
|
|
# quitting at multi-user.target (which would flash a black/console screen
|
|
# during the login shell's network wait). The kiosk login shell runs
|
|
# `sudo -n plymouth quit --wait` immediately before exec'ing cage.
|
|
systemd.services.plymouth-quit.wantedBy = pkgs.lib.mkForce [ ];
|
|
systemd.services.plymouth-quit-wait.wantedBy = pkgs.lib.mkForce [ ];
|
|
|
|
# Allow the kiosk user to quit Plymouth (NOPASSWD, command-scoped).
|
|
security.sudo.extraRules = [
|
|
{
|
|
users = [ "kiosk" ];
|
|
commands = [
|
|
{
|
|
command = "/run/current-system/sw/bin/plymouth";
|
|
options = [ "NOPASSWD" ];
|
|
}
|
|
];
|
|
}
|
|
];
|
|
|
|
hardware.graphics.enable = true;
|
|
|
|
time.timeZone = "America/Chicago";
|
|
|
|
networking.networkmanager.enable = true;
|
|
|
|
nix.settings.access-tokens = "gitea.radiantwavetech.com=ff524968d31acc5c4da350d0884a87264d0e8881";
|
|
|
|
system.autoUpgrade = {
|
|
enable = true;
|
|
flake = upgradeFlakeRef;
|
|
dates = "*:00/10";
|
|
flags = [ "--refresh" "-L" ];
|
|
operation = "switch";
|
|
};
|
|
|
|
system.activationScripts.cleanRadiantFiles = ''
|
|
rm -rf /home/kiosk/.local/radiantwave/audio
|
|
'';
|
|
|
|
# Virtual machine declaration
|
|
virtualisation.vmVariant = {
|
|
boot.loader.systemd-boot.enable = pkgs.lib.mkForce false;
|
|
boot.loader.efi.canTouchEfiVariables = pkgs.lib.mkForce false;
|
|
virtualisation.forwardPorts = [
|
|
{ from = "host"; host.port = 2222; guest.port = 22; }
|
|
];
|
|
virtualisation.qemu.options = [
|
|
"-device intel-hda"
|
|
"-device hda-duplex"
|
|
"-vga virtio"
|
|
];
|
|
virtualisation = {
|
|
cores = 8;
|
|
memorySize = 8192;
|
|
};
|
|
services.logrotate.enable = false;
|
|
};
|
|
virtualisation.diskSize = 10485760;
|
|
|
|
# NixOS settings.
|
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
|
nix.settings.accept-flake-config = true;
|
|
system.stateVersion = "24.05";
|
|
|
|
############################################################
|
|
# User configuration
|
|
############################################################
|
|
|
|
users.users.root.openssh.authorizedKeys.keys = myKey;
|
|
users.users.kiosk = {
|
|
isNormalUser = true;
|
|
uid = 1000;
|
|
extraGroups = ["audio" "video" "input" "networkmanager" "wheel" "seat"];
|
|
openssh.authorizedKeys.keys = myKey;
|
|
password = "Fuckp@ssw0rds";
|
|
};
|
|
|
|
services.getty.autologinUser = "kiosk";
|
|
|
|
# --- The Kiosk Startup Script ---
|
|
environment.loginShellInit = ''
|
|
if [ "$(tty)" = "/dev/tty1" ]; then
|
|
export GODEBUG=netdns=cgo
|
|
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
|
|
export SDL_VIDEODRIVER=wayland
|
|
export QT_QPA_PLATFORM=wayland
|
|
export XDG_SESSION_TYPE=wayland
|
|
export WLR_RENDERER=pixman
|
|
export WLR_NO_HARDWARE_CURSORS=1
|
|
|
|
# Pin system audio to 100% and unmute before launching RadiantWave
|
|
${pkgs.systemd}/bin/systemctl --user start radiantwave-audio-init.service || true
|
|
|
|
# Release the splash screen right before the compositor takes over the display
|
|
sudo -n plymouth quit --wait || true
|
|
|
|
exec dbus-run-session cage -- /run/current-system/sw/bin/radiantwave -d -s > /tmp/kiosk.log 2>&1
|
|
fi
|
|
'';
|
|
|
|
############################################################
|
|
# Service configuration
|
|
############################################################
|
|
|
|
services.pipewire = { enable = true; alsa.enable = true; pulse.enable = true; };
|
|
|
|
# New sinks with no stored volume default to ~40% in WirePlumber 0.5+.
|
|
# Force every newly-created sink to 100% so switching the default sink
|
|
# never changes the volume.
|
|
services.pipewire.wireplumber.extraConfig."99-kiosk-audio" = {
|
|
"wireplumber.settings"."device.routes.default-sink-volume" = 1.0;
|
|
};
|
|
|
|
# --- Pin all system audio sinks to 100% before RadiantWave launches ---
|
|
# User unit (PipeWire runs per-user in the kiosk session), owned by this
|
|
# system config. Ordered after PipeWire/WirePlumber; the retry loop covers
|
|
# sink readiness at boot. Pins every connected sink (not just the default)
|
|
# and unmutes them, so switching the default sink never changes the volume.
|
|
# RadiantWave's loginShellInit waits on this before exec'ing cage.
|
|
systemd.user.services.radiantwave-audio-init = {
|
|
description = "Pin all system audio sinks to 100% and unmute";
|
|
after = [ "pipewire.service" "wireplumber.service" ];
|
|
wants = [ "pipewire.service" "wireplumber.service" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
};
|
|
script = ''
|
|
# Wait until WirePlumber exposes at least one sink.
|
|
# `wpctl list` output is tab-separated: ID, name, media/object type,
|
|
# then `*` if the node is the current default. `cut -f1` grabs the ID.
|
|
sinks=""
|
|
for i in {1..30}; do
|
|
sinks=$(${pkgs.wireplumber}/bin/wpctl list audio sinks 2>/dev/null \
|
|
| ${pkgs.coreutils}/bin/cut -f1)
|
|
if [ -n "$sinks" ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Pin and unmute every connected sink
|
|
for sink_id in $sinks; do
|
|
${pkgs.wireplumber}/bin/wpctl set-volume "$sink_id" 1.0 2>/dev/null || true
|
|
${pkgs.wireplumber}/bin/wpctl set-mute "$sink_id" 0 2>/dev/null || true
|
|
done
|
|
|
|
# Fallback in case the sink list came up empty
|
|
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0 2>/dev/null || true
|
|
${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 2>/dev/null || true
|
|
|
|
# Persist 100% as WirePlumber's default for any brand-new sink so it
|
|
# comes up at full volume even before this service runs on next boot.
|
|
${pkgs.wireplumber}/bin/wpctl settings device.routes.default-sink-volume 1.0 --save 2>/dev/null || true
|
|
'';
|
|
};
|
|
|
|
services.seatd.enable = true;
|
|
services.openssh.enable = true;
|
|
services.openssh.settings.PermitRootLogin = "prohibit-password";
|
|
|
|
# Networking Network-Manager
|
|
services.resolved.enable = false;
|
|
systemd.services.NetworkManager-wait-online.enable = false;
|
|
systemd.network.wait-online.enable = false;
|
|
|
|
# --- Set ownership of Radiantwave files ---
|
|
systemd.tmpfiles.rules = [
|
|
"d /home/kiosk/.local/radiantwave 0770 kiosk kiosk -"
|
|
];
|
|
|
|
# --- Backup service: runs before nixos-upgrade ---
|
|
systemd.services.radiantwave-backup = {
|
|
description = "Backup RadiantWave user data before upgrade";
|
|
before = [ "nixos-upgrade.service" ];
|
|
wantedBy = [ "nixos-upgrade.service" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = "kiosk";
|
|
};
|
|
script = ''
|
|
BACKUP_DIR="/home/kiosk/.local/radiantwave-backup"
|
|
SOURCE_DIR="/home/kiosk/.local/radiantwave"
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
${pkgs.rsync}/bin/rsync -a \
|
|
--exclude='audio/' \
|
|
"$SOURCE_DIR/" "$BACKUP_DIR/"
|
|
|
|
echo "Backup completed at $(date)" > "$BACKUP_DIR/.backup-timestamp"
|
|
'';
|
|
};
|
|
|
|
# --- Health check service: runs after nixos-upgrade ---
|
|
systemd.services.radiantwave-healthcheck = {
|
|
description = "Verify RadiantWave is running after upgrade, rollback if not";
|
|
after = [ "nixos-upgrade.service" ];
|
|
wantedBy = [ "nixos-upgrade.service" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
# Give cage time to start radiantwave after the switch
|
|
ExecStartPre = "${pkgs.coreutils}/bin/sleep 90";
|
|
};
|
|
script = ''
|
|
# The installed `radiantwave` is a makeWrapper script that execs the real
|
|
# binary (`.radiantwave-unwrapped`), so the process comm is not exactly
|
|
# `radiantwave`; match the exec'd binary's command line instead.
|
|
if ${pkgs.procps}/bin/pgrep -f '\.radiantwave-unwrapped' > /dev/null; then
|
|
echo "RadiantWave is running, upgrade successful"
|
|
exit 0
|
|
fi
|
|
|
|
echo "RadiantWave not found after upgrade, rolling back..."
|
|
|
|
# Roll back to previous NixOS generation
|
|
/run/current-system/sw/bin/nixos-rebuild switch --rollback
|
|
|
|
echo "Rollback completed at $(date)"
|
|
'';
|
|
};
|
|
}
|