An easy-to-host PDS on the ATProtocol, MacOS. Grandma-approved.

fix(MM-135): address PR review — ReadWritePaths, ExecStart quoting, arch guard

- Add ReadWritePaths = [ cfg.settings.data_dir ] so custom data_dir paths
are writable under ProtectSystem=strict (no-op for the default /var/lib/ezpds)
- Quote activeConfigFile in ExecStart: --config '${path}' handles spaces per
systemd's unit file quoting rules
- Guard nixosModules package default with self.packages ? ${pkgs.system} so
unsupported architectures surface the module's own "option not set" error
rather than an opaque attrset attribute-missing error
- Clarify filterAttrs comment: filter drops all nulls, not just database_url
- Document secrets-service ordering for agenix/sops-nix configFile users
- Update nix/CLAUDE.md to document ReadWritePaths guarantee

authored by malpercio.dev and committed by

Tangled cf833fbd 9d098e76

+22 -5
+7 -2
flake.nix
··· 68 68 # self is captured from the outputs function closure. 69 69 nixosModules.default = { lib, pkgs, ... }: { 70 70 imports = [ ./nix/module.nix ]; 71 - config.services.ezpds.package = 72 - lib.mkDefault self.packages.${pkgs.system}.relay; 71 + # Guard the package default: on unsupported architectures self.packages 72 + # won't have an entry, and the raw attrset access would produce an 73 + # opaque "attribute missing" error. When the guard fails, NixOS surfaces 74 + # its own "option services.ezpds.package is not set" message instead. 75 + config.services.ezpds.package = lib.mkIf 76 + (self.packages ? ${pkgs.system}) 77 + (lib.mkDefault self.packages.${pkgs.system}.relay); 73 78 }; 74 79 }; 75 80 }
+1
nix/CLAUDE.md
··· 19 19 - Dedicated `ezpds` system user/group created automatically 20 20 - systemd service runs with hardening: ProtectSystem=strict, ProtectHome, NoNewPrivileges, PrivateTmp 21 21 - StateDirectory "ezpds" managed by systemd (mode 0750) 22 + - ReadWritePaths always includes cfg.settings.data_dir — required when data_dir is not /var/lib/ezpds, since ProtectSystem=strict blocks writes elsewhere 22 23 - **Expects**: Caller provides `services.ezpds.settings.public_url` (or a complete `configFile`) 23 24 24 25 ### docker.nix
+14 -3
nix/module.nix
··· 3 3 let 4 4 cfg = config.services.ezpds; 5 5 6 - # Build the TOML attrset, omitting database_url when null. 7 - # When null, the relay binary derives the database path from data_dir. 6 + # Build the TOML attrset, omitting any null values (currently only 7 + # database_url can be null). When null, the relay binary derives the 8 + # database path from data_dir. 8 9 settingsToml = lib.filterAttrs (_: v: v != null) { 9 10 inherit (cfg.settings) bind_address port data_dir public_url database_url; 10 11 }; ··· 34 35 When set, all settings.* options are ignored and this path is 35 36 passed directly to --config. Use with agenix or sops-nix to 36 37 keep secrets outside the world-readable Nix store. 38 + 39 + When using agenix or sops-nix, ensure the secrets service runs 40 + before ezpds to avoid a startup race: 41 + systemd.services.ezpds.after = [ "agenix.service" ]; 42 + systemd.services.ezpds.wants = [ "agenix.service" ]; 37 43 ''; 38 44 }; 39 45 ··· 97 103 serviceConfig = { 98 104 User = "ezpds"; 99 105 Group = "ezpds"; 100 - ExecStart = "${cfg.package}/bin/relay --config ${activeConfigFile}"; 106 + ExecStart = "${cfg.package}/bin/relay --config '${activeConfigFile}'"; 101 107 StateDirectory = "ezpds"; 102 108 StateDirectoryMode = "0750"; 109 + # Extend write access to custom data_dir paths. When data_dir is the 110 + # default (/var/lib/ezpds), StateDirectory already covers it and this 111 + # is a no-op. For any other path, ProtectSystem=strict would otherwise 112 + # block all writes at runtime. 113 + ReadWritePaths = [ cfg.settings.data_dir ]; 103 114 Restart = "on-failure"; 104 115 PrivateTmp = true; 105 116 ProtectSystem = "strict";