An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
at main 122 lines 4.0 kB view raw
1{ lib, pkgs, config, ... }: 2 3let 4 cfg = config.services.ezpds; 5 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. 9 settingsToml = lib.filterAttrs (_: v: v != null) { 10 inherit (cfg.settings) bind_address port data_dir public_url database_url; 11 }; 12 13 generatedConfigFile = (pkgs.formats.toml { }).generate "relay.toml" settingsToml; 14 15 # When configFile is set, bypass the Nix-store-generated TOML entirely. 16 # This is the escape hatch for secret injection via agenix or sops-nix. 17 activeConfigFile = 18 if cfg.configFile != null then cfg.configFile else generatedConfigFile; 19 20in 21{ 22 options.services.ezpds = { 23 enable = lib.mkEnableOption "ezpds relay server"; 24 25 package = lib.mkOption { 26 type = lib.types.package; 27 description = "The ezpds relay package to use."; 28 }; 29 30 configFile = lib.mkOption { 31 type = lib.types.nullOr lib.types.str; 32 default = null; 33 description = '' 34 Path to a relay.toml configuration file. 35 When set, all settings.* options are ignored and this path is 36 passed directly to --config. Use with agenix or sops-nix to 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" ]; 43 ''; 44 }; 45 46 settings = { 47 bind_address = lib.mkOption { 48 type = lib.types.str; 49 default = "0.0.0.0"; 50 description = "IP address to bind the relay HTTP server to."; 51 }; 52 53 port = lib.mkOption { 54 type = lib.types.port; 55 default = 8080; 56 description = "TCP port to bind the relay HTTP server to."; 57 }; 58 59 data_dir = lib.mkOption { 60 type = lib.types.str; 61 default = "/var/lib/ezpds"; 62 description = '' 63 Path to the relay data directory. Must be writable by the ezpds user. 64 Uses lib.types.str (not lib.types.path) to preserve the value as a 65 literal string and avoid Nix store coercion of runtime paths. 66 ''; 67 }; 68 69 public_url = lib.mkOption { 70 type = lib.types.str; 71 description = '' 72 Public URL where this relay is reachable (e.g. https://relay.example.com). 73 Required Nix evaluation fails if this option is not set. 74 ''; 75 }; 76 77 database_url = lib.mkOption { 78 type = lib.types.nullOr lib.types.str; 79 default = null; 80 description = '' 81 SQLite database URL. When null (the default), the relay derives 82 the database path from data_dir. Omitted from the generated 83 relay.toml when null. 84 ''; 85 }; 86 }; 87 }; 88 89 config = lib.mkIf cfg.enable { 90 users.users.ezpds = { 91 isSystemUser = true; 92 group = "ezpds"; 93 description = "ezpds relay service user"; 94 }; 95 96 users.groups.ezpds = { }; 97 98 systemd.services.ezpds = { 99 description = "ezpds relay server"; 100 wantedBy = [ "multi-user.target" ]; 101 after = [ "network.target" ]; 102 103 serviceConfig = { 104 User = "ezpds"; 105 Group = "ezpds"; 106 ExecStart = "${cfg.package}/bin/relay --config '${activeConfigFile}'"; 107 StateDirectory = "ezpds"; 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 ]; 114 Restart = "on-failure"; 115 PrivateTmp = true; 116 ProtectSystem = "strict"; 117 ProtectHome = true; 118 NoNewPrivileges = true; 119 }; 120 }; 121 }; 122}