# services/frigate.nix — Local NVR with AI object detection # Consumes go2rtc streams; no MQTT / Home Assistant dependency. # Authentication delegated to Authelia by hijacking the /auth location # that the upstream Frigate module bakes into every nginx location block. # # The Frigate NixOS module hardcodes `auth_request /auth` + `auth_request_set` # in every nginx location, reading $upstream_http_remote_role from the auth # subrequest response. Authelia doesn't return a Remote-Role header, so we # use a tiny local-only nginx wrapper (port 9092) that proxies to Authelia # and injects `Remote-Role: admin` into the response. The /auth location # then points at the wrapper instead of Authelia directly. { config, lib, ... }: { config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") { services.frigate = { enable = true; hostname = "frigate.nordhammer.it"; checkConfig = false; settings = { mqtt.enabled = false; proxy.header_map = { user = "Remote-User"; role = "Remote-Role"; }; cameras.kids_bedroom = { enabled = true; ffmpeg.inputs = [{ path = "rtsp://127.0.0.1:8554/kids_bedroom"; roles = [ "detect" "record" "audio" ]; }]; detect = { enabled = true; width = 1280; height = 720; }; audio.enabled = true; }; record = { enabled = true; retain = { days = 7; mode = "motion"; }; }; snapshots = { enabled = true; retain.default = 14; }; }; }; # Local-only auth wrapper: proxies to Authelia and adds Remote-Role header # so that auth_request_set $role $upstream_http_remote_role gets "admin". services.nginx.virtualHosts."frigate-auth-wrapper" = { listen = [{ addr = "127.0.0.1"; port = 9092; }]; locations."/" = { proxyPass = "http://127.0.0.1:9091/api/verify"; extraConfig = '' proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URL $http_x_original_url; proxy_set_header X-Forwarded-Method $http_x_forwarded_method; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header X-Forwarded-Host $http_x_forwarded_host; proxy_set_header X-Forwarded-Uri $http_x_forwarded_uri; proxy_set_header X-Forwarded-For $http_x_forwarded_for; add_header Remote-Role admin always; ''; }; }; services.nginx.virtualHosts."frigate.nordhammer.it" = { useACMEHost = "nordhammer.it"; forceSSL = true; # Point /auth at the wrapper (9092) instead of Authelia (9091) directly. # The wrapper proxies to Authelia and injects Remote-Role: admin. locations."/auth" = lib.mkForce { proxyPass = "http://127.0.0.1:9092/"; extraConfig = '' internal; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Forwarded-Method $request_method; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Uri $request_uri; proxy_set_header X-Forwarded-For $remote_addr; ''; }; # The Frigate module serves the frontend from "/" without auth_request, # so unauthenticated users see a broken loading page instead of being # redirected to Authelia. Gate it behind auth too. locations."/".extraConfig = lib.mkAfter '' auth_request /auth; ''; # Redirect 401 → Authelia login portal extraConfig = lib.mkAfter '' error_page 401 =302 https://auth.nordhammer.it/?rd=$scheme://$http_host$request_uri; ''; }; # GPU access for hardware-accelerated ffmpeg decoding users.users.frigate.extraGroups = [ "video" "render" ]; }; }