diff --git a/deploy/nixos/hardware/raspberry-pi-4.nix b/deploy/nixos/hardware/raspberry-pi-4.nix index f32bf69..5b136dc 100644 --- a/deploy/nixos/hardware/raspberry-pi-4.nix +++ b/deploy/nixos/hardware/raspberry-pi-4.nix @@ -122,6 +122,41 @@ hardware.enableRedistributableFirmware = true; + # pcscd MUST be enabled, and not because this board has a card reader. + # + # The app constructs @pokusew/pcsclite at startup. That calls + # SCardEstablishContext(), which calls SCardCheckDaemonAvailability(), which + # — when there is no pcscd to find — BUSY-LOOPS in fstatat64 at ~92% CPU + # instead of returning an error. It runs on Electron's main thread, before + # the BrowserWindow is created, so the window never appears and the panel + # stays white forever. Nothing is logged, nothing throws, and V8's own + # inspector cannot be serviced because the thread never yields: CDP + # Debugger.pause and Profiler.stop both hang. It took a native gdb backtrace + # to see it at all: + # + # #0 fstatat64 libc + # #1 SCardCheckDaemonAvailability libpcsclite + # #2 SCardEstablishContext libpcsclite + # #3 PCSCLite::PCSCLite() pcsclite.node + # + # The x86 machines never hit this because upboard.nix and batm3.nix both + # enable pcscd for their actual readers. This module did not, which is the + # entire difference. A running pcscd with no reader attached just idles, so + # this is cheap insurance rather than a claim about the hardware. + services.pcscd.enable = true; + + # pcscd gates client access via polkit; without a rule the `bitspire` service + # user is "Rejected unauthorized PC/SC client". Same wiring as upboard.nix. + security.polkit.extraConfig = '' + polkit.addRule(function(action, subject) { + if ((action.id == "org.debian.pcsc-lite.access_pcsc" || + action.id == "org.debian.pcsc-lite.access_card") && + subject.user == "bitspire") { + return polkit.Result.YES; + } + }); + ''; + # Kiosk: never sleep. systemd.targets = { sleep.enable = false;