# Edit this configuration file to define what should be installed on # your system. Help is available in the configuration.nix(5) man page # and in the NixOS manual (accessible by running `nixos-help`). { pkgs, lib, ssh, config, inputs, ... }: let secrets = config.age.secrets; in { nix = { settings = { experimental-features = [ "nix-command" "flakes" ]; auto-optimise-store = true; }; gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; }; networking = { hostName = "turny"; # Define your hostname. useDHCP = true; useNetworkd = true; nftables.enable = true; wireless = { enable = true; networks = { KotiWLANi.pskRaw = "ext:psk_kotiwlani"; }; secretsFile = secrets.wpa_supplicant.path; }; }; services.avahi = { publish.enable = true; }; services.tailscale = { enable = true; useRoutingFeatures = "client"; }; # Set your time zone. time.timeZone = "Europe/Helsinki"; # Select internationalisation properties. i18n.defaultLocale = "en_US.UTF-8"; # Define a user account. Don't forget to set a password with 'passwd'. users.users.jokke = { isNormalUser = true; extraGroups = [ "wheel" ]; # Enable 'sudo' for the user. packages = [ pkgs.nh ]; openssh.authorizedKeys.keys = [ ssh.publicKeys.yubikey ]; initialPassword = "changeme"; }; users.users.root.openssh.authorizedKeys.keys = [ ssh.publicKeys.yubikey ]; nix.settings.trusted-users = [ "jokke" ]; environment.systemPackages = with pkgs; [ vim wget htop git alsa-utils ]; # Enable the OpenSSH daemon. services.openssh = { enable = true; settings.PasswordAuthentication = false; }; modules.firewall = { enable = true; allInterfaces = [ "ssh" ]; }; # Enable audio (ALSA) hardware.alsa.enable = true; # Complete config.txt settings: boot-critical u-boot lines plus SPI enable. # The configtxt module generates the full config.txt, overriding the # hardcoded one in sd-image-aarch64.nix which ignores these settings. hardware.raspberry-pi.configtxt.settings = { pi3 = { kernel = "u-boot-rpi3.bin"; core_freq = 250; }; pi02 = { kernel = "u-boot-rpi3.bin"; }; pi4 = { kernel = "u-boot-rpi4.bin"; enable_gic = true; armstub = "armstub8-gic.bin"; }; all = { arm_64bit = true; enable_uart = true; avoid_warnings = true; dtparam = [ "audio=on" "spi=on" ]; }; }; # Use the configtxt module's generated config.txt when building SD images sdImage.populateFirmwareCommands = let inherit (config.hardware.raspberry-pi.configtxt) file; in '' (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/) cp ${file} firmware/config.txt cp ${pkgs.ubootRaspberryPi3_64bit}/u-boot.bin firmware/u-boot-rpi3.bin cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-2-b.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-3-b.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-3-b-plus.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-cm3.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-zero-2.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2710-rpi-zero-2-w.dtb firmware/ cp ${pkgs.ubootRaspberryPi4_64bit}/u-boot.bin firmware/u-boot-rpi4.bin cp ${pkgs.raspberrypi-armstubs}/armstub8-gic.bin firmware/armstub8-gic.bin cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-4-b.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-400.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-cm4.dtb firmware/ cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-cm4s.dtb firmware/ ''; # Load SPI and GPIO kernel modules so /dev/spidev* and /dev/gpiomem exist boot.kernelModules = [ "spi_bcm2835" "bcm2835_gpiomem" ]; # Use the firmware-passed device tree (with dtparam/dtoverlay applied) # instead of u-boot loading a fresh DTB from FDTDIR boot.loader.generic-extlinux-compatible.useGenerationDeviceTree = false; # Mount the firmware partition so config.txt is accessible and can be # updated on deploys. The sd-image module defaults to noauto; we need it # mounted so the activation script can sync config.txt. fileSystems."/boot/firmware".options = [ "nofail" "defaults" ]; # Sync the generated config.txt to the firmware partition on each deploy # so dtparam/dtoverlay changes take effect without reflashing the SD card. system.activationScripts.syncConfigTxt = { text = '' if mountpoint -q /boot/firmware; then cp -f ${config.hardware.raspberry-pi.configtxt.file} /boot/firmware/config.txt fi ''; }; # Turny service systemd.services.turny = { description = "Turny Spotify RFID Controller"; documentation = [ "https://github.com/repomaa/turny" ]; after = [ "network.target" "sound.target" ]; wants = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "simple"; ExecStart = lib.getExe inputs.turny.packages.${pkgs.stdenv.hostPlatform.system}.default; User = "turny"; Group = "turny"; WorkingDirectory = "/var/lib/turny"; Restart = "always"; RestartSec = 10; RestartPreventExitStatus = 23; NoNewPrivileges = true; PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; ReadWritePaths = [ "/var/lib/turny" ]; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; LimitNOFILE = 1024; MemoryAccounting = true; MemoryMax = "100M"; StandardOutput = "journal"; StandardError = "journal"; SyslogIdentifier = "turny"; }; environment = { RUST_LOG = "info,turny=debug"; RUST_BACKTRACE = "1"; }; }; users.users.turny = { isSystemUser = true; group = "turny"; extraGroups = [ "gpio" "spi" "audio" ]; home = "/var/lib/turny"; createHome = true; }; users.groups.turny = { }; users.groups.gpio = { }; users.groups.spi = { }; # Udev rules for GPIO and SPI access services.udev.extraRules = '' SUBSYSTEM=="gpio", GROUP="gpio", MODE="0660" SUBSYSTEM=="gpiomem", GROUP="gpio", MODE="0660" SUBSYSTEM=="spidev", GROUP="spi", MODE="0660" ''; # This value determines the NixOS release from which the default # settings for stateful data, like user locations and database versions # on the system were taken. It's perfectly fine and recommended to leave # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). system.stateVersion = "25.05"; # Did you read the comment? sdImage.compressImage = false; }