[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.

docs: porxie is now provided by nixpkgs

Lyna 0d08d3fc 3bb9d60d

+11 -443
+1 -1
README.md
··· 76 76 77 77 ### Run: Nix 78 78 79 - For Nix users, a [`flake.nix`](./flake.nix) is included in this repository that provides a package and a NixOS module. Please note that this is maintained on a best-effort basis for now and may be upstreamed by another contributor later. 79 + To run Porxie with Nix, you can use the [package](https://search.nixos.org/packages?channel=unstable&query=porxie) or [NixOS module](https://search.nixos.org/options?channel=unstable&query=porxie) provided directly in nixpkgs. 80 80 81 81 ## Policy Service 82 82
+4 -4
flake.lock
··· 2 2 "nodes": { 3 3 "nixpkgs": { 4 4 "locked": { 5 - "lastModified": 1775823930, 6 - "narHash": "sha256-ALT447J7FcxP/97J01A/gp/hgdO5lXRsm+zLMt+gIjc=", 5 + "lastModified": 1776255774, 6 + "narHash": "sha256-psVTpH6PK3q1htMJpmdz1hLF5pQgEshu7gQWgKO6t6Y=", 7 7 "owner": "nixos", 8 8 "repo": "nixpkgs", 9 - "rev": "8c11f88bb9573a10a7d6bf87161ef08455ac70b9", 9 + "rev": "566acc07c54dc807f91625bb286cb9b321b5f42a", 10 10 "type": "github" 11 11 }, 12 12 "original": { ··· 24 24 }, 25 25 "root": "root", 26 26 "version": 7 27 - } 27 + }
+6 -438
flake.nix
··· 1 1 { 2 - description = "Nix flake for Porxie: an atproto blob proxy"; 2 + description = "Porxie, an ATProto blob proxy for secure content delivery"; 3 3 4 4 inputs = { 5 5 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; ··· 11 11 forAllSystems = 12 12 function: 13 13 nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed ( 14 - system: (function system nixpkgs.legacyPackages.${system}) 14 + system: function system nixpkgs.legacyPackages.${system} 15 15 ); 16 16 in 17 17 { ··· 23 23 cargo 24 24 rustfmt 25 25 clippy 26 - bashInteractive 27 26 ]; 28 27 }; 29 28 } ··· 31 30 32 31 packages = forAllSystems ( 33 32 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; 42 - 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 - }; 53 - 54 - cargoLock.lockFile = ./Cargo.lock; 55 - 56 - meta = { 57 - inherit (toml) homepage description; 58 - mainProgram = "porxie"; 59 - license = lib.licenses.agpl3Only; 60 - maintainers = with lib.maintainers; [ "Blooym" ]; 61 - }; 62 - } 63 - ) { }; 64 - 65 - default = self.packages.${system}.porxie; 33 + porxie = pkgs.lib.warn "using the porxie flake directly is deprecated; use porxie from nixpkgs instead" pkgs.porxie; 34 + default = pkgs.lib.warn "using the porxie flake directly is deprecated; use porxie from nixpkgs instead" pkgs.porxie; 66 35 } 67 36 ); 68 37 69 38 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"; 83 - 84 - options.services.porxie = { 85 - enable = lib.mkEnableOption "Porxie"; 86 - 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 - }; 93 - 94 - user = mkOption { 95 - type = types.str; 96 - default = "porxie"; 97 - description = "User that Porxie runs under."; 98 - }; 99 - 100 - group = mkOption { 101 - type = types.str; 102 - default = "porxie"; 103 - description = "Group that Porxie runs under."; 104 - }; 105 - 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 - }; 114 - 115 - settings = mkOption { 116 - type = types.submodule { 117 - freeformType = types.attrsOf types.str; 118 - 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. 126 - 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 - }; 132 - 133 - authToken = mkOption { 134 - type = types.nullOr types.str; 135 - default = null; 136 - description = '' 137 - Bearer token for authenticating admin requests. 138 - 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 - }; 144 - 145 - blob = { 146 - allowedMimetypes = mkOption { 147 - type = types.listOf types.str; 148 - default = [ "image/*" ]; 149 - description = '' 150 - Blob mimetypes that can be served. 151 - 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 - }; 159 - 160 - maxSize = mkOption { 161 - type = types.str; 162 - default = "50mb"; 163 - description = '' 164 - Maximum blob size that can be fetched and served. 165 - 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 - }; 170 - 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. 176 - 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 - }; 182 - 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 - }; 190 - 191 - httpTimeout = mkOption { 192 - type = types.str; 193 - default = "30s"; 194 - description = '' 195 - Maximum duration before blob fetch requests are timed out. 196 - ''; 197 - }; 198 - 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. 204 - 205 - This value should be lower than {option}`settings.blob.httpTimeout`. 206 - ''; 207 - }; 208 - }; 209 - 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. 216 - 217 - Can typically be left as default unless using a custom or local development 218 - setup. 219 - ''; 220 - }; 221 - 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. 235 - 236 - This value should be lower than {option}`settings.identity.httpTimeout`. 237 - ''; 238 - }; 239 - }; 240 - 241 - cache = { 242 - allocation = mkOption { 243 - type = types.str; 244 - default = "512mb"; 245 - description = '' 246 - Total memory allocation for the internal cache. 247 - 248 - Blobs are cached using an LFU policy. The most frequently requested blobs 249 - are kept longest when the cache approaches its limit. 250 - 251 - For production deployments, a CDN or caching layer in front of this server 252 - is recommended for lower latency and better global availability. 253 - 254 - Setting this too high can exhaust process or system memory. The minimum 255 - value is 8mb. 256 - ''; 257 - }; 258 - 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 - }; 266 - 267 - ownershipTtl = mkOption { 268 - type = types.str; 269 - default = "1day"; 270 - description = '' 271 - How long blob ownership can be cached before expiring. 272 - ''; 273 - }; 274 - 275 - policyTtl = mkOption { 276 - type = types.str; 277 - default = "1h"; 278 - description = '' 279 - How long policy decisions can be cached before expiring. 280 - ''; 281 - }; 282 - 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 - }; 291 - 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. 298 - 299 - Requests are sent as HTTP GET <url>/<did>/<cid>. 300 - 301 - The service is expected to return HTTP 200 (OK) if permitted or HTTP 410 302 - (GONE) if restricted. 303 - ''; 304 - }; 305 - 306 - requestHeaders = mkOption { 307 - type = types.listOf types.str; 308 - default = [ ]; 309 - description = '' 310 - Headers sent alongside all requests to the policy service. 311 - 312 - Each header must be in the format "Name: value". When setting via 313 - environment variable, headers are pipe-separated (|). 314 - 315 - Should be set via {option}`environmentFiles` for sensitive values such as 316 - API keys. 317 - ''; 318 - }; 319 - 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. 326 - 327 - Warning: enabling this means restricted blobs may be served when the 328 - policy service is unreachable. 329 - ''; 330 - }; 331 - 332 - httpTimeout = mkOption { 333 - type = types.str; 334 - default = "30s"; 335 - description = '' 336 - Maximum duration before policy service requests are timed out. 337 - ''; 338 - }; 339 - 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. 345 - 346 - This value should be lower than {option}`settings.policy.httpTimeout`. 347 - ''; 348 - }; 349 - }; 350 - }; 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 - ''; 362 - }; 363 - }; 364 - 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 - }; 373 - 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" ]; 422 - 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; 431 - 432 - RuntimeDirectory = "porxie"; 433 - RuntimeDirectoryMode = "0750"; 434 - 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 - }; 468 - }; 469 - } 470 - ); 471 - 472 - default = self.nixosModules.porxie; 39 + porxie = nixpkgs.lib.warn "using the porxie flake directly is deprecated; use porxie from nixpkgs instead" nixpkgs.nixosModules.porxie; 40 + default = nixpkgs.lib.warn "using the porxie flake directly is deprecated; use porxie from nixpkgs instead" nixpkgs.nixosModules.porxie; 473 41 }; 474 42 }; 475 43 }