Tangled infrastructure definitions in Nix

Compare changes

Choose any two refs to compare.

+1
.gitignore
··· 1 + .env
+1
common/ssh.nix
··· 1 1 { 2 2 sshKeys = [ 3 3 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICf5rx2r0w98r5lOpxr+/hScABDnk1UfgTH8T2WzeNp4 icy@kvothe" 4 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICJPYX06+qKr9IHWfkgCtHbExoBOOwS/+iAWbog9bAdk icy@wyndle" 4 5 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMj1Dn9YuFo2BNr993ymBa6nzyyIKAURIqMbUtfI8+4X op@mantis" 5 6 ]; 6 7 }
+100 -34
flake.lock
··· 1 1 { 2 2 "nodes": { 3 + "actor-typeahead-src": { 4 + "flake": false, 5 + "locked": { 6 + "lastModified": 1762835797, 7 + "narHash": "sha256-heizoWUKDdar6ymfZTnj3ytcEv/L4d4fzSmtr0HlXsQ=", 8 + "ref": "refs/heads/main", 9 + "rev": "677fe7f743050a4e7f09d4a6f87bbf1325a06f6b", 10 + "revCount": 6, 11 + "type": "git", 12 + "url": "https://tangled.org/@jakelazaroff.com/actor-typeahead" 13 + }, 14 + "original": { 15 + "type": "git", 16 + "url": "https://tangled.org/@jakelazaroff.com/actor-typeahead" 17 + } 18 + }, 3 19 "colmena": { 4 20 "inputs": { 5 21 "flake-compat": "flake-compat", ··· 29 45 ] 30 46 }, 31 47 "locked": { 32 - "lastModified": 1751854533, 33 - "narHash": "sha256-U/OQFplExOR1jazZY4KkaQkJqOl59xlh21HP9mI79Vc=", 48 + "lastModified": 1766150702, 49 + "narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=", 34 50 "owner": "nix-community", 35 51 "repo": "disko", 36 - "rev": "16b74a1e304197248a1bc663280f2548dbfcae3c", 52 + "rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378", 37 53 "type": "github" 38 54 }, 39 55 "original": { ··· 58 74 "type": "github" 59 75 } 60 76 }, 77 + "flake-compat_2": { 78 + "flake": false, 79 + "locked": { 80 + "lastModified": 1751685974, 81 + "narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=", 82 + "rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1", 83 + "type": "tarball", 84 + "url": "https://git.lix.systems/api/v1/repos/lix-project/flake-compat/archive/549f2762aebeff29a2e5ece7a7dc0f955281a1d1.tar.gz?rev=549f2762aebeff29a2e5ece7a7dc0f955281a1d1" 85 + }, 86 + "original": { 87 + "type": "tarball", 88 + "url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz" 89 + } 90 + }, 61 91 "flake-utils": { 62 92 "locked": { 63 93 "lastModified": 1659877975, ··· 73 103 "type": "github" 74 104 } 75 105 }, 76 - "gitignore": { 106 + "flake-utils_2": { 107 + "inputs": { 108 + "systems": "systems" 109 + }, 110 + "locked": { 111 + "lastModified": 1731533236, 112 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 113 + "owner": "numtide", 114 + "repo": "flake-utils", 115 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 116 + "type": "github" 117 + }, 118 + "original": { 119 + "owner": "numtide", 120 + "repo": "flake-utils", 121 + "type": "github" 122 + } 123 + }, 124 + "gomod2nix": { 77 125 "inputs": { 126 + "flake-utils": "flake-utils_2", 78 127 "nixpkgs": [ 79 128 "tangled", 80 129 "nixpkgs" 81 130 ] 82 131 }, 83 132 "locked": { 84 - "lastModified": 1709087332, 85 - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 86 - "owner": "hercules-ci", 87 - "repo": "gitignore.nix", 88 - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 133 + "lastModified": 1763982521, 134 + "narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=", 135 + "owner": "nix-community", 136 + "repo": "gomod2nix", 137 + "rev": "02e63a239d6eabd595db56852535992c898eba72", 89 138 "type": "github" 90 139 }, 91 140 "original": { 92 - "owner": "hercules-ci", 93 - "repo": "gitignore.nix", 141 + "owner": "nix-community", 142 + "repo": "gomod2nix", 94 143 "type": "github" 95 144 } 96 145 }, ··· 134 183 "indigo": { 135 184 "flake": false, 136 185 "locked": { 137 - "lastModified": 1745333930, 138 - "narHash": "sha256-83fIHqDE+dfnZ88HaNuwfKFO+R0RKAM1WxMfNh/Matk=", 186 + "lastModified": 1753693716, 187 + "narHash": "sha256-DMIKnCJRODQXEHUxA+7mLzRALmnZhkkbHlFT2rCQYrE=", 139 188 "owner": "oppiliappan", 140 189 "repo": "indigo", 141 - "rev": "e4e59280737b8676611fc077a228d47b3e8e9491", 190 + "rev": "5f170569da9360f57add450a278d73538092d8ca", 142 191 "type": "github" 143 192 }, 144 193 "original": { ··· 163 212 "lucide-src": { 164 213 "flake": false, 165 214 "locked": { 166 - "lastModified": 1742302029, 167 - "narHash": "sha256-OyPVtpnC4/AAmPq84Wt1r1Gcs48d9KG+UBCtZK87e9k=", 215 + "lastModified": 1754044466, 216 + "narHash": "sha256-+exBR2OToB1iv7ZQI2S4B0lXA/QRvC9n6U99UxGpJGs=", 168 217 "type": "tarball", 169 - "url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip" 218 + "url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip" 170 219 }, 171 220 "original": { 172 221 "type": "tarball", 173 - "url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip" 222 + "url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip" 174 223 } 175 224 }, 176 225 "nixery-flake": { 177 226 "flake": false, 178 227 "locked": { 179 - "lastModified": 1745149613, 180 - "narHash": "sha256-rcSnsnSWA0IUjmbG2iSpvVB0702tcR3zIyU3iFJBo0g=", 228 + "lastModified": 1762501370, 229 + "narHash": "sha256-WO2NvvFB3KkFfChE5F6ghog7mvBAVKpMsQMqwadZT4k=", 181 230 "owner": "tazjin", 182 231 "repo": "nixery", 183 - "rev": "c6d4692b1b6eb105c9abce0411d2ef4b8708a6e1", 232 + "rev": "be8a4005de3f27f95e677e7b61abef387d4a840d", 184 233 "type": "github" 185 234 }, 186 235 "original": { ··· 207 256 }, 208 257 "nixpkgs_2": { 209 258 "locked": { 210 - "lastModified": 1751792365, 211 - "narHash": "sha256-J1kI6oAj25IG4EdVlg2hQz8NZTBNYvIS0l4wpr9KcUo=", 259 + "lastModified": 1767379071, 260 + "narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=", 212 261 "owner": "nixos", 213 262 "repo": "nixpkgs", 214 - "rev": "1fd8bada0b6117e6c7eb54aad5813023eed37ccb", 263 + "rev": "fb7944c166a3b630f177938e478f0378e64ce108", 215 264 "type": "github" 216 265 }, 217 266 "original": { ··· 223 272 }, 224 273 "nixpkgs_3": { 225 274 "locked": { 226 - "lastModified": 1746904237, 227 - "narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=", 275 + "lastModified": 1766070988, 276 + "narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=", 228 277 "owner": "nixos", 229 278 "repo": "nixpkgs", 230 - "rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956", 279 + "rev": "c6245e83d836d0433170a16eb185cefe0572f8b8", 231 280 "type": "github" 232 281 }, 233 282 "original": { ··· 275 324 "type": "github" 276 325 } 277 326 }, 327 + "systems": { 328 + "locked": { 329 + "lastModified": 1681028828, 330 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 331 + "owner": "nix-systems", 332 + "repo": "default", 333 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 334 + "type": "github" 335 + }, 336 + "original": { 337 + "owner": "nix-systems", 338 + "repo": "default", 339 + "type": "github" 340 + } 341 + }, 278 342 "tangled": { 279 343 "inputs": { 280 - "gitignore": "gitignore", 344 + "actor-typeahead-src": "actor-typeahead-src", 345 + "flake-compat": "flake-compat_2", 346 + "gomod2nix": "gomod2nix", 281 347 "htmx-src": "htmx-src", 282 348 "htmx-ws-src": "htmx-ws-src", 283 349 "ibm-plex-mono-src": "ibm-plex-mono-src", ··· 288 354 "sqlite-lib-src": "sqlite-lib-src" 289 355 }, 290 356 "locked": { 291 - "lastModified": 1751967742, 292 - "narHash": "sha256-nkZ1Ny6BaW/KD4BWtWm2kJ5+92kNSWcj4iyqyIBq7yw=", 357 + "lastModified": 1767683698, 358 + "narHash": "sha256-MFrfNmTKTdOOsyXUvvqPwH6zqvDZZpURnd7QdJkVOgU=", 293 359 "ref": "refs/heads/master", 294 - "rev": "643a3ac18326821472691488635cc66f4ffdbf2b", 295 - "revCount": 884, 360 + "rev": "b31a2a3590fefc4c70817f94a20076df2428b4d3", 361 + "revCount": 1791, 296 362 "type": "git", 297 - "url": "https://tangled.sh/@tangled.sh/core" 363 + "url": "https://tangled.org/@tangled.org/core" 298 364 }, 299 365 "original": { 300 366 "type": "git", 301 - "url": "https://tangled.sh/@tangled.sh/core" 367 + "url": "https://tangled.org/@tangled.org/core" 302 368 } 303 369 } 304 370 },
+104 -33
flake.nix
··· 1 1 { 2 2 description = "nix infra for tangled"; 3 + 3 4 inputs = { 4 5 nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; 5 - tangled.url = "git+https://tangled.sh/@tangled.sh/core"; 6 + tangled.url = "git+https://tangled.org/@tangled.org/core"; 6 7 colmena.url = "github:zhaofengli/colmena/release-0.4.x"; 7 8 disko = { 8 9 url = "github:nix-community/disko"; ··· 16 17 }; 17 18 }; 18 19 19 - outputs = 20 - { nixpkgs, disko, colmena, nixery-flake, tangled, ... }: 20 + outputs = { nixpkgs, disko, colmena, nixery-flake, tangled, ... }: 21 + let 22 + system = "x86_64-linux"; 23 + commonArgs = import ./common/ssh.nix; 24 + 25 + # Helper function to create nixosConfiguration 26 + mkHost = hostname: extraModules: 27 + nixpkgs.lib.nixosSystem { 28 + inherit system; 29 + specialArgs = { inherit commonArgs; }; 30 + modules = [ 31 + disko.nixosModules.disko 32 + ./hosts/${hostname}/configuration.nix 33 + ] ++ extraModules; 34 + }; 35 + 36 + # Helper function to create colmena host 37 + mkColmenaHost = hostname: targetHost: targetPort: extraModules: 38 + { 39 + deployment = { 40 + inherit targetHost; 41 + inherit targetPort; 42 + targetUser = "tangler"; 43 + buildOnTarget = true; 44 + }; 45 + nixpkgs.system = system; 46 + time.timeZone = "Europe/Helsinki"; 47 + imports = [ 48 + disko.nixosModules.disko 49 + ./hosts/${hostname}/configuration.nix 50 + ] ++ extraModules; 51 + }; 52 + 53 + # Host configurations 54 + hosts = { 55 + appview = { 56 + modules = [ 57 + tangled.nixosModules.appview 58 + ./hosts/appview/services/appview.nix 59 + ./hosts/appview/services/nginx.nix 60 + ]; 61 + target = "95.111.205.38"; 62 + }; 63 + 64 + pds = { 65 + modules = [ 66 + ./hosts/pds/services/nginx.nix 67 + ./hosts/pds/services/pds.nix 68 + ]; 69 + target = "tngl.sh"; 70 + }; 71 + 72 + nixery = { 73 + modules = [ 74 + tangled.nixosModules.spindle 75 + ./hosts/nixery/services/nginx.nix 76 + ./hosts/nixery/services/openbao/openbao.nix 77 + ./hosts/nixery/services/openbao/proxy.nix 78 + ./hosts/nixery/services/nixery.nix 79 + ]; 80 + target = "nixery.tangled.sh"; 81 + }; 82 + 83 + spindle = { 84 + modules = [ 85 + tangled.nixosModules.spindle 86 + ./hosts/spindle/services/openbao/openbao.nix 87 + ./hosts/spindle/services/openbao/proxy.nix 88 + ./hosts/spindle/services/spindle.nix 89 + ./hosts/spindle/services/nginx.nix 90 + ]; 91 + target = "spindle.alpha.tangled.sh"; 92 + }; 93 + 94 + knot1 = { 95 + modules = [ 96 + tangled.nixosModules.knot 97 + ./hosts/knot1/services/knot.nix 98 + ./hosts/knot1/services/nginx.nix 99 + ]; 100 + target = "knot1.alpha.tangled.sh"; 101 + }; 102 + }; 103 + in 21 104 { 22 - nixosConfigurations.nixery = nixpkgs.lib.nixosSystem { 23 - system = "x86_64-linux"; 24 - modules = [ 25 - disko.nixosModules.disko 26 - tangled.nixosModules.spindle 27 - ./hosts/nixery/configuration.nix 28 - ]; 105 + # nixos-anywhere and nixos-rebuild use these 106 + nixosConfigurations = { 107 + appview = mkHost "appview" hosts.appview.modules; 108 + pds = mkHost "pds" hosts.pds.modules; 109 + nixery = mkHost "nixery" hosts.nixery.modules; 110 + spindle = mkHost "spindle" hosts.spindle.modules; 111 + knot1 = mkHost "knot1" hosts.knot1.modules; 29 112 }; 113 + 114 + # colmena uses this 30 115 colmenaHive = colmena.lib.makeHive { 31 116 meta = { 32 - nixpkgs = nixpkgs.legacyPackages.x86_64-linux; 117 + nixpkgs = nixpkgs.legacyPackages.${system}; 33 118 specialArgs = { 119 + inherit commonArgs; 34 120 nixery-pkgs = import nixery-flake.outPath { 35 - pkgs = import nixpkgs { system = "x86_64-linux"; }; 121 + pkgs = import nixpkgs { inherit system; }; 36 122 }; 37 - commonArgs = import ./common/ssh.nix; 38 123 }; 39 124 }; 40 125 41 126 defaults = { pkgs, ... }: { 42 - environment.systemPackages = [ 43 - pkgs.curl 44 - ]; 127 + environment.systemPackages = [ pkgs.curl ]; 45 128 }; 46 - nixery = { pkgs, ... }: { 47 - deployment = { 48 - targetHost = "nixery.tangled.sh"; 49 - targetPort = 22; 50 - targetUser = "tangler"; 51 - buildOnTarget = true; 52 - }; 53 - nixpkgs.system = "x86_64-linux"; 54 129 55 - imports = [ 56 - disko.nixosModules.disko 57 - tangled.nixosModules.spindle 58 - ./hosts/nixery/configuration.nix 59 - ./hosts/nixery/services/nginx.nix 60 - ./hosts/nixery/services/nixery.nix 61 - ]; 62 - time.timeZone = "Europe/Helsinki"; 63 - }; 130 + appview = mkColmenaHost "appview" hosts.appview.target 2222 hosts.appview.modules; 131 + pds = mkColmenaHost "pds" hosts.pds.target 22 hosts.pds.modules; 132 + nixery = mkColmenaHost "nixery" hosts.nixery.target 22 hosts.nixery.modules; 133 + spindle = mkColmenaHost "spindle" hosts.spindle.target 22 hosts.spindle.modules; 134 + knot1 = mkColmenaHost "knot1" hosts.knot1.target 22 hosts.knot1.modules; 64 135 }; 65 136 }; 66 137 }
+62
hosts/appview/configuration.nix
··· 1 + { modulesPath 2 + , lib 3 + , pkgs 4 + , ... 5 + } @ args: 6 + { 7 + imports = [ 8 + (modulesPath + "/installer/scan/not-detected.nix") 9 + (modulesPath + "/profiles/qemu-guest.nix") 10 + ./disk-config.nix 11 + ]; 12 + boot.loader.grub = { 13 + # no need to set devices, disko will add all devices that have a EF02 partition to the list already 14 + # devices = [ ]; 15 + efiSupport = true; 16 + efiInstallAsRemovable = true; 17 + }; 18 + 19 + networking.hostName = "appview-arn"; 20 + services = { 21 + openssh.enable = true; 22 + openssh.ports = [2222]; 23 + }; 24 + 25 + # networking.extraHosts = '' 26 + # 85.9.211.103 knot1.tangled.sh 27 + # ''; 28 + 29 + 30 + nix = { 31 + extraOptions = '' 32 + experimental-features = nix-command flakes ca-derivations 33 + warn-dirty = false 34 + keep-outputs = false 35 + ''; 36 + }; 37 + 38 + environment.systemPackages = map lib.lowPrio [ 39 + pkgs.curl 40 + pkgs.gitMinimal 41 + ]; 42 + 43 + users.users.tangler = { 44 + extraGroups = [ "networkmanager" "wheel" ]; 45 + openssh.authorizedKeys.keys = args.commonArgs.sshKeys; 46 + isNormalUser = true; 47 + }; 48 + 49 + security.sudo.extraRules = [ 50 + { 51 + users = [ "tangler" ]; 52 + commands = [ 53 + { 54 + command = "ALL"; 55 + options = [ "NOPASSWD" ]; 56 + } 57 + ]; 58 + } 59 + ]; 60 + 61 + system.stateVersion = "25.05"; 62 + }
+56
hosts/appview/disk-config.nix
··· 1 + # Example to create a bios compatible gpt partition 2 + { lib, ... }: 3 + { 4 + disko.devices = { 5 + disk.disk1 = { 6 + device = lib.mkDefault "/dev/vda"; 7 + type = "disk"; 8 + content = { 9 + type = "gpt"; 10 + partitions = { 11 + boot = { 12 + name = "boot"; 13 + size = "1M"; 14 + type = "EF02"; 15 + }; 16 + esp = { 17 + name = "ESP"; 18 + size = "500M"; 19 + type = "EF00"; 20 + content = { 21 + type = "filesystem"; 22 + format = "vfat"; 23 + mountpoint = "/boot"; 24 + }; 25 + }; 26 + root = { 27 + name = "root"; 28 + size = "100%"; 29 + content = { 30 + type = "lvm_pv"; 31 + vg = "pool"; 32 + }; 33 + }; 34 + }; 35 + }; 36 + }; 37 + lvm_vg = { 38 + pool = { 39 + type = "lvm_vg"; 40 + lvs = { 41 + root = { 42 + size = "100%FREE"; 43 + content = { 44 + type = "filesystem"; 45 + format = "ext4"; 46 + mountpoint = "/"; 47 + mountOptions = [ 48 + "defaults" 49 + ]; 50 + }; 51 + }; 52 + }; 53 + }; 54 + }; 55 + }; 56 + }
+11
hosts/appview/services/appview.nix
··· 1 + { modulesPath 2 + , lib 3 + , pkgs 4 + , ... 5 + } @ args: 6 + { 7 + services.tangled.appview = { 8 + enable = true; 9 + environmentFile = "/etc/secrets/appview.env"; 10 + }; 11 + }
+6
hosts/appview/services/litestream.nix
··· 1 + { 2 + services.litestream = { 3 + enable = true; 4 + environmentFile = "/etc/secrets/litestream.env" 5 + }; 6 + }
+53
hosts/appview/services/nginx-alpha.nix
··· 1 + { config, pkgs, ... }: 2 + { 3 + services.nginx = { 4 + enable = true; 5 + recommendedTlsSettings = true; 6 + recommendedOptimisation = true; 7 + recommendedGzipSettings = true; 8 + 9 + # Fix proxy headers hash warnings 10 + appendHttpConfig = '' 11 + proxy_headers_hash_max_size 1024; 12 + proxy_headers_hash_bucket_size 128; 13 + ''; 14 + 15 + virtualHosts = { 16 + # AppView service on alpha.tangled.sh 17 + "alpha.tangled.sh" = { 18 + forceSSL = true; 19 + enableACME = true; 20 + 21 + locations."/" = { 22 + proxyPass = "http://127.0.0.1:3000"; 23 + extraConfig = '' 24 + proxy_http_version 1.1; 25 + proxy_set_header Host $host; 26 + proxy_set_header X-Real-IP $remote_addr; 27 + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 28 + proxy_set_header X-Forwarded-Proto $scheme; 29 + client_max_body_size 100M; 30 + ''; 31 + }; 32 + 33 + # WebSocket support for /logs endpoint 34 + locations."~ /logs$" = { 35 + proxyPass = "http://127.0.0.1:3000"; 36 + proxyWebsockets = true; 37 + extraConfig = '' 38 + proxy_read_timeout 86400; 39 + ''; 40 + }; 41 + }; 42 + }; 43 + }; 44 + 45 + # Open firewall ports 46 + networking.firewall.allowedTCPPorts = [ 80 443 ]; 47 + 48 + # ACME configuration for Let's Encrypt 49 + security.acme = { 50 + acceptTerms = true; 51 + defaults.email = "team@tangled.org"; 52 + }; 53 + }
+106
hosts/appview/services/nginx.nix
··· 1 + { config, pkgs, ... }: 2 + { 3 + services.nginx = { 4 + enable = true; 5 + recommendedProxySettings = true; 6 + recommendedTlsSettings = true; 7 + recommendedOptimisation = true; 8 + recommendedGzipSettings = true; 9 + 10 + streamConfig = '' 11 + upstream knot-sailor { 12 + server 94.237.110.185:22; 13 + } 14 + 15 + server { 16 + listen 22; 17 + listen [::]:22; 18 + proxy_pass knot-sailor; 19 + } 20 + ''; 21 + 22 + virtualHosts = { 23 + # Redirect tangled.sh โ†’ tangled.org 24 + "tangled.sh" = { 25 + serverAliases = [ "www.tangled.sh" ]; 26 + locations."/" = { 27 + return = "301 https://tangled.org$request_uri"; 28 + }; 29 + forceSSL = true; 30 + enableACME = true; 31 + }; 32 + 33 + # Redirect strings.tangled.sh โ†’ tangled.org/strings/* 34 + "strings.tangled.sh" = { 35 + locations."/" = { 36 + return = "301 https://tangled.org/strings$request_uri"; 37 + }; 38 + forceSSL = true; 39 + enableACME = true; 40 + }; 41 + 42 + # Redirect strings.tangled.org โ†’ tangled.org/strings/* 43 + "strings.tangled.org" = { 44 + locations."/" = { 45 + return = "301 https://tangled.org/strings$request_uri"; 46 + }; 47 + forceSSL = true; 48 + enableACME = true; 49 + }; 50 + 51 + # Main app on tangled.org 52 + "tangled.org" = { 53 + serverAliases = [ "www.tangled.org" ]; 54 + 55 + forceSSL = true; 56 + enableACME = true; 57 + 58 + extraConfig = '' 59 + # Redirect www โ†’ bare domain 60 + if ($host = www.tangled.org) { 61 + return 301 https://tangled.org$request_uri; 62 + } 63 + 64 + client_max_body_size 100M; 65 + ''; 66 + 67 + locations."~ ^/@tangled\\.sh(/.*)?$" = { 68 + extraConfig = '' 69 + rewrite ^/@tangled\.sh(.*)$ https://tangled.org/@tangled.org$1 permanent; 70 + ''; 71 + }; 72 + 73 + locations."~ ^/tangled\\.sh(/.*)?$" = { 74 + extraConfig = '' 75 + rewrite ^/tangled\.sh(.*)$ https://tangled.org/tangled.org$1 permanent; 76 + ''; 77 + }; 78 + 79 + 80 + locations."~ /logs$" = { 81 + proxyPass = "http://127.0.0.1:3000"; 82 + proxyWebsockets = true; 83 + extraConfig = '' 84 + proxy_read_timeout 86400; 85 + ''; 86 + }; 87 + 88 + locations."/" = { 89 + proxyPass = "http://127.0.0.1:3000"; 90 + extraConfig = '' 91 + client_max_body_size 100M; 92 + ''; 93 + }; 94 + }; 95 + }; 96 + }; 97 + 98 + # Open firewall ports 99 + networking.firewall.allowedTCPPorts = [ 80 443 2222 22 ]; 100 + 101 + # ACME configuration for Let's Encrypt 102 + security.acme = { 103 + acceptTerms = true; 104 + defaults.email = "team@tangled.org"; 105 + }; 106 + }
+57
hosts/knot1/configuration.nix
··· 1 + { modulesPath 2 + , lib 3 + , pkgs 4 + , ... 5 + } @ args: 6 + { 7 + imports = [ 8 + (modulesPath + "/installer/scan/not-detected.nix") 9 + (modulesPath + "/profiles/qemu-guest.nix") 10 + ./disk-config.nix 11 + ]; 12 + boot.loader.grub = { 13 + # no need to set devices, disko will add all devices that have a EF02 partition to the list already 14 + # devices = [ ]; 15 + efiSupport = true; 16 + efiInstallAsRemovable = true; 17 + }; 18 + 19 + networking.hostName = "knot1-ams"; 20 + services = { 21 + openssh.enable = true; 22 + }; 23 + 24 + 25 + nix = { 26 + extraOptions = '' 27 + experimental-features = nix-command flakes ca-derivations 28 + warn-dirty = false 29 + keep-outputs = false 30 + ''; 31 + }; 32 + 33 + environment.systemPackages = map lib.lowPrio [ 34 + pkgs.curl 35 + pkgs.gitMinimal 36 + ]; 37 + 38 + users.users.tangler = { 39 + extraGroups = [ "networkmanager" "wheel" "docker" ]; 40 + openssh.authorizedKeys.keys = args.commonArgs.sshKeys; 41 + isNormalUser = true; 42 + }; 43 + 44 + security.sudo.extraRules = [ 45 + { 46 + users = [ "tangler" ]; 47 + commands = [ 48 + { 49 + command = "ALL"; 50 + options = [ "NOPASSWD" ]; 51 + } 52 + ]; 53 + } 54 + ]; 55 + 56 + system.stateVersion = "25.05"; 57 + }
+56
hosts/knot1/disk-config.nix
··· 1 + # Example to create a bios compatible gpt partition 2 + { lib, ... }: 3 + { 4 + disko.devices = { 5 + disk.disk1 = { 6 + device = lib.mkDefault "/dev/vda"; 7 + type = "disk"; 8 + content = { 9 + type = "gpt"; 10 + partitions = { 11 + boot = { 12 + name = "boot"; 13 + size = "1M"; 14 + type = "EF02"; 15 + }; 16 + esp = { 17 + name = "ESP"; 18 + size = "500M"; 19 + type = "EF00"; 20 + content = { 21 + type = "filesystem"; 22 + format = "vfat"; 23 + mountpoint = "/boot"; 24 + }; 25 + }; 26 + root = { 27 + name = "root"; 28 + size = "100%"; 29 + content = { 30 + type = "lvm_pv"; 31 + vg = "pool"; 32 + }; 33 + }; 34 + }; 35 + }; 36 + }; 37 + lvm_vg = { 38 + pool = { 39 + type = "lvm_vg"; 40 + lvs = { 41 + root = { 42 + size = "100%FREE"; 43 + content = { 44 + type = "filesystem"; 45 + format = "ext4"; 46 + mountpoint = "/"; 47 + mountOptions = [ 48 + "defaults" 49 + ]; 50 + }; 51 + }; 52 + }; 53 + }; 54 + }; 55 + }; 56 + }
+11
hosts/knot1/services/knot.nix
··· 1 + { 2 + services.tangled.knot = { 3 + enable = true; 4 + stateDir = "/home/git"; 5 + server = { 6 + listenAddr = "127.0.0.1:5555"; 7 + owner = "did:plc:hwevmowznbiukdf6uk5dwrrq"; 8 + hostname = "knot1.alpha.tangled.sh"; 9 + }; 10 + }; 11 + }
+35
hosts/knot1/services/nginx.nix
··· 1 + { 2 + services.nginx = { 3 + enable = true; 4 + virtualHosts = { 5 + "knot1.alpha.tangled.sh" = { 6 + forceSSL = true; 7 + enableACME = true; 8 + locations."/" = { 9 + proxyPass = "http://127.0.0.1:5555"; 10 + 11 + extraConfig = '' 12 + proxy_set_header X-Forwarded-For $remote_addr; 13 + proxy_set_header Host $host; 14 + proxy_set_header X-Real-IP $remote_addr; 15 + proxy_set_header X-Forwarded-Proto $scheme; 16 + ''; 17 + }; 18 + locations."/events" = { 19 + proxyPass = "http://127.0.0.1:5555"; 20 + extraConfig = '' 21 + proxy_set_header X-Forwarded-For $remote_addr; 22 + proxy_set_header Host $host; 23 + proxy_set_header Upgrade $http_upgrade; 24 + proxy_set_header Connection "upgrade"; 25 + ''; 26 + }; 27 + }; 28 + }; 29 + }; 30 + security.acme = { 31 + acceptTerms = true; 32 + defaults.email = "team@tangled.org"; 33 + }; 34 + networking.firewall.allowedTCPPorts = [ 80 443 ]; 35 + }
+9 -1
hosts/nixery/configuration.nix
··· 19 19 networking.hostName = "nixery"; 20 20 services = { 21 21 openssh.enable = true; 22 - tangled-spindle = { 22 + tangled.spindle = { 23 23 enable = true; 24 24 server = { 25 25 owner = "did:plc:wshs7t2adsemcrrd4snkeqli"; # @tangled.sh 26 26 hostname = "spindle.tangled.sh"; 27 27 listenAddr = "127.0.0.1:6555"; 28 + queueSize = 100; 29 + maxJobCount = 2; 30 + secrets = { 31 + provider = "openbao"; 32 + }; 33 + }; 34 + pipelines = { 35 + workflowTimeout = "15m"; 28 36 }; 29 37 }; 30 38 };
+94
hosts/nixery/services/docker-cleanup.nix
··· 1 + { config, pkgs, ... }: 2 + 3 + { 4 + systemd.services.docker-cleanup = { 5 + description = "Docker cleanup service - removes unused containers, networks, images, and volumes"; 6 + 7 + serviceConfig = { 8 + Type = "oneshot"; 9 + User = "root"; 10 + ExecStart = pkgs.writeShellScript "docker-cleanup" '' 11 + set -e 12 + 13 + echo "Starting Docker cleanup at $(date)" 14 + 15 + # remove containers running for 15+ minutes 16 + echo "Removing containers running for 15+ minutes..." 17 + CONTAINERS_TO_REMOVE=$(${pkgs.docker}/bin/docker ps --format "table {{.ID}}\t{{.RunningFor}}" --no-trunc | awk ' 18 + /minute/ { 19 + if ($2 >= 15) print $1 20 + } 21 + /hour/ { 22 + print $1 23 + } 24 + /day/ { 25 + print $1 26 + } 27 + /week/ { 28 + print $1 29 + } 30 + /month/ { 31 + print $1 32 + } 33 + /year/ { 34 + print $1 35 + } 36 + ') 37 + 38 + if [ -n "$CONTAINERS_TO_REMOVE" ]; then 39 + echo "Found containers to remove: $CONTAINERS_TO_REMOVE" 40 + echo "$CONTAINERS_TO_REMOVE" | xargs -r ${pkgs.docker}/bin/docker rm -f 41 + else 42 + echo "No containers running for 15+ minutes found" 43 + fi 44 + 45 + # remove stopped containers 46 + echo "Removing stopped containers..." 47 + ${pkgs.docker}/bin/docker container prune -f || true 48 + 49 + # remove unused networks (excluding default networks) 50 + echo "Removing unused networks..." 51 + ${pkgs.docker}/bin/docker network prune -f || true 52 + 53 + # remove unused images (dangling images only by default) 54 + echo "Removing dangling images..." 55 + ${pkgs.docker}/bin/docker image prune -f || true 56 + 57 + # remove unused volumes 58 + echo "Removing unused volumes..." 59 + ${pkgs.docker}/bin/docker volume prune -f || true 60 + 61 + echo "Docker cleanup completed at $(date)" 62 + ''; 63 + 64 + PrivateNetwork = false; # Needs network access for Docker 65 + ProtectSystem = "strict"; 66 + ProtectHome = true; 67 + NoNewPrivileges = true; 68 + 69 + # Logging 70 + StandardOutput = "journal"; 71 + StandardError = "journal"; 72 + }; 73 + 74 + # ensure docker is running before cleanup 75 + after = [ "docker.service" ]; 76 + requires = [ "docker.service" ]; 77 + 78 + # don't restart on failure 79 + restartIfChanged = false; 80 + }; 81 + 82 + # timer to run the cleanup service every n minutes 83 + systemd.timers.docker-cleanup = { 84 + description = "Timer for Docker cleanup service"; 85 + timerConfig = { 86 + OnCalendar = "*:0/15"; # run every 15 minutes 87 + Persistent = true; # persist timer across reboots 88 + WakeSystem = false; # run immediately if the system was powered off when timer should have run 89 + }; 90 + wantedBy = [ "timers.target" ]; 91 + }; 92 + 93 + virtualisation.docker.enable = true; 94 + }
+10 -1
hosts/nixery/services/nginx.nix
··· 27 27 proxy_set_header X-Forwarded-For $remote_addr; 28 28 proxy_set_header Host $host; 29 29 proxy_set_header Upgrade $http_upgrade; 30 - proxy_set_header Connection Upgrade; 30 + proxy_set_header Connection "upgrade"; 31 + ''; 32 + }; 33 + locations."/logs/" = { 34 + proxyPass = "http://localhost:6555"; 35 + extraConfig = '' 36 + proxy_set_header X-Forwarded-For $remote_addr; 37 + proxy_set_header Host $host; 38 + proxy_set_header Upgrade $http_upgrade; 39 + proxy_set_header Connection "upgrade"; 31 40 ''; 32 41 }; 33 42 };
+39
hosts/nixery/services/openbao/openbao.nix
··· 1 + { config, pkgs, lib, ... }: 2 + { 3 + # Create openbao user and group 4 + users.groups.openbao = {}; 5 + 6 + users.users.openbao = { 7 + isSystemUser = true; 8 + group = "openbao"; 9 + home = "/var/lib/openbao"; 10 + createHome = true; 11 + description = "OpenBao service user"; 12 + }; 13 + 14 + systemd.services.openbao = { 15 + serviceConfig = { 16 + DynamicUser = lib.mkForce false; 17 + User = "openbao"; 18 + Group = "openbao"; 19 + }; 20 + }; 21 + 22 + services.openbao = { 23 + enable = true; 24 + settings = { 25 + ui = true; 26 + 27 + listener.default = { 28 + type = "tcp"; 29 + address = "127.0.0.1:8201"; 30 + tls_disable = true; 31 + }; 32 + 33 + cluster_addr = "http://127.0.0.1:8202"; 34 + api_addr = "http://127.0.0.1:8201"; 35 + 36 + storage.raft.path = "/var/lib/openbao"; 37 + }; 38 + }; 39 + }
+100
hosts/nixery/services/openbao/proxy.nix
··· 1 + { pkgs, ... }: 2 + 3 + { 4 + systemd.services.openbao-proxy = { 5 + description = "OpenBao Proxy with Auto-Auth"; 6 + after = [ "network.target" ]; 7 + wantedBy = [ "multi-user.target" ]; 8 + serviceConfig = { 9 + User = "root"; 10 + ExecStart = "${pkgs.openbao}/bin/bao proxy -config=/etc/openbao/proxy.hcl"; 11 + Restart = "always"; 12 + RestartSec = "5"; 13 + LimitNOFILE = "65536"; 14 + }; 15 + }; 16 + 17 + 18 + 19 + environment.etc."openbao/proxy.hcl".text = '' 20 + vault { 21 + address = "http://localhost:8201" 22 + 23 + # Retry configuration 24 + retry { 25 + num_retries = 5 26 + } 27 + } 28 + 29 + # Auto-Auth using AppRole 30 + auto_auth { 31 + method "approle" { 32 + mount_path = "auth/approle" 33 + config = { 34 + role_id_file_path = "/etc/openbao/role-id" 35 + secret_id_file_path = "/etc/openbao/secret-id" 36 + remove_secret_id_file_after_reading = false 37 + } 38 + } 39 + 40 + # Write authenticated token to file 41 + sink "file" { 42 + config = { 43 + path = "/var/lib/openbao/token" 44 + mode = 0640 45 + } 46 + } 47 + } 48 + 49 + # API Proxy listener for Spindle 50 + listener "tcp" { 51 + address = "127.0.0.1:8200" 52 + tls_disable = true 53 + 54 + # Security headers 55 + require_request_header = false 56 + 57 + # Enable proxy API for management 58 + proxy_api { 59 + enable_quit = true 60 + } 61 + } 62 + 63 + # Enable API proxy with auto-auth token 64 + api_proxy { 65 + use_auto_auth_token = true 66 + } 67 + 68 + cache { 69 + } 70 + 71 + # Logging configuration 72 + log_level = "info" 73 + log_format = "standard" 74 + log_file = "/var/log/openbao/proxy.log" 75 + log_rotate_duration = "24h" 76 + log_rotate_max_files = 30 77 + 78 + # Process management 79 + pid_file = "/var/lib/openbao/proxy.pid" 80 + 81 + # Disable idle connections for reliability 82 + disable_idle_connections = ["auto-auth", "proxying"] 83 + ''; 84 + 85 + # Create necessary directories and files 86 + systemd.tmpfiles.rules = [ 87 + # Directories 88 + "d /var/lib/openbao 0755 root root -" 89 + "d /var/lib/openbao/cache 0755 root root -" 90 + "d /var/log/openbao 0755 root root -" 91 + "d /etc/openbao 0755 root root -" 92 + 93 + # Credential files (content must be populated externally) 94 + "f /etc/openbao/role-id 0600 root root -" 95 + "f /etc/openbao/secret-id 0600 root root -" 96 + 97 + # Configuration file 98 + "f /etc/openbao/proxy.hcl 0644 root root -" 99 + ]; 100 + }
+57
hosts/pds/configuration.nix
··· 1 + { modulesPath 2 + , lib 3 + , pkgs 4 + , ... 5 + } @ args: 6 + { 7 + imports = [ 8 + (modulesPath + "/installer/scan/not-detected.nix") 9 + (modulesPath + "/profiles/qemu-guest.nix") 10 + ./disk-config.nix 11 + ]; 12 + boot.loader.grub = { 13 + # no need to set devices, disko will add all devices that have a EF02 partition to the list already 14 + # devices = [ ]; 15 + efiSupport = true; 16 + efiInstallAsRemovable = true; 17 + }; 18 + 19 + networking.hostName = "pds"; 20 + services = { 21 + openssh.enable = true; 22 + }; 23 + 24 + 25 + nix = { 26 + extraOptions = '' 27 + experimental-features = nix-command flakes ca-derivations 28 + warn-dirty = false 29 + keep-outputs = false 30 + ''; 31 + }; 32 + 33 + environment.systemPackages = map lib.lowPrio [ 34 + pkgs.curl 35 + pkgs.gitMinimal 36 + ]; 37 + 38 + users.users.tangler = { 39 + extraGroups = [ "networkmanager" "wheel" ]; 40 + openssh.authorizedKeys.keys = args.commonArgs.sshKeys; 41 + isNormalUser = true; 42 + }; 43 + 44 + security.sudo.extraRules = [ 45 + { 46 + users = [ "tangler" ]; 47 + commands = [ 48 + { 49 + command = "ALL"; 50 + options = [ "NOPASSWD" ]; 51 + } 52 + ]; 53 + } 54 + ]; 55 + 56 + system.stateVersion = "25.05"; 57 + }
+56
hosts/pds/disk-config.nix
··· 1 + # Example to create a bios compatible gpt partition 2 + { lib, ... }: 3 + { 4 + disko.devices = { 5 + disk.disk1 = { 6 + device = lib.mkDefault "/dev/vda"; 7 + type = "disk"; 8 + content = { 9 + type = "gpt"; 10 + partitions = { 11 + boot = { 12 + name = "boot"; 13 + size = "1M"; 14 + type = "EF02"; 15 + }; 16 + esp = { 17 + name = "ESP"; 18 + size = "500M"; 19 + type = "EF00"; 20 + content = { 21 + type = "filesystem"; 22 + format = "vfat"; 23 + mountpoint = "/boot"; 24 + }; 25 + }; 26 + root = { 27 + name = "root"; 28 + size = "100%"; 29 + content = { 30 + type = "lvm_pv"; 31 + vg = "pool"; 32 + }; 33 + }; 34 + }; 35 + }; 36 + }; 37 + lvm_vg = { 38 + pool = { 39 + type = "lvm_vg"; 40 + lvs = { 41 + root = { 42 + size = "100%FREE"; 43 + content = { 44 + type = "filesystem"; 45 + format = "ext4"; 46 + mountpoint = "/"; 47 + mountOptions = [ 48 + "defaults" 49 + ]; 50 + }; 51 + }; 52 + }; 53 + }; 54 + }; 55 + }; 56 + }
+35
hosts/pds/services/nginx.nix
··· 1 + { 2 + services.nginx = { 3 + enable = true; 4 + virtualHosts."tngl.sh" = { 5 + forceSSL = true; 6 + enableACME = true; 7 + 8 + # match exact root 9 + locations."= /" = { 10 + extraConfig = '' 11 + return 301 https://tangled.sh; 12 + ''; 13 + }; 14 + 15 + # match all other paths 16 + locations."/" = { 17 + proxyPass = "http://localhost:3000"; 18 + extraConfig = '' 19 + proxy_set_header Host $host; 20 + proxy_set_header X-Real-IP $remote_addr; 21 + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 22 + proxy_set_header X-Forwarded-Proto $scheme; 23 + proxy_set_header Upgrade $http_upgrade; 24 + proxy_set_header Connection "upgrade"; 25 + ''; 26 + }; 27 + }; 28 + }; 29 + 30 + security.acme = { 31 + acceptTerms = true; 32 + defaults.email = "anirudh@tangled.sh"; 33 + }; 34 + networking.firewall.allowedTCPPorts = [ 80 443 ]; 35 + }
+13
hosts/pds/services/pds.nix
··· 1 + { 2 + services.pds = { 3 + enable = true; 4 + settings = { 5 + PDS_HOSTNAME = "tngl.sh"; 6 + PDS_PORT = 3000; 7 + PDS_HOST = "127.0.0.1"; 8 + }; 9 + environmentFiles = [ 10 + "/var/secrets/pds.env" 11 + ]; 12 + }; 13 + }
+57
hosts/spindle/configuration.nix
··· 1 + { modulesPath 2 + , lib 3 + , pkgs 4 + , ... 5 + } @ args: 6 + { 7 + imports = [ 8 + (modulesPath + "/installer/scan/not-detected.nix") 9 + (modulesPath + "/profiles/qemu-guest.nix") 10 + ./disk-config.nix 11 + ]; 12 + boot.loader.grub = { 13 + # no need to set devices, disko will add all devices that have a EF02 partition to the list already 14 + # devices = [ ]; 15 + efiSupport = true; 16 + efiInstallAsRemovable = true; 17 + }; 18 + 19 + networking.hostName = "spindle-waw"; 20 + services = { 21 + openssh.enable = true; 22 + }; 23 + 24 + 25 + nix = { 26 + extraOptions = '' 27 + experimental-features = nix-command flakes ca-derivations 28 + warn-dirty = false 29 + keep-outputs = false 30 + ''; 31 + }; 32 + 33 + environment.systemPackages = map lib.lowPrio [ 34 + pkgs.curl 35 + pkgs.gitMinimal 36 + ]; 37 + 38 + users.users.tangler = { 39 + extraGroups = [ "networkmanager" "wheel" "docker" ]; 40 + openssh.authorizedKeys.keys = args.commonArgs.sshKeys; 41 + isNormalUser = true; 42 + }; 43 + 44 + security.sudo.extraRules = [ 45 + { 46 + users = [ "tangler" ]; 47 + commands = [ 48 + { 49 + command = "ALL"; 50 + options = [ "NOPASSWD" ]; 51 + } 52 + ]; 53 + } 54 + ]; 55 + 56 + system.stateVersion = "25.05"; 57 + }
+56
hosts/spindle/disk-config.nix
··· 1 + # Example to create a bios compatible gpt partition 2 + { lib, ... }: 3 + { 4 + disko.devices = { 5 + disk.disk1 = { 6 + device = lib.mkDefault "/dev/vda"; 7 + type = "disk"; 8 + content = { 9 + type = "gpt"; 10 + partitions = { 11 + boot = { 12 + name = "boot"; 13 + size = "1M"; 14 + type = "EF02"; 15 + }; 16 + esp = { 17 + name = "ESP"; 18 + size = "500M"; 19 + type = "EF00"; 20 + content = { 21 + type = "filesystem"; 22 + format = "vfat"; 23 + mountpoint = "/boot"; 24 + }; 25 + }; 26 + root = { 27 + name = "root"; 28 + size = "100%"; 29 + content = { 30 + type = "lvm_pv"; 31 + vg = "pool"; 32 + }; 33 + }; 34 + }; 35 + }; 36 + }; 37 + lvm_vg = { 38 + pool = { 39 + type = "lvm_vg"; 40 + lvs = { 41 + root = { 42 + size = "100%FREE"; 43 + content = { 44 + type = "filesystem"; 45 + format = "ext4"; 46 + mountpoint = "/"; 47 + mountOptions = [ 48 + "defaults" 49 + ]; 50 + }; 51 + }; 52 + }; 53 + }; 54 + }; 55 + }; 56 + }
+37
hosts/spindle/services/nginx.nix
··· 1 + { 2 + services.nginx = { 3 + enable = true; 4 + virtualHosts = { 5 + "spindle.alpha.tangled.sh" = { 6 + forceSSL = true; 7 + enableACME = true; 8 + locations."/" = { 9 + proxyPass = "http://127.0.0.1:6555"; 10 + }; 11 + locations."/events" = { 12 + proxyPass = "http://127.0.0.1:6555"; 13 + extraConfig = '' 14 + proxy_set_header X-Forwarded-For $remote_addr; 15 + proxy_set_header Host $host; 16 + proxy_set_header Upgrade $http_upgrade; 17 + proxy_set_header Connection "upgrade"; 18 + ''; 19 + }; 20 + locations."/logs/" = { 21 + proxyPass = "http://127.0.0.1:6555"; 22 + extraConfig = '' 23 + proxy_set_header X-Forwarded-For $remote_addr; 24 + proxy_set_header Host $host; 25 + proxy_set_header Upgrade $http_upgrade; 26 + proxy_set_header Connection "upgrade"; 27 + ''; 28 + }; 29 + }; 30 + }; 31 + }; 32 + security.acme = { 33 + acceptTerms = true; 34 + defaults.email = "team@tangled.org"; 35 + }; 36 + networking.firewall.allowedTCPPorts = [ 80 443 ]; 37 + }
+39
hosts/spindle/services/openbao/openbao.nix
··· 1 + { config, pkgs, lib, ... }: 2 + { 3 + # Create openbao user and group 4 + users.groups.openbao = {}; 5 + 6 + users.users.openbao = { 7 + isSystemUser = true; 8 + group = "openbao"; 9 + home = "/var/lib/openbao"; 10 + createHome = true; 11 + description = "OpenBao service user"; 12 + }; 13 + 14 + systemd.services.openbao = { 15 + serviceConfig = { 16 + DynamicUser = lib.mkForce false; 17 + User = "openbao"; 18 + Group = "openbao"; 19 + }; 20 + }; 21 + 22 + services.openbao = { 23 + enable = true; 24 + settings = { 25 + ui = true; 26 + 27 + listener.default = { 28 + type = "tcp"; 29 + address = "127.0.0.1:8201"; 30 + tls_disable = true; 31 + }; 32 + 33 + cluster_addr = "http://127.0.0.1:8202"; 34 + api_addr = "http://127.0.0.1:8201"; 35 + 36 + storage.raft.path = "/var/lib/openbao"; 37 + }; 38 + }; 39 + }
+100
hosts/spindle/services/openbao/proxy.nix
··· 1 + { pkgs, ... }: 2 + 3 + { 4 + systemd.services.openbao-proxy = { 5 + description = "OpenBao Proxy with Auto-Auth"; 6 + after = [ "network.target" ]; 7 + wantedBy = [ "multi-user.target" ]; 8 + serviceConfig = { 9 + User = "root"; 10 + ExecStart = "${pkgs.openbao}/bin/bao proxy -config=/etc/openbao/proxy.hcl"; 11 + Restart = "always"; 12 + RestartSec = "5"; 13 + LimitNOFILE = "65536"; 14 + }; 15 + }; 16 + 17 + 18 + 19 + environment.etc."openbao/proxy.hcl".text = '' 20 + vault { 21 + address = "http://localhost:8201" 22 + 23 + # Retry configuration 24 + retry { 25 + num_retries = 5 26 + } 27 + } 28 + 29 + # Auto-Auth using AppRole 30 + auto_auth { 31 + method "approle" { 32 + mount_path = "auth/approle" 33 + config = { 34 + role_id_file_path = "/etc/openbao/role-id" 35 + secret_id_file_path = "/etc/openbao/secret-id" 36 + remove_secret_id_file_after_reading = false 37 + } 38 + } 39 + 40 + # Write authenticated token to file 41 + sink "file" { 42 + config = { 43 + path = "/var/lib/openbao/token" 44 + mode = 0640 45 + } 46 + } 47 + } 48 + 49 + # API Proxy listener for Spindle 50 + listener "tcp" { 51 + address = "127.0.0.1:8200" 52 + tls_disable = true 53 + 54 + # Security headers 55 + require_request_header = false 56 + 57 + # Enable proxy API for management 58 + proxy_api { 59 + enable_quit = true 60 + } 61 + } 62 + 63 + # Enable API proxy with auto-auth token 64 + api_proxy { 65 + use_auto_auth_token = true 66 + } 67 + 68 + cache { 69 + } 70 + 71 + # Logging configuration 72 + log_level = "info" 73 + log_format = "standard" 74 + log_file = "/var/log/openbao/proxy.log" 75 + log_rotate_duration = "24h" 76 + log_rotate_max_files = 30 77 + 78 + # Process management 79 + pid_file = "/var/lib/openbao/proxy.pid" 80 + 81 + # Disable idle connections for reliability 82 + disable_idle_connections = ["auto-auth", "proxying"] 83 + ''; 84 + 85 + # Create necessary directories and files 86 + systemd.tmpfiles.rules = [ 87 + # Directories 88 + "d /var/lib/openbao 0755 root root -" 89 + "d /var/lib/openbao/cache 0755 root root -" 90 + "d /var/log/openbao 0755 root root -" 91 + "d /etc/openbao 0755 root root -" 92 + 93 + # Credential files (content must be populated externally) 94 + "f /etc/openbao/role-id 0600 root root -" 95 + "f /etc/openbao/secret-id 0600 root root -" 96 + 97 + # Configuration file 98 + "f /etc/openbao/proxy.hcl 0644 root root -" 99 + ]; 100 + }
+19
hosts/spindle/services/spindle.nix
··· 1 + { config, pkgs, ... }: 2 + { 3 + services.tangled.spindle = { 4 + enable = true; 5 + server = { 6 + owner = "did:plc:wshs7t2adsemcrrd4snkeqli"; # @tangled.sh 7 + hostname = "spindle.alpha.tangled.sh"; 8 + listenAddr = "127.0.0.1:6555"; 9 + queueSize = 100; 10 + maxJobCount = 2; 11 + secrets = { 12 + provider = "openbao"; 13 + }; 14 + }; 15 + pipelines = { 16 + workflowTimeout = "15m"; 17 + }; 18 + }; 19 + }