[MIRROR ONLY] A correct and efficient ATProto blob proxy for secure content delivery. codeberg.org/Blooym/porxie
36
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: add nix+direnv dev setup

Lyna 8b7dd331 ea849b59

+413 -350
+1
.envrc
··· 1 + use flake
+4 -1
.gitignore
··· 1 - /target 1 + target/ 2 + .direnv/ 3 + 4 + 2 5 test_server.py
+3 -3
flake.lock
··· 2 2 "nodes": { 3 3 "nixpkgs": { 4 4 "locked": { 5 - "lastModified": 1772082373, 6 - "narHash": "sha256-wySf8a6hvuqgFdwvvzPPTARBCMLDz7WFAufGkllD1M4=", 5 + "lastModified": 1775823930, 6 + "narHash": "sha256-ALT447J7FcxP/97J01A/gp/hgdO5lXRsm+zLMt+gIjc=", 7 7 "owner": "nixos", 8 8 "repo": "nixpkgs", 9 - "rev": "26eaeac4e409d7b5a6bf6f90a2a2dc223c78d915", 9 + "rev": "8c11f88bb9573a10a7d6bf87161ef08455ac70b9", 10 10 "type": "github" 11 11 }, 12 12 "original": {
+405 -346
flake.nix
··· 5 5 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 6 }; 7 7 8 - outputs = { self, nixpkgs, ...}: let 9 - forAllSystems = function: 10 - nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed ( 11 - system: (function system nixpkgs.legacyPackages.${system}) 8 + outputs = 9 + { self, nixpkgs, ... }: 10 + let 11 + forAllSystems = 12 + function: 13 + nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed ( 14 + system: (function system nixpkgs.legacyPackages.${system}) 15 + ); 16 + in 17 + { 18 + devShells = forAllSystems ( 19 + system: pkgs: { 20 + default = pkgs.mkShell { 21 + packages = with pkgs; [ 22 + rustc 23 + cargo 24 + rustfmt 25 + clippy 26 + bashInteractive 27 + ]; 28 + }; 29 + } 12 30 ); 13 - in { 14 - packages = forAllSystems (system: pkgs: { 15 - porxie = pkgs.callPackage ({ lib, rustPlatform }: let 16 - toml = (lib.importTOML ./Cargo.toml).package; 17 - in rustPlatform.buildRustPackage { 18 - pname = "porxie"; 19 - inherit (toml) version; 20 31 21 - src = lib.fileset.toSource { 22 - root = ./.; 23 - fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ./.)) ( 24 - lib.fileset.unions [ 25 - ./Cargo.toml 26 - ./Cargo.lock 27 - ./src 28 - ] 29 - ); 30 - }; 32 + packages = forAllSystems ( 33 + system: pkgs: { 34 + porxie = pkgs.callPackage ( 35 + { lib, rustPlatform }: 36 + let 37 + toml = (lib.importTOML ./Cargo.toml).package; 38 + in 39 + rustPlatform.buildRustPackage { 40 + pname = "porxie"; 41 + inherit (toml) version; 31 42 32 - cargoLock.lockFile = ./Cargo.lock; 43 + src = lib.fileset.toSource { 44 + root = ./.; 45 + fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ./.)) ( 46 + lib.fileset.unions [ 47 + ./Cargo.toml 48 + ./Cargo.lock 49 + ./src 50 + ] 51 + ); 52 + }; 33 53 34 - meta = { 35 - inherit (toml) homepage description; 36 - mainProgram = "porxie"; 37 - license = lib.licenses.agpl3Only; 38 - maintainers = with lib.maintainers; [ "Blooym" ]; 39 - }; 40 - }) { }; 54 + cargoLock.lockFile = ./Cargo.lock; 41 55 42 - default = self.packages.${system}.porxie; 43 - }); 56 + meta = { 57 + inherit (toml) homepage description; 58 + mainProgram = "porxie"; 59 + license = lib.licenses.agpl3Only; 60 + maintainers = with lib.maintainers; [ "Blooym" ]; 61 + }; 62 + } 63 + ) { }; 44 64 45 - nixosModules = { 46 - porxie = ({ config, lib, pkgs, ... }: let 47 - inherit (lib) types mkOption; 48 - cfg = config.services.porxie; 49 - in { 50 - _class = "nixos"; 65 + default = self.packages.${system}.porxie; 66 + } 67 + ); 51 68 52 - options.services.porxie = { 53 - enable = lib.mkEnableOption "Porxie"; 69 + nixosModules = { 70 + porxie = ( 71 + { 72 + config, 73 + lib, 74 + pkgs, 75 + ... 76 + }: 77 + let 78 + inherit (lib) types mkOption; 79 + cfg = config.services.porxie; 80 + in 81 + { 82 + _class = "nixos"; 54 83 55 - package = mkOption { 56 - type = types.package; 57 - default = self.packages.${pkgs.stdenv.hostPlatform.system}.porxie; 58 - defaultText = lib.literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.porxie"; 59 - description = "The Porxie package to use."; 60 - }; 84 + options.services.porxie = { 85 + enable = lib.mkEnableOption "Porxie"; 61 86 62 - user = mkOption { 63 - type = types.str; 64 - default = "porxie"; 65 - description = "User that Porxie runs under."; 66 - }; 87 + package = mkOption { 88 + type = types.package; 89 + default = self.packages.${pkgs.stdenv.hostPlatform.system}.porxie; 90 + defaultText = lib.literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.porxie"; 91 + description = "The Porxie package to use."; 92 + }; 67 93 68 - group = mkOption { 69 - type = types.str; 70 - default = "porxie"; 71 - description = "Group that Porxie runs under."; 72 - }; 94 + user = mkOption { 95 + type = types.str; 96 + default = "porxie"; 97 + description = "User that Porxie runs under."; 98 + }; 73 99 74 - environmentFiles = mkOption { 75 - type = types.listOf types.path; 76 - default = []; 77 - description = '' 78 - Files to load environment variables from. Use for secrets such as 79 - {env}`PORXIE_SERVER_AUTH_TOKEN` and {env}`PORXIE_POLICY_REQUEST_HEADERS`. 80 - ''; 81 - }; 100 + group = mkOption { 101 + type = types.str; 102 + default = "porxie"; 103 + description = "Group that Porxie runs under."; 104 + }; 82 105 83 - settings = mkOption { 84 - type = types.submodule { 85 - freeformType = types.attrsOf types.str; 106 + environmentFiles = mkOption { 107 + type = types.listOf types.path; 108 + default = [ ]; 109 + description = '' 110 + Files to load environment variables from. Use for secrets such as 111 + {env}`PORXIE_SERVER_AUTH_TOKEN` and {env}`PORXIE_POLICY_REQUEST_HEADERS`. 112 + ''; 113 + }; 86 114 87 - options = { 88 - server = { 89 - address = mkOption { 90 - type = types.str; 91 - default = "ip:127.0.0.1:6314"; 92 - description = '' 93 - Address to bind the server to. 115 + settings = mkOption { 116 + type = types.submodule { 117 + freeformType = types.attrsOf types.str; 94 118 95 - Use the 'ip:' prefix for an IP address (e.g. 'ip:127.0.0.1:6314'), or on 96 - UNIX systems, the 'unix:' prefix for a UNIX socket path 97 - (e.g. 'unix:/run/porxie.sock'). 98 - ''; 99 - }; 119 + options = { 120 + server = { 121 + address = mkOption { 122 + type = types.str; 123 + default = "ip:127.0.0.1:6314"; 124 + description = '' 125 + Address to bind the server to. 100 126 101 - authToken = mkOption { 102 - type = types.nullOr types.str; 103 - default = null; 104 - description = '' 105 - Bearer token for authenticating admin requests. 127 + Use the 'ip:' prefix for an IP address (e.g. 'ip:127.0.0.1:6314'), or on 128 + UNIX systems, the 'unix:' prefix for a UNIX socket path 129 + (e.g. 'unix:/run/porxie.sock'). 130 + ''; 131 + }; 106 132 107 - When unset, all authenticated endpoints will reject requests with HTTP 401. 108 - Should be set via {option}`environmentFiles` rather than directly. 109 - ''; 110 - }; 111 - }; 133 + authToken = mkOption { 134 + type = types.nullOr types.str; 135 + default = null; 136 + description = '' 137 + Bearer token for authenticating admin requests. 112 138 113 - blob = { 114 - allowedMimetypes = mkOption { 115 - type = types.listOf types.str; 116 - default = [ "image/*" ]; 117 - description = '' 118 - Blob mimetypes that can be served. 139 + When unset, all authenticated endpoints will reject requests with HTTP 401. 140 + Should be set via {option}`environmentFiles` rather than directly. 141 + ''; 142 + }; 143 + }; 119 144 120 - Validation is done loosely via content inference. Further validation can be 121 - done by a layer above this proxy, such as an image transformation service. 122 - When inference fails, the blob's type falls back to 123 - `application/octet-stream`. When that type is allowed, blobs failing 124 - inference can still be served. 125 - ''; 126 - }; 145 + blob = { 146 + allowedMimetypes = mkOption { 147 + type = types.listOf types.str; 148 + default = [ "image/*" ]; 149 + description = '' 150 + Blob mimetypes that can be served. 127 151 128 - maxSize = mkOption { 129 - type = types.str; 130 - default = "50mb"; 131 - description = '' 132 - Maximum blob size that can be fetched and served. 152 + Validation is done loosely via content inference. Further validation can be 153 + done by a layer above this proxy, such as an image transformation service. 154 + When inference fails, the blob's type falls back to 155 + `application/octet-stream`. When that type is allowed, blobs failing 156 + inference can still be served. 157 + ''; 158 + }; 133 159 134 - Blobs that exceed this limit will return HTTP 413. Setting this too high can 135 - exhaust process or system memory. The minimum value is 512kb. 136 - ''; 137 - }; 160 + maxSize = mkOption { 161 + type = types.str; 162 + default = "50mb"; 163 + description = '' 164 + Maximum blob size that can be fetched and served. 138 165 139 - cacheHeader = mkOption { 140 - type = types.str; 141 - default = "public, max-age=604800, must-revalidate, immutable"; 142 - description = '' 143 - The Cache-Control header value to send alongside blob responses. 166 + Blobs that exceed this limit will return HTTP 413. Setting this too high can 167 + exhaust process or system memory. The minimum value is 512kb. 168 + ''; 169 + }; 144 170 145 - This does not affect internal cache lifetimes, only how downstream clients 146 - such as CDNs and browsers are instructed to cache responses. Intermediary 147 - caches may need to be cleared manually for changes to take effect quickly. 148 - ''; 149 - }; 171 + cacheHeader = mkOption { 172 + type = types.str; 173 + default = "public, max-age=604800, must-revalidate, immutable"; 174 + description = '' 175 + The Cache-Control header value to send alongside blob responses. 150 176 151 - processingTimeout = mkOption { 152 - type = types.str; 153 - default = "1m"; 154 - description = '' 155 - Maximum duration a blob can be processed by this server before aborting. 156 - ''; 157 - }; 177 + This does not affect internal cache lifetimes, only how downstream clients 178 + such as CDNs and browsers are instructed to cache responses. Intermediary 179 + caches may need to be cleared manually for changes to take effect quickly. 180 + ''; 181 + }; 158 182 159 - httpTimeout = mkOption { 160 - type = types.str; 161 - default = "30s"; 162 - description = '' 163 - Maximum duration before blob fetch requests are timed out. 164 - ''; 165 - }; 183 + processingTimeout = mkOption { 184 + type = types.str; 185 + default = "1m"; 186 + description = '' 187 + Maximum duration a blob can be processed by this server before aborting. 188 + ''; 189 + }; 166 190 167 - httpConnectTimeout = mkOption { 168 - type = types.str; 169 - default = "10s"; 170 - description = '' 171 - Maximum duration before an attempted connection to a blob upstream is aborted. 191 + httpTimeout = mkOption { 192 + type = types.str; 193 + default = "30s"; 194 + description = '' 195 + Maximum duration before blob fetch requests are timed out. 196 + ''; 197 + }; 172 198 173 - This value should be lower than {option}`settings.blob.httpTimeout`. 174 - ''; 175 - }; 176 - }; 199 + httpConnectTimeout = mkOption { 200 + type = types.str; 201 + default = "10s"; 202 + description = '' 203 + Maximum duration before an attempted connection to a blob upstream is aborted. 177 204 178 - identity = { 179 - plcUrl = mkOption { 180 - type = types.str; 181 - default = "https://plc.directory"; 182 - description = '' 183 - URL of the PLC instance used for `did:plc` lookups. 205 + This value should be lower than {option}`settings.blob.httpTimeout`. 206 + ''; 207 + }; 208 + }; 184 209 185 - Can typically be left as default unless using a custom or local development 186 - setup. 187 - ''; 188 - }; 210 + identity = { 211 + plcUrl = mkOption { 212 + type = types.str; 213 + default = "https://plc.directory"; 214 + description = '' 215 + URL of the PLC instance used for `did:plc` lookups. 189 216 190 - httpTimeout = mkOption { 191 - type = types.str; 192 - default = "10s"; 193 - description = '' 194 - Maximum duration before identity resolution requests are timed out. 195 - ''; 196 - }; 217 + Can typically be left as default unless using a custom or local development 218 + setup. 219 + ''; 220 + }; 197 221 198 - httpConnectTimeout = mkOption { 199 - type = types.str; 200 - default = "8s"; 201 - description = '' 202 - Maximum duration before a connection attempt to an identity upstream is aborted. 222 + httpTimeout = mkOption { 223 + type = types.str; 224 + default = "10s"; 225 + description = '' 226 + Maximum duration before identity resolution requests are timed out. 227 + ''; 228 + }; 229 + 230 + httpConnectTimeout = mkOption { 231 + type = types.str; 232 + default = "8s"; 233 + description = '' 234 + Maximum duration before a connection attempt to an identity upstream is aborted. 203 235 204 - This value should be lower than {option}`settings.identity.httpTimeout`. 205 - ''; 206 - }; 207 - }; 236 + This value should be lower than {option}`settings.identity.httpTimeout`. 237 + ''; 238 + }; 239 + }; 208 240 209 - cache = { 210 - allocation = mkOption { 211 - type = types.str; 212 - default = "512mb"; 213 - description = '' 214 - Total memory allocation for the internal cache. 241 + cache = { 242 + allocation = mkOption { 243 + type = types.str; 244 + default = "512mb"; 245 + description = '' 246 + Total memory allocation for the internal cache. 215 247 216 - Blobs are cached using an LFU policy. The most frequently requested blobs 217 - are kept longest when the cache approaches its limit. 248 + Blobs are cached using an LFU policy. The most frequently requested blobs 249 + are kept longest when the cache approaches its limit. 218 250 219 - For production deployments, a CDN or caching layer in front of this server 220 - is recommended for lower latency and better global availability. 251 + For production deployments, a CDN or caching layer in front of this server 252 + is recommended for lower latency and better global availability. 221 253 222 - Setting this too high can exhaust process or system memory. The minimum 223 - value is 8mb. 224 - ''; 225 - }; 254 + Setting this too high can exhaust process or system memory. The minimum 255 + value is 8mb. 256 + ''; 257 + }; 226 258 227 - blobTti = mkOption { 228 - type = types.str; 229 - default = "7days"; 230 - description = '' 231 - How long blobs can be idle in the cache before expiring. 232 - ''; 233 - }; 259 + blobTti = mkOption { 260 + type = types.str; 261 + default = "7days"; 262 + description = '' 263 + How long blobs can be idle in the cache before expiring. 264 + ''; 265 + }; 234 266 235 - ownershipTtl = mkOption { 236 - type = types.str; 237 - default = "1day"; 238 - description = '' 239 - How long blob ownership can be cached before expiring. 240 - ''; 241 - }; 267 + ownershipTtl = mkOption { 268 + type = types.str; 269 + default = "1day"; 270 + description = '' 271 + How long blob ownership can be cached before expiring. 272 + ''; 273 + }; 242 274 243 - policyTtl = mkOption { 244 - type = types.str; 245 - default = "1h"; 246 - description = '' 247 - How long policy decisions can be cached before expiring. 248 - ''; 249 - }; 275 + policyTtl = mkOption { 276 + type = types.str; 277 + default = "1h"; 278 + description = '' 279 + How long policy decisions can be cached before expiring. 280 + ''; 281 + }; 250 282 251 - identityTtl = mkOption { 252 - type = types.str; 253 - default = "1h"; 254 - description = '' 255 - How long identity lookups (DID resolution, etc) can be cached before expiring. 256 - ''; 257 - }; 258 - }; 283 + identityTtl = mkOption { 284 + type = types.str; 285 + default = "1h"; 286 + description = '' 287 + How long identity lookups (DID resolution, etc) can be cached before expiring. 288 + ''; 289 + }; 290 + }; 259 291 260 - policy = { 261 - url = mkOption { 262 - type = types.nullOr types.str; 263 - default = null; 264 - description = '' 265 - Policy service URL that DID+CID pairs will be checked against. 292 + policy = { 293 + url = mkOption { 294 + type = types.nullOr types.str; 295 + default = null; 296 + description = '' 297 + Policy service URL that DID+CID pairs will be checked against. 266 298 267 - Requests are sent as HTTP GET <url>/<did>/<cid>. 299 + Requests are sent as HTTP GET <url>/<did>/<cid>. 268 300 269 - The service is expected to return HTTP 200 (OK) if permitted or HTTP 410 270 - (GONE) if restricted. 271 - ''; 272 - }; 301 + The service is expected to return HTTP 200 (OK) if permitted or HTTP 410 302 + (GONE) if restricted. 303 + ''; 304 + }; 273 305 274 - requestHeaders = mkOption { 275 - type = types.listOf types.str; 276 - default = []; 277 - description = '' 278 - Headers sent alongside all requests to the policy service. 306 + requestHeaders = mkOption { 307 + type = types.listOf types.str; 308 + default = [ ]; 309 + description = '' 310 + Headers sent alongside all requests to the policy service. 279 311 280 - Each header must be in the format "Name: value". When setting via 281 - environment variable, headers are pipe-separated (|). 312 + Each header must be in the format "Name: value". When setting via 313 + environment variable, headers are pipe-separated (|). 282 314 283 - Should be set via {option}`environmentFiles` for sensitive values such as 284 - API keys. 285 - ''; 286 - }; 315 + Should be set via {option}`environmentFiles` for sensitive values such as 316 + API keys. 317 + ''; 318 + }; 287 319 288 - failOpen = mkOption { 289 - type = types.bool; 290 - default = false; 291 - description = '' 292 - Allow requests to proceed if the policy service is unavailable or returns 293 - an unexpected status code. 320 + failOpen = mkOption { 321 + type = types.bool; 322 + default = false; 323 + description = '' 324 + Allow requests to proceed if the policy service is unavailable or returns 325 + an unexpected status code. 294 326 295 - Warning: enabling this means restricted blobs may be served when the 296 - policy service is unreachable. 297 - ''; 298 - }; 327 + Warning: enabling this means restricted blobs may be served when the 328 + policy service is unreachable. 329 + ''; 330 + }; 299 331 300 - httpTimeout = mkOption { 301 - type = types.str; 302 - default = "30s"; 303 - description = '' 304 - Maximum duration before policy service requests are timed out. 305 - ''; 306 - }; 332 + httpTimeout = mkOption { 333 + type = types.str; 334 + default = "30s"; 335 + description = '' 336 + Maximum duration before policy service requests are timed out. 337 + ''; 338 + }; 307 339 308 - httpConnectTimeout = mkOption { 309 - type = types.str; 310 - default = "10s"; 311 - description = '' 312 - Maximum duration before an attempted connection to the policy service is aborted. 340 + httpConnectTimeout = mkOption { 341 + type = types.str; 342 + default = "10s"; 343 + description = '' 344 + Maximum duration before an attempted connection to the policy service is aborted. 313 345 314 - This value should be lower than {option}`settings.policy.httpTimeout`. 315 - ''; 346 + This value should be lower than {option}`settings.policy.httpTimeout`. 347 + ''; 348 + }; 349 + }; 316 350 }; 317 351 }; 352 + 353 + default = { }; 354 + description = '' 355 + Configuration for Porxie. Refer to 356 + <https://codeberg.org/Blooym/porxie/src/branch/main/README.md#configuration> 357 + for further guidance. 358 + 359 + Secrets such as {option}`settings.server.authToken` should be set via 360 + {option}`environmentFiles`. 361 + ''; 318 362 }; 319 363 }; 320 364 321 - default = {}; 322 - description = '' 323 - Configuration for Porxie. Refer to 324 - <https://codeberg.org/Blooym/porxie/src/branch/main/README.md#configuration> 325 - for further guidance. 365 + config = lib.mkIf cfg.enable { 366 + users = { 367 + users.${cfg.user} = { 368 + isSystemUser = true; 369 + inherit (cfg) group; 370 + }; 371 + groups.${cfg.group} = { }; 372 + }; 326 373 327 - Secrets such as {option}`settings.server.authToken` should be set via 328 - {option}`environmentFiles`. 329 - ''; 330 - }; 331 - }; 374 + systemd.services.porxie = 375 + let 376 + knownKeys = [ 377 + "server" 378 + "blob" 379 + "identity" 380 + "cache" 381 + "policy" 382 + ]; 383 + env = 384 + (lib.filterAttrs (k: _: !(builtins.elem k knownKeys)) cfg.settings) 385 + // lib.filterAttrs (_: v: v != null) { 386 + PORXIE_SERVER_ADDRESS = cfg.settings.server.address; 387 + PORXIE_SERVER_AUTH_TOKEN = cfg.settings.server.authToken; 388 + PORXIE_BLOB_ALLOWED_MIMETYPES = 389 + if cfg.settings.blob.allowedMimetypes != [ ] then 390 + lib.concatStringsSep "," cfg.settings.blob.allowedMimetypes 391 + else 392 + null; 393 + PORXIE_BLOB_MAX_SIZE = cfg.settings.blob.maxSize; 394 + PORXIE_BLOB_CACHE_HEADER = cfg.settings.blob.cacheHeader; 395 + PORXIE_BLOB_PROCESSING_TIMEOUT = cfg.settings.blob.processingTimeout; 396 + PORXIE_BLOB_HTTP_TIMEOUT = cfg.settings.blob.httpTimeout; 397 + PORXIE_BLOB_HTTP_CONNECT_TIMEOUT = cfg.settings.blob.httpConnectTimeout; 398 + PORXIE_IDENTITY_PLC_URL = cfg.settings.identity.plcUrl; 399 + PORXIE_IDENTITY_HTTP_TIMEOUT = cfg.settings.identity.httpTimeout; 400 + PORXIE_IDENTITY_HTTP_CONNECT_TIMEOUT = cfg.settings.identity.httpConnectTimeout; 401 + PORXIE_CACHE_ALLOCATION = cfg.settings.cache.allocation; 402 + PORXIE_CACHE_BLOB_TTI = cfg.settings.cache.blobTti; 403 + PORXIE_CACHE_OWNERSHIP_TTL = cfg.settings.cache.ownershipTtl; 404 + PORXIE_CACHE_POLICY_TTL = cfg.settings.cache.policyTtl; 405 + PORXIE_CACHE_IDENTITY_TTL = cfg.settings.cache.identityTtl; 406 + PORXIE_POLICY_URL = cfg.settings.policy.url; 407 + PORXIE_POLICY_REQUEST_HEADERS = 408 + if cfg.settings.policy.requestHeaders != [ ] then 409 + lib.concatStringsSep "|" cfg.settings.policy.requestHeaders 410 + else 411 + null; 412 + PORXIE_POLICY_FAIL_OPEN = if cfg.settings.policy.failOpen then "true" else null; 413 + PORXIE_POLICY_HTTP_TIMEOUT = cfg.settings.policy.httpTimeout; 414 + PORXIE_POLICY_HTTP_CONNECT_TIMEOUT = cfg.settings.policy.httpConnectTimeout; 415 + }; 416 + in 417 + { 418 + description = "Porxie atproto blob proxy"; 419 + after = [ "network-online.target" ]; 420 + wants = [ "network-online.target" ]; 421 + wantedBy = [ "multi-user.target" ]; 332 422 333 - config = lib.mkIf cfg.enable { 334 - users = { 335 - users.${cfg.user} = { 336 - isSystemUser = true; 337 - inherit (cfg) group; 338 - }; 339 - groups.${cfg.group} = { }; 340 - }; 423 + serviceConfig = { 424 + User = cfg.user; 425 + Group = cfg.group; 426 + ExecStart = lib.getExe cfg.package; 427 + Environment = lib.mapAttrsToList (k: v: "${k}=${v}") env; 428 + EnvironmentFile = cfg.environmentFiles; 429 + Restart = "on-failure"; 430 + RestartSec = 5; 341 431 342 - systemd.services.porxie = let 343 - knownKeys = [ "server" "blob" "identity" "cache" "policy" ]; 344 - env = (lib.filterAttrs (k: _: !(builtins.elem k knownKeys)) cfg.settings) // lib.filterAttrs (_: v: v != null) { 345 - PORXIE_SERVER_ADDRESS = cfg.settings.server.address; 346 - PORXIE_SERVER_AUTH_TOKEN = cfg.settings.server.authToken; 347 - PORXIE_BLOB_ALLOWED_MIMETYPES = if cfg.settings.blob.allowedMimetypes != [] then lib.concatStringsSep "," cfg.settings.blob.allowedMimetypes else null; 348 - PORXIE_BLOB_MAX_SIZE = cfg.settings.blob.maxSize; 349 - PORXIE_BLOB_CACHE_HEADER = cfg.settings.blob.cacheHeader; 350 - PORXIE_BLOB_PROCESSING_TIMEOUT = cfg.settings.blob.processingTimeout; 351 - PORXIE_BLOB_HTTP_TIMEOUT = cfg.settings.blob.httpTimeout; 352 - PORXIE_BLOB_HTTP_CONNECT_TIMEOUT = cfg.settings.blob.httpConnectTimeout; 353 - PORXIE_IDENTITY_PLC_URL = cfg.settings.identity.plcUrl; 354 - PORXIE_IDENTITY_HTTP_TIMEOUT = cfg.settings.identity.httpTimeout; 355 - PORXIE_IDENTITY_HTTP_CONNECT_TIMEOUT = cfg.settings.identity.httpConnectTimeout; 356 - PORXIE_CACHE_ALLOCATION = cfg.settings.cache.allocation; 357 - PORXIE_CACHE_BLOB_TTI = cfg.settings.cache.blobTti; 358 - PORXIE_CACHE_OWNERSHIP_TTL = cfg.settings.cache.ownershipTtl; 359 - PORXIE_CACHE_POLICY_TTL = cfg.settings.cache.policyTtl; 360 - PORXIE_CACHE_IDENTITY_TTL = cfg.settings.cache.identityTtl; 361 - PORXIE_POLICY_URL = cfg.settings.policy.url; 362 - PORXIE_POLICY_REQUEST_HEADERS = if cfg.settings.policy.requestHeaders != [] then lib.concatStringsSep "|" cfg.settings.policy.requestHeaders else null; 363 - PORXIE_POLICY_FAIL_OPEN = if cfg.settings.policy.failOpen then "true" else null; 364 - PORXIE_POLICY_HTTP_TIMEOUT = cfg.settings.policy.httpTimeout; 365 - PORXIE_POLICY_HTTP_CONNECT_TIMEOUT = cfg.settings.policy.httpConnectTimeout; 366 - }; 367 - in { 368 - description = "Porxie atproto blob proxy"; 369 - after = [ "network-online.target" ]; 370 - wants = [ "network-online.target" ]; 371 - wantedBy = [ "multi-user.target" ]; 432 + RuntimeDirectory = "porxie"; 433 + RuntimeDirectoryMode = "0750"; 372 434 373 - serviceConfig = { 374 - User = cfg.user; 375 - Group = cfg.group; 376 - ExecStart = lib.getExe cfg.package; 377 - Environment = lib.mapAttrsToList (k: v: "${k}=${v}") env; 378 - EnvironmentFile = cfg.environmentFiles; 379 - Restart = "on-failure"; 380 - RestartSec = 5; 381 - 382 - RuntimeDirectory = "porxie"; 383 - RuntimeDirectoryMode = "0750"; 384 - 385 - CapabilityBoundingSet = ""; 386 - AmbientCapabilities = ""; 387 - NoNewPrivileges = true; 388 - ReadOnlyRootFilesystem = true; 389 - ProtectSystem = "strict"; 390 - ProtectHome = true; 391 - PrivateTmp = true; 392 - PrivateDevices = true; 393 - ProtectKernelTunables = true; 394 - ProtectKernelModules = true; 395 - ProtectKernelLogs = true; 396 - ProtectControlGroups = true; 397 - ProtectClock = true; 398 - ProtectHostname = true; 399 - RestrictSUIDSGID = true; 400 - RestrictRealtime = true; 401 - RestrictNamespaces = true; 402 - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; 403 - LockPersonality = true; 404 - MemoryDenyWriteExecute = true; 405 - RemoveIPC = true; 406 - SystemCallArchitectures = "native"; 407 - SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; 435 + CapabilityBoundingSet = ""; 436 + AmbientCapabilities = ""; 437 + NoNewPrivileges = true; 438 + ReadOnlyRootFilesystem = true; 439 + ProtectSystem = "strict"; 440 + ProtectHome = true; 441 + PrivateTmp = true; 442 + PrivateDevices = true; 443 + ProtectKernelTunables = true; 444 + ProtectKernelModules = true; 445 + ProtectKernelLogs = true; 446 + ProtectControlGroups = true; 447 + ProtectClock = true; 448 + ProtectHostname = true; 449 + RestrictSUIDSGID = true; 450 + RestrictRealtime = true; 451 + RestrictNamespaces = true; 452 + RestrictAddressFamilies = [ 453 + "AF_INET" 454 + "AF_INET6" 455 + "AF_UNIX" 456 + ]; 457 + LockPersonality = true; 458 + MemoryDenyWriteExecute = true; 459 + RemoveIPC = true; 460 + SystemCallArchitectures = "native"; 461 + SystemCallFilter = [ 462 + "@system-service" 463 + "~@privileged" 464 + "~@resources" 465 + ]; 466 + }; 467 + }; 408 468 }; 409 - }; 410 - }; 411 - }); 469 + } 470 + ); 412 471 413 - default = self.nixosModules.porxie; 472 + default = self.nixosModules.porxie; 473 + }; 414 474 }; 415 - }; 416 475 }