{ config, pkgs, lib, ... }: { ############################################ # Base system ############################################ nixpkgs.hostPlatform = "x86_64-linux"; system.stateVersion = "24.11"; # Simple console-based kiosk using cage (Wayland single-app compositor) services.xserver.enable = false; # Not using an X11 display manager # Autologin to TTY1 as kiosk user services.getty.autologinUser = "kiosk"; # Kiosk user users.users.kiosk = { isNormalUser = true; description = "Kiosk User"; home = "/home/kiosk"; extraGroups = [ "wheel" ]; initialPassword = "kiosk"; }; # Packages required (aligning with the Debian preseed intent) environment.systemPackages = with pkgs; [ firefox cage curl unzip # chromium # available if you want it in addition to Firefox ]; ############################################ # Firefox policies (preconfigured profile settings) ############################################ programs.firefox = { enable = true; policies = { DisableDeveloperTools = true; BlockAboutAddons = true; BlockAboutConfig = true; BlockAboutProfiles = true; BlockAboutSupport = true; DisableFirefoxAccounts = true; DisablePrivateBrowsing = true; DisableProfileImport = true; DisableProfileRefresh = true; DisableSafeMode = true; DisablePocket = true; DisableFirefoxScreenshots = true; DisableSetDesktopBackground = true; Homepage = { URL = "https://mahn.ke"; Locked = true; }; NewTabPage = { Enabled = false; }; # Use a Linux path for downloads in kiosk DownloadDirectory = { Path = "/home/kiosk/Downloads"; Locked = true; }; PromptForDownloadLocation = false; StartDownloadsInTempDirectory = false; DisableAppUpdate = true; Permissions = { Camera = "deny"; Microphone = "deny"; Location = "deny"; Notifications = "deny"; }; ShowHomeButton = false; DisplayMenuBar = false; DisplayBookmarksToolbar = false; # Extension & user messaging controls (per your Debian policy JSON) UserMessaging = { ExtensionRecommendations = false; FeatureRecommendations = false; UrlbarInterventions = false; SkipOnboarding = false; MoreFromMozilla = false; FirefoxLabs = false; Locked = false; }; # Install Tampermonkey automatically (Firefox will fetch at runtime). # Note: AMO URL may change; this is the typical latest channel. Extensions = { Install = [ "https://addons.mozilla.org/firefox/downloads/latest/tampermonkey/latest.xpi" ]; }; }; # Helpful preferences to keep Firefox minimal preferences = { "browser.fullscreen.autohide" = true; "browser.shell.checkDefaultBrowser" = false; "browser.startup.page" = 1; # Start with homepage }; }; ############################################ # Kiosk launch behavior (replicates your bash_profile approach) ############################################ # Create a bash_profile for the kiosk user that launches cage + firefox system.activationScripts.kioskBashProfile = lib.stringAfter ["users"] '' mkdir -p /home/kiosk chown kiosk:kiosk /home/kiosk sudo -u kiosk mkdir -p /home/kiosk/.config cat > /home/kiosk/.bash_profile <<'EOF' if [ -z "$WAYLAND_DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then exec ${pkgs.cage}/bin/cage ${pkgs.firefox}/bin/firefox --kiosk https://c3nav.de fi EOF chown kiosk:kiosk /home/kiosk/.bash_profile ''; # Unpack preconfigured Firefox profile from the repository into kiosk's home system.activationScripts.kioskFirefoxProfile = lib.stringAfter ["users"] '' mkdir -p /home/kiosk/.mozilla/firefox # Only unzip if directory is empty (first activation) if [ -z "$(ls -A /home/kiosk/.mozilla/firefox 2>/dev/null)" ]; then ${pkgs.unzip}/bin/unzip -o ${../Firefox.zip} -d /home/kiosk/.mozilla/firefox chown -R kiosk:kiosk /home/kiosk/.mozilla/firefox fi ''; ############################################ # Include your userscripts in the image for easy import ############################################ environment.etc."kiosk/tampermonkey".source = ./../tampermonkey; ############################################ # Networking & basic services ############################################ networking.hostName = "kiosk"; time.timeZone = "UTC"; services.openssh.enable = true; # optional, mirrors preseed tasksel ssh-server # Keep system simple, disable unneeded DM services.displayManager.enable = false; }