feat(nixos): add opt-in printing and scanning module

New `modules/printing-scanning.nix` exposes `local.printing.*` and `local.scanning.*` (CUPS drivers, SANE with a Canon BJNP `bjnpHosts` escape hatch, shared Avahi mDNS), all off by default so a consumer opts in and a server gets none of it. Imported from `modules/nixos.nix`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 10:40:36 +02:00
co-authored by Claude Opus 4.8
parent 5e1b9332a4
commit 387997f7b6
2 changed files with 81 additions and 1 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
{ config, lib, pkgs, ... }:
{
imports = [ ./shutdown-debug.nix ];
imports = [ ./shutdown-debug.nix ./printing-scanning.nix ];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Deduplicate identical store paths via hardlinks.
+80
View File
@@ -0,0 +1,80 @@
# Printing and scanning, opt-in and off by default. Both are network-facing
# (CUPS, and mDNS discovery for WiFi devices), so a consumer that doesn't want
# them — a server, a hardened host — simply leaves the flags off and gets none
# of it: no avahi, no open firewall, no cups. Nothing to override.
#
# Printers take vendor drivers (the `drivers` option, never hard-coded).
# Scanners are driverless by default via sane-airscan (eSCL/WSD); the one
# exception is Canon's BJNP protocol, whose hosts are listed in `bjnpHosts`.
# Avahi (mDNS) is shared by both and comes up when either is enabled, so WiFi
# devices are discoverable.
{ config, lib, pkgs, ... }:
let
cfg = config.local;
netDiscovery = cfg.printing.enable || cfg.scanning.enable;
in
{
options.local = {
printing = {
enable = lib.mkEnableOption "CUPS printing (WiFi/driverless-friendly)";
drivers = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression "[ pkgs.gutenprint ]";
description = "Extra CUPS driver packages (vendor/model specific).";
};
};
scanning = {
enable =
lib.mkEnableOption "SANE scanning with driverless network scanners (sane-airscan)";
bjnpHosts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = lib.literalExpression ''[ "bjnp://192.168.1.50" ]'';
description = ''
Network scanners reachable over Canon's BJNP protocol, registered
with SANE's `pixma` backend (written to `/etc/sane.d/pixma.conf`).
BJNP is not driverless eSCL/WSD, so `sane-airscan` can't see these;
and BJNP broadcast auto-discovery is usually dropped by the firewall,
so list each device's `bjnp://<ip>` explicitly.
'';
};
};
};
config = lib.mkMerge [
(lib.mkIf cfg.printing.enable {
services.printing = {
enable = true;
drivers = cfg.printing.drivers;
};
})
# sane-airscan = driverless eSCL/WSD; hardware.sane also creates the
# `scanner` group + udev rules. Group membership is per-user → inventory.
# A pixma.conf built the same way the module builds net.conf (a text file
# dropped at /etc/sane.d/, merged by mkSaneConfig) registers BJNP hosts.
(lib.mkIf cfg.scanning.enable {
hardware.sane = {
enable = true;
extraBackends = [ pkgs.sane-airscan ]
++ lib.optional (cfg.scanning.bjnpHosts != [ ]) (pkgs.writeTextFile {
name = "sane-pixma-net-conf";
destination = "/etc/sane.d/pixma.conf";
text = lib.concatMapStrings (h: h + "\n") cfg.scanning.bjnpHosts;
});
};
})
# mDNS so CUPS/SANE can discover WiFi printers and scanners on the LAN.
(lib.mkIf netDiscovery {
services.avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
};
})
];
}