Your one-stop-cake-shop for everything Freshly Baked has to offer

feat(pm/umber): add copyparty

I want a private file host for things I can't put on our main copyparty
instance. I'll host that on umber

Changed files
+138
packetmix
systems
+138
packetmix/systems/umber/copyparty.nix
···
··· 1 + # SPDX-FileCopyrightText: 2025 FreshlyBakedCake 2 + # 3 + # SPDX-License-Identifier: MIT 4 + 5 + { 6 + project, 7 + pkgs, 8 + config, 9 + lib, 10 + ... 11 + }: 12 + { 13 + imports = [ 14 + project.inputs.copyparty.result.nixosModules.default 15 + ]; 16 + 17 + config = { 18 + nixpkgs.overlays = [ project.inputs.copyparty.result.overlays.default ]; 19 + 20 + services.copyparty = 21 + let 22 + admins = [ 23 + "minion" 24 + ]; 25 + in 26 + { 27 + enable = true; 28 + 29 + settings = { 30 + i = "127.0.0.1"; # ip 31 + p = 1030; # port 32 + 33 + # we'll be using nginx for this... 34 + http-only = true; 35 + no-crt = true; 36 + 37 + idp-store = 3; 38 + idp-h-usr = "X-Webauth-Login"; 39 + idp-adm = admins; 40 + have-idp-hdrs = 1; # https://github.com/9001/copyparty/issues/849 41 + 42 + shr = "/share"; 43 + shr-db = "/var/lib/copyparty/shares.db"; 44 + shr-adm = admins; 45 + 46 + # as we might have private directories, better to be a bit conservative about permissions... 47 + chmod-f = 700; 48 + chmod-d = 700; 49 + 50 + magic = true; # "enable filetype detection on nameless uploads" 51 + 52 + e2dsa = true; # index files to allow searching, upload undo, etc. 53 + e2ts = true; # and scan metadata... 54 + 55 + rss = true; # allow (experimental) rss support -> useful for antennapod/miniflux/co. 56 + dav-auth = true; # "force auth for all folders" notably "(required by davfs2 when only some folders are world-readable)" 57 + 58 + xvol = true; # don't allow symlinks to break out of confinement... 59 + no-robots = true; # not really meant to be indexed. Maybe we want to add anubis at some point too... 60 + 61 + ah-alg = "argon2"; 62 + 63 + spinner = "⭐"; # [hopefully this isn't too boring for you, tripflag](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) 64 + 65 + xm = "aw,f,j,t3600,${project.inputs.copyparty.src}/bin/hooks/wget.py"; # download URLs that are pasted into the message box 66 + 67 + xff-src = "127.0.0.1"; 68 + rproxy = 1; 69 + 70 + exp = true; 71 + }; 72 + 73 + volumes = { 74 + "/" = { 75 + path = "/var/lib/copyparty/data"; 76 + 77 + access = { 78 + r = "*"; 79 + A = admins; 80 + }; 81 + }; 82 + "/private" = { 83 + path = "/var/lib/copyparty/private"; 84 + 85 + access = { 86 + A = [ 87 + "minion" 88 + ]; 89 + }; 90 + }; 91 + }; 92 + }; 93 + 94 + systemd.services.copyparty = { 95 + path = [ pkgs.wget ]; # Needed for downloading files by URL 96 + serviceConfig = { 97 + BindReadOnlyPaths = [ 98 + "/etc/ssl" 99 + "/etc/static/ssl" 100 + ]; # Required for wget to validate SSL for downloads 101 + StateDirectory = 102 + "copyparty " 103 + + (lib.pipe config.services.copyparty.volumes [ 104 + builtins.attrValues 105 + (map (mount: mount.path)) 106 + (map (lib.removePrefix "/var/lib/")) 107 + (lib.concatStringsSep " ") 108 + ]); 109 + }; 110 + }; 111 + 112 + services.nginx.enable = true; 113 + 114 + services.nginx.virtualHosts."copyparty.starrysky.fyi" = { 115 + serverName = "copyparty.starrysky.fyi"; 116 + 117 + addSSL = true; 118 + enableACME = true; 119 + acmeRoot = null; 120 + 121 + locations."/" = { 122 + proxyPass = "http://127.0.0.1:1030"; 123 + recommendedProxySettings = true; 124 + proxyWebsockets = true; 125 + }; 126 + 127 + extraConfig = '' 128 + client_max_body_size 1024M; 129 + ''; 130 + }; 131 + services.nginx.tailscaleAuth = { 132 + enable = true; 133 + virtualHosts = [ "copyparty.starrysky.fyi" ]; 134 + }; 135 + 136 + clicks.storage.impermanence.persist.directories = [ "/var/lib/copyparty" ]; 137 + }; 138 + }