From 99c1e0a4fc07fbf47711b838a839ee3854689fd7 Mon Sep 17 00:00:00 2001 From: Bryan Ramos Date: Sun, 19 Apr 2026 13:29:28 -0400 Subject: [PATCH 1/3] added tv --- flake.nix | 2 + system/machines/tv/default.nix | 12 +++ system/machines/tv/hardware.nix | 39 +++++++++ system/machines/tv/modules/default.nix | 5 ++ system/machines/tv/modules/kiosk/default.nix | 84 ++++++++++++++++++ system/machines/tv/system.nix | 90 ++++++++++++++++++++ 6 files changed, 232 insertions(+) create mode 100644 system/machines/tv/default.nix create mode 100644 system/machines/tv/hardware.nix create mode 100644 system/machines/tv/modules/default.nix create mode 100644 system/machines/tv/modules/kiosk/default.nix create mode 100644 system/machines/tv/system.nix diff --git a/flake.nix b/flake.nix index 1ecc950..fae9300 100644 --- a/flake.nix +++ b/flake.nix @@ -25,6 +25,7 @@ url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + nixos-hardware.url = "github:NixOS/nixos-hardware/master"; }; outputs = { self, nixpkgs, nixpkgs-unstable, nur, ... }@inputs: @@ -64,6 +65,7 @@ desktop = mkSystem { path = ./system/machines/desktop; }; server = mkSystem { path = ./system/machines/server; }; wsl = mkSystem { path = ./system/machines/wsl; }; + tv = mkSystem { path = ./system/machines/tv; system = "aarch64-linux"; }; }; devShells.x86_64-linux.default = with mkPkgs "x86_64-linux"; mkShell { diff --git a/system/machines/tv/default.nix b/system/machines/tv/default.nix new file mode 100644 index 0000000..a788314 --- /dev/null +++ b/system/machines/tv/default.nix @@ -0,0 +1,12 @@ +{ inputs, ... }: + +{ + imports = [ + inputs.nixos-hardware.nixosModules.raspberry-pi-4 + ../../../user + ../../keys + ./hardware.nix + ./system.nix + ./modules/kiosk + ]; +} \ No newline at end of file diff --git a/system/machines/tv/hardware.nix b/system/machines/tv/hardware.nix new file mode 100644 index 0000000..e42e88b --- /dev/null +++ b/system/machines/tv/hardware.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; + + boot = { + initrd = { + availableKernelModules = [ "xhci_pci" "usbhid" ]; + kernelModules = [ ]; + }; + kernelModules = [ ]; + extraModulePackages = [ ]; + + # Pi boots via extlinux from the Hydra SD image — not GRUB/systemd-boot + loader = { + grub.enable = false; + generic-extlinux-compatible.enable = true; + }; + }; + + # UUIDs are baked into the Hydra SD image — identical on every Pi flashed + # from that image. FIRMWARE (FAT) holds the Pi bootloader; NIXOS_SD (ext4) + # is root. /boot/firmware must be mounted so nixos-rebuild can update + # extlinux config on subsequent rebuilds. + fileSystems."/" = { + device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888"; + fsType = "ext4"; + }; + + fileSystems."/boot/firmware" = { + device = "/dev/disk/by-uuid/2178-694E"; + fsType = "vfat"; + options = [ "nofail" "noauto" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; +} \ No newline at end of file diff --git a/system/machines/tv/modules/default.nix b/system/machines/tv/modules/default.nix new file mode 100644 index 0000000..aa06974 --- /dev/null +++ b/system/machines/tv/modules/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./kiosk + ]; +} diff --git a/system/machines/tv/modules/kiosk/default.nix b/system/machines/tv/modules/kiosk/default.nix new file mode 100644 index 0000000..2584265 --- /dev/null +++ b/system/machines/tv/modules/kiosk/default.nix @@ -0,0 +1,84 @@ +{ pkgs, lib, config, ... }: + +with lib; +let + cfg = config.modules.system.kiosk; +in +{ + options.modules.system.kiosk = { + enable = mkEnableOption "kiosk mode (Cage + Firefox fullscreen browser)"; + + user = mkOption { + type = types.str; + description = "user account the kiosk session runs as"; + }; + + url = mkOption { + type = types.str; + default = "about:blank"; + description = "URL loaded by Firefox on startup"; + }; + }; + + config = mkIf cfg.enable { + # Cage is a minimal Wayland compositor that runs exactly one program + # fullscreen. Combined with auto-login it gives us a zero-chrome kiosk. + services.cage = { + enable = true; + user = cfg.user; + program = '' + ${pkgs.firefox}/bin/firefox \ + --kiosk \ + ${cfg.url} + ''; + }; + + # System-wide Firefox policies. Writes /etc/firefox/policies/policies.json + # which Firefox reads regardless of how it's launched. + programs.firefox = { + enable = true; + policies = { + # Force-install uBlock Origin from AMO. The "latest.xpi" URL is + # Mozilla's stable redirect — it always resolves to the current + # release and is explicitly supported for enterprise policy use. + ExtensionSettings = { + "uBlock0@raymondhill.net" = { + install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi"; + installation_mode = "force_installed"; + }; + }; + + # Trim everything Firefox does that makes no sense on a TV + DisableTelemetry = true; + DisablePocket = true; + DisableFirefoxStudies = true; + DisableProfileImport = true; + DontCheckDefaultBrowser = true; + OverrideFirstRunPage = ""; + OverridePostUpdatePage = ""; + NoDefaultBookmarks = true; + PasswordManagerEnabled = false; + + # DRM on — required for Netflix/Prime/etc. to even attempt playback. + # On aarch64 the Widevine CDM still has to be fetched at runtime and + # may be refused by streaming services; YouTube/Twitch/non-DRM work + # regardless. + EncryptedMediaExtensions = { + Enabled = true; + Locked = true; + }; + }; + }; + + # Firefox runs on Wayland cleanly only with this env var. + environment.sessionVariables.MOZ_ENABLE_WAYLAND = "1"; + + # PipeWire for audio out over HDMI / 3.5mm + services.pipewire = { + enable = true; + audio.enable = true; + pulse.enable = true; + alsa.enable = true; + }; + }; +} diff --git a/system/machines/tv/system.nix b/system/machines/tv/system.nix new file mode 100644 index 0000000..6b03cf4 --- /dev/null +++ b/system/machines/tv/system.nix @@ -0,0 +1,90 @@ +{ pkgs, lib, config, ... }: + +{ + system.stateVersion = "25.11"; + + imports = [ ./modules ]; + + modules.system.kiosk = { + enable = true; + user = config.user.name; + url = "about:blank"; + }; + + users.users.${config.user.name} = { + isNormalUser = true; + shell = pkgs.bash; + extraGroups = [ "wheel" "networkmanager" "video" "audio" "input" ]; + openssh.authorizedKeys.keys = [ + "${config.machines.keys.desktop.ssh}" + ]; + }; + + nix = { + channel.enable = false; + package = pkgs.nixVersions.stable; + extraOptions = "experimental-features = nix-command flakes"; + settings = { + auto-optimise-store = true; + trusted-users = [ "${config.user.name}" ]; + }; + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + }; + + environment.systemPackages = with pkgs; [ + vim + git + htop + ]; + + security.sudo = { + wheelNeedsPassword = false; + execWheelOnly = true; + }; + + time.timeZone = "America/New_York"; + + services.timesyncd = { + enable = true; + servers = [ + "0.pool.ntp.org" + "1.pool.ntp.org" + "2.pool.ntp.org" + "3.pool.ntp.org" + ]; + }; + + i18n.defaultLocale = "en_US.UTF-8"; + console.font = "Lat2-Terminus16"; + + networking = { + hostName = "tv"; + useDHCP = false; + interfaces.end0 = { + ipv4.addresses = [{ + address = "192.168.0.176"; + prefixLength = 24; + }]; + }; + defaultGateway = "192.168.0.1"; + nameservers = [ "1.1.1.1" "8.8.8.8" ]; + firewall = { + enable = true; + allowedTCPPorts = [ 22 ]; + }; + }; + + services.openssh = { + enable = true; + startWhenNeeded = true; + settings = { + X11Forwarding = false; + PasswordAuthentication = false; + PermitRootLogin = "no"; + }; + }; +} \ No newline at end of file From 022812a5b7239f69b3db625af9fea9b36babc977 Mon Sep 17 00:00:00 2001 From: Bryan Ramos Date: Sun, 19 Apr 2026 14:11:30 -0400 Subject: [PATCH 2/3] removed power --- system/machines/tv/modules/kiosk/default.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/machines/tv/modules/kiosk/default.nix b/system/machines/tv/modules/kiosk/default.nix index 2584265..c2837f0 100644 --- a/system/machines/tv/modules/kiosk/default.nix +++ b/system/machines/tv/modules/kiosk/default.nix @@ -73,6 +73,12 @@ in # Firefox runs on Wayland cleanly only with this env var. environment.sessionVariables.MOZ_ENABLE_WAYLAND = "1"; + # Pi 4 can't wake from a powered-off state via the remote (no standby + # circuit), so shutting down via the remote's power button strands the + # system until someone reaches the power cable. Ignore the key entirely + # and rely on the TV's own power to hide the display. + services.logind.settings.Login.HandlePowerKey = "ignore"; + # PipeWire for audio out over HDMI / 3.5mm services.pipewire = { enable = true; From 54844e3558b448319c937cd67cf6cabccd16198e Mon Sep 17 00:00:00 2001 From: Bryan Ramos Date: Sun, 19 Apr 2026 14:25:42 -0400 Subject: [PATCH 3/3] fixed ssh --- system/keys/windows/ssh.pub.key | 1 + system/machines/tv/system.nix | 1 + 2 files changed, 2 insertions(+) create mode 100644 system/keys/windows/ssh.pub.key diff --git a/system/keys/windows/ssh.pub.key b/system/keys/windows/ssh.pub.key new file mode 100644 index 0000000..dedd09a --- /dev/null +++ b/system/keys/windows/ssh.pub.key @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBamn0zxYt6mwWLrPkDDXTrfe/tdpGCw6DBLiTw5QuxD bryan@ramos.codes diff --git a/system/machines/tv/system.nix b/system/machines/tv/system.nix index 6b03cf4..6052c6a 100644 --- a/system/machines/tv/system.nix +++ b/system/machines/tv/system.nix @@ -17,6 +17,7 @@ extraGroups = [ "wheel" "networkmanager" "video" "audio" "input" ]; openssh.authorizedKeys.keys = [ "${config.machines.keys.desktop.ssh}" + "${config.machines.keys.windows.ssh}" ]; };