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 }
+238 -16
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": 1750040002, 33 - "narHash": "sha256-KrC9iOVYIn6ukpVlHbqSA4hYCZ6oDyJKrcLqv4c5v84=", 48 + "lastModified": 1766150702, 49 + "narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=", 34 50 "owner": "nix-community", 35 51 "repo": "disko", 36 - "rev": "7f1857b31522062a6a00f88cbccf86b43acceed1", 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 }, 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": { 125 + "inputs": { 126 + "flake-utils": "flake-utils_2", 127 + "nixpkgs": [ 128 + "tangled", 129 + "nixpkgs" 130 + ] 131 + }, 132 + "locked": { 133 + "lastModified": 1763982521, 134 + "narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=", 135 + "owner": "nix-community", 136 + "repo": "gomod2nix", 137 + "rev": "02e63a239d6eabd595db56852535992c898eba72", 138 + "type": "github" 139 + }, 140 + "original": { 141 + "owner": "nix-community", 142 + "repo": "gomod2nix", 143 + "type": "github" 144 + } 145 + }, 146 + "htmx-src": { 147 + "flake": false, 148 + "locked": { 149 + "narHash": "sha256-nm6avZuEBg67SSyyZUhjpXVNstHHgUxrtBHqJgowU08=", 150 + "type": "file", 151 + "url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js" 152 + }, 153 + "original": { 154 + "type": "file", 155 + "url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js" 156 + } 157 + }, 158 + "htmx-ws-src": { 159 + "flake": false, 160 + "locked": { 161 + "narHash": "sha256-2fg6KyEJoO24q0fQqbz9RMaYNPQrMwpZh29tkSqdqGY=", 162 + "type": "file", 163 + "url": "https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.2" 164 + }, 165 + "original": { 166 + "type": "file", 167 + "url": "https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.2" 168 + } 169 + }, 170 + "ibm-plex-mono-src": { 171 + "flake": false, 172 + "locked": { 173 + "lastModified": 1731402384, 174 + "narHash": "sha256-OwUmrPfEehLDz0fl2ChYLK8FQM2p0G1+EMrGsYEq+6g=", 175 + "type": "tarball", 176 + "url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip" 177 + }, 178 + "original": { 179 + "type": "tarball", 180 + "url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip" 181 + } 182 + }, 183 + "indigo": { 184 + "flake": false, 185 + "locked": { 186 + "lastModified": 1753693716, 187 + "narHash": "sha256-DMIKnCJRODQXEHUxA+7mLzRALmnZhkkbHlFT2rCQYrE=", 188 + "owner": "oppiliappan", 189 + "repo": "indigo", 190 + "rev": "5f170569da9360f57add450a278d73538092d8ca", 191 + "type": "github" 192 + }, 193 + "original": { 194 + "owner": "oppiliappan", 195 + "repo": "indigo", 196 + "type": "github" 197 + } 198 + }, 199 + "inter-fonts-src": { 200 + "flake": false, 201 + "locked": { 202 + "lastModified": 1731687360, 203 + "narHash": "sha256-5vdKKvHAeZi6igrfpbOdhZlDX2/5+UvzlnCQV6DdqoQ=", 204 + "type": "tarball", 205 + "url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip" 206 + }, 207 + "original": { 208 + "type": "tarball", 209 + "url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip" 210 + } 211 + }, 212 + "lucide-src": { 213 + "flake": false, 214 + "locked": { 215 + "lastModified": 1754044466, 216 + "narHash": "sha256-+exBR2OToB1iv7ZQI2S4B0lXA/QRvC9n6U99UxGpJGs=", 217 + "type": "tarball", 218 + "url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip" 219 + }, 220 + "original": { 221 + "type": "tarball", 222 + "url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip" 223 + } 224 + }, 76 225 "nixery-flake": { 77 226 "flake": false, 78 227 "locked": { 79 - "lastModified": 1745149613, 80 - "narHash": "sha256-rcSnsnSWA0IUjmbG2iSpvVB0702tcR3zIyU3iFJBo0g=", 228 + "lastModified": 1762501370, 229 + "narHash": "sha256-WO2NvvFB3KkFfChE5F6ghog7mvBAVKpMsQMqwadZT4k=", 81 230 "owner": "tazjin", 82 231 "repo": "nixery", 83 - "rev": "c6d4692b1b6eb105c9abce0411d2ef4b8708a6e1", 232 + "rev": "be8a4005de3f27f95e677e7b61abef387d4a840d", 84 233 "type": "github" 85 234 }, 86 235 "original": { ··· 91 240 }, 92 241 "nixpkgs": { 93 242 "locked": { 94 - "lastModified": 1746461020, 95 - "narHash": "sha256-7+pG1I9jvxNlmln4YgnlW4o+w0TZX24k688mibiFDUE=", 243 + "lastModified": 1683408522, 244 + "narHash": "sha256-9kcPh6Uxo17a3kK3XCHhcWiV1Yu1kYj22RHiymUhMkU=", 96 245 "owner": "NixOS", 97 246 "repo": "nixpkgs", 98 - "rev": "3730d8a308f94996a9ba7c7138ede69c1b9ac4ae", 247 + "rev": "897876e4c484f1e8f92009fd11b7d988a121a4e7", 99 248 "type": "github" 100 249 }, 101 250 "original": { ··· 107 256 }, 108 257 "nixpkgs_2": { 109 258 "locked": { 110 - "lastModified": 1750134718, 111 - "narHash": "sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI=", 259 + "lastModified": 1767634882, 260 + "narHash": "sha256-2GffSfQxe3sedHzK+sTKlYo/NTIAGzbFCIsNMUPAAnk=", 112 261 "owner": "nixos", 113 262 "repo": "nixpkgs", 114 - "rev": "9e83b64f727c88a7711a2c463a7b16eedb69a84c", 263 + "rev": "3c9db02515ef1d9b6b709fc60ba9a540957f661c", 264 + "type": "github" 265 + }, 266 + "original": { 267 + "owner": "nixos", 268 + "ref": "nixos-25.11", 269 + "repo": "nixpkgs", 270 + "type": "github" 271 + } 272 + }, 273 + "nixpkgs_3": { 274 + "locked": { 275 + "lastModified": 1766070988, 276 + "narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=", 277 + "owner": "nixos", 278 + "repo": "nixpkgs", 279 + "rev": "c6245e83d836d0433170a16eb185cefe0572f8b8", 115 280 "type": "github" 116 281 }, 117 282 "original": { ··· 126 291 "colmena": "colmena", 127 292 "disko": "disko", 128 293 "nixery-flake": "nixery-flake", 129 - "nixpkgs": "nixpkgs_2" 294 + "nixpkgs": "nixpkgs_2", 295 + "tangled": "tangled" 296 + } 297 + }, 298 + "sqlite-lib-src": { 299 + "flake": false, 300 + "locked": { 301 + "lastModified": 1706631843, 302 + "narHash": "sha256-bJoMjirsBjm2Qk9KPiy3yV3+8b/POlYe76/FQbciHro=", 303 + "type": "tarball", 304 + "url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip" 305 + }, 306 + "original": { 307 + "type": "tarball", 308 + "url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip" 130 309 } 131 310 }, 132 311 "stable": { 133 312 "locked": { 134 - "lastModified": 1688392541, 135 - "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", 313 + "lastModified": 1669735802, 314 + "narHash": "sha256-qtG/o/i5ZWZLmXw108N2aPiVsxOcidpHJYNkT45ry9Q=", 136 315 "owner": "NixOS", 137 316 "repo": "nixpkgs", 138 - "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", 317 + "rev": "731cc710aeebecbf45a258e977e8b68350549522", 139 318 "type": "github" 140 319 }, 141 320 "original": { ··· 143 322 "ref": "nixos-22.11", 144 323 "repo": "nixpkgs", 145 324 "type": "github" 325 + } 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 + }, 342 + "tangled": { 343 + "inputs": { 344 + "actor-typeahead-src": "actor-typeahead-src", 345 + "flake-compat": "flake-compat_2", 346 + "gomod2nix": "gomod2nix", 347 + "htmx-src": "htmx-src", 348 + "htmx-ws-src": "htmx-ws-src", 349 + "ibm-plex-mono-src": "ibm-plex-mono-src", 350 + "indigo": "indigo", 351 + "inter-fonts-src": "inter-fonts-src", 352 + "lucide-src": "lucide-src", 353 + "nixpkgs": "nixpkgs_3", 354 + "sqlite-lib-src": "sqlite-lib-src" 355 + }, 356 + "locked": { 357 + "lastModified": 1767767073, 358 + "narHash": "sha256-BSZJ1TY5lGt7xNgFRtcKwYcSOI6VC2CHLfm7y/GgHwU=", 359 + "ref": "refs/heads/master", 360 + "rev": "6dc86ffbed5a290ca6a4890caa2dadea5c8b8a81", 361 + "revCount": 1792, 362 + "type": "git", 363 + "url": "https://tangled.org/tangled.org/core" 364 + }, 365 + "original": { 366 + "type": "git", 367 + "url": "https://tangled.org/tangled.org/core" 146 368 } 147 369 } 148 370 },
+105 -31
flake.nix
··· 1 1 { 2 2 description = "nix infra for tangled"; 3 + 3 4 inputs = { 4 - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; 5 + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; 6 + tangled.url = "git+https://tangled.org/tangled.org/core"; 5 7 colmena.url = "github:zhaofengli/colmena/release-0.4.x"; 6 8 disko = { 7 9 url = "github:nix-community/disko"; ··· 15 17 }; 16 18 }; 17 19 18 - outputs = 19 - { nixpkgs, disko, colmena, nixery-flake, ... }: 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 20 104 { 21 - nixosConfigurations.nixery = nixpkgs.lib.nixosSystem { 22 - system = "x86_64-linux"; 23 - modules = [ 24 - disko.nixosModules.disko 25 - ./hosts/nixery/configuration.nix 26 - ]; 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; 27 112 }; 113 + 114 + # colmena uses this 28 115 colmenaHive = colmena.lib.makeHive { 29 116 meta = { 30 - nixpkgs = nixpkgs.legacyPackages.x86_64-linux; 117 + nixpkgs = nixpkgs.legacyPackages.${system}; 31 118 specialArgs = { 119 + inherit commonArgs; 32 120 nixery-pkgs = import nixery-flake.outPath { 33 - pkgs = import nixpkgs { system = "x86_64-linux"; }; 121 + pkgs = import nixpkgs { inherit system; }; 34 122 }; 35 - commonArgs = import ./common/ssh.nix; 36 123 }; 37 124 }; 38 125 39 126 defaults = { pkgs, ... }: { 40 - environment.systemPackages = [ 41 - pkgs.curl 42 - ]; 127 + environment.systemPackages = [ pkgs.curl ]; 43 128 }; 44 - nixery = { pkgs, ... }: { 45 - deployment = { 46 - targetHost = "nixery.tangled.sh"; 47 - targetPort = 22; 48 - targetUser = "tangler"; 49 - buildOnTarget = true; 50 - }; 51 - nixpkgs.system = "x86_64-linux"; 52 129 53 - imports = [ 54 - disko.nixosModules.disko 55 - ./hosts/nixery/configuration.nix 56 - ./hosts/nixery/services/nginx.nix 57 - ./hosts/nixery/services/nixery.nix 58 - ]; 59 - time.timeZone = "Europe/Helsinki"; 60 - }; 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; 61 135 }; 62 136 }; 63 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 + }
+123
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 + # bot blocking 11 + appendHttpConfig = '' 12 + map $http_user_agent $block_bot { 13 + default 0; 14 + ~*PerplexityBot 1; 15 + ~*GPTBot 1; 16 + ~*ChatGPT-User 1; 17 + ~*CCBot 1; 18 + ~*anthropic-ai 1; 19 + ~*Claude-Web 1; 20 + } 21 + ''; 22 + 23 + streamConfig = '' 24 + upstream knot-sailor { 25 + server 94.237.110.185:22; 26 + } 27 + 28 + server { 29 + listen 22; 30 + listen [::]:22; 31 + proxy_pass knot-sailor; 32 + } 33 + ''; 34 + 35 + virtualHosts = { 36 + # Redirect tangled.sh โ†’ tangled.org 37 + "tangled.sh" = { 38 + serverAliases = [ "www.tangled.sh" ]; 39 + locations."/" = { 40 + return = "301 https://tangled.org$request_uri"; 41 + }; 42 + forceSSL = true; 43 + enableACME = true; 44 + }; 45 + 46 + # Redirect strings.tangled.sh โ†’ tangled.org/strings/* 47 + "strings.tangled.sh" = { 48 + locations."/" = { 49 + return = "301 https://tangled.org/strings$request_uri"; 50 + }; 51 + forceSSL = true; 52 + enableACME = true; 53 + }; 54 + 55 + # Redirect strings.tangled.org โ†’ tangled.org/strings/* 56 + "strings.tangled.org" = { 57 + locations."/" = { 58 + return = "301 https://tangled.org/strings$request_uri"; 59 + }; 60 + forceSSL = true; 61 + enableACME = true; 62 + }; 63 + 64 + # Main app on tangled.org 65 + "tangled.org" = { 66 + serverAliases = [ "www.tangled.org" ]; 67 + 68 + forceSSL = true; 69 + enableACME = true; 70 + 71 + extraConfig = '' 72 + if ($block_bot) { 73 + return 403; 74 + } 75 + 76 + # Redirect www โ†’ bare domain 77 + if ($host = www.tangled.org) { 78 + return 301 https://tangled.org$request_uri; 79 + } 80 + 81 + client_max_body_size 100M; 82 + ''; 83 + 84 + locations."~ ^/@tangled\\.sh(/.*)?$" = { 85 + extraConfig = '' 86 + rewrite ^/@tangled\.sh(.*)$ https://tangled.org/@tangled.org$1 permanent; 87 + ''; 88 + }; 89 + 90 + locations."~ ^/tangled\\.sh(/.*)?$" = { 91 + extraConfig = '' 92 + rewrite ^/tangled\.sh(.*)$ https://tangled.org/tangled.org$1 permanent; 93 + ''; 94 + }; 95 + 96 + 97 + locations."~ /logs$" = { 98 + proxyPass = "http://127.0.0.1:3000"; 99 + proxyWebsockets = true; 100 + extraConfig = '' 101 + proxy_read_timeout 86400; 102 + ''; 103 + }; 104 + 105 + locations."/" = { 106 + proxyPass = "http://127.0.0.1:3000"; 107 + extraConfig = '' 108 + client_max_body_size 100M; 109 + ''; 110 + }; 111 + }; 112 + }; 113 + }; 114 + 115 + # Open firewall ports 116 + networking.firewall.allowedTCPPorts = [ 80 443 2222 22 ]; 117 + 118 + # ACME configuration for Let's Encrypt 119 + security.acme = { 120 + acceptTerms = true; 121 + defaults.email = "team@tangled.org"; 122 + }; 123 + }
+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 + }
+16
hosts/nixery/configuration.nix
··· 19 19 networking.hostName = "nixery"; 20 20 services = { 21 21 openssh.enable = true; 22 + tangled.spindle = { 23 + enable = true; 24 + server = { 25 + owner = "did:plc:wshs7t2adsemcrrd4snkeqli"; # @tangled.sh 26 + hostname = "spindle.tangled.sh"; 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"; 36 + }; 37 + }; 22 38 }; 23 39 24 40 virtualisation.docker = {
+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 + }
+49 -5
hosts/nixery/services/nginx.nix
··· 1 + { tangled-pkgs, pkgs, ... }: 2 + 1 3 { 2 4 services.nginx = { 3 5 enable = true; 4 - virtualHosts."nixery.tangled.sh" = { 5 - addSSL = true; 6 - enableACME = true; 7 - locations."/" = { 8 - proxyPass = "http://localhost:8080"; 6 + virtualHosts = { 7 + "docs.tangled.org" = { 8 + forceSSL = true; 9 + enableACME = true; 10 + root = "${tangled-pkgs.docs}"; 11 + locations."/" = { 12 + tryFiles = "$uri $uri/ =404"; 13 + index = "index.html"; 14 + }; 15 + }; 16 + "nixery.tangled.sh" = { 17 + forceSSL = true; 18 + enableACME = true; 19 + locations."/" = { 20 + proxyPass = "http://localhost:8080"; 21 + extraConfig = '' 22 + proxy_set_header Host $host; 23 + proxy_set_header X-Real-IP $remote_addr; 24 + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 25 + proxy_set_header X-Forwarded-Proto $scheme; 26 + ''; 27 + }; 28 + }; 29 + "spindle.tangled.sh" = { 30 + forceSSL = true; 31 + enableACME = true; 32 + locations."/" = { 33 + proxyPass = "http://localhost:6555"; 34 + }; 35 + locations."/events" = { 36 + proxyPass = "http://localhost:6555"; 37 + extraConfig = '' 38 + proxy_set_header X-Forwarded-For $remote_addr; 39 + proxy_set_header Host $host; 40 + proxy_set_header Upgrade $http_upgrade; 41 + proxy_set_header Connection "upgrade"; 42 + ''; 43 + }; 44 + locations."/logs/" = { 45 + proxyPass = "http://localhost:6555"; 46 + extraConfig = '' 47 + proxy_set_header X-Forwarded-For $remote_addr; 48 + proxy_set_header Host $host; 49 + proxy_set_header Upgrade $http_upgrade; 50 + proxy_set_header Connection "upgrade"; 51 + ''; 52 + }; 9 53 }; 10 54 }; 11 55 };
+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 + }