Tangled infrastructure definitions in Nix

Compare changes

Choose any two refs to compare.

+90 -78
flake.nix
··· 1 1 { 2 2 description = "nix infra for tangled"; 3 + 3 4 inputs = { 4 5 nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; 5 6 tangled.url = "git+https://tangled.org/tangled.org/core"; ··· 16 17 }; 17 18 }; 18 19 19 - outputs = 20 - { nixpkgs, disko, colmena, nixery-flake, tangled, ... }: 21 - { 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 - ]; 29 - }; 30 - nixosConfigurations.pds = nixpkgs.lib.nixosSystem { 31 - system = "x86_64-linux"; 32 - specialArgs = { 33 - commonArgs = import ./common/ssh.nix; 34 - }; 35 - modules = [ 36 - disko.nixosModules.disko 37 - ./hosts/pds/configuration.nix 38 - ]; 39 - }; 40 - nixosConfigurations.appview = nixpkgs.lib.nixosSystem { 41 - system = "x86_64-linux"; 42 - specialArgs = { 43 - commonArgs = import ./common/ssh.nix; 44 - }; 45 - modules = [ 46 - disko.nixosModules.disko 47 - ./hosts/appview/configuration.nix 48 - ]; 49 - }; 20 + outputs = { nixpkgs, disko, colmena, nixery-flake, tangled, ... }: 21 + let 22 + system = "x86_64-linux"; 23 + commonArgs = import ./common/ssh.nix; 50 24 51 - colmenaHive = colmena.lib.makeHive { 52 - meta = { 53 - nixpkgs = nixpkgs.legacyPackages.x86_64-linux; 54 - specialArgs = { 55 - nixery-pkgs = import nixery-flake.outPath { 56 - pkgs = import nixpkgs { system = "x86_64-linux"; }; 57 - }; 58 - tangled-pkgs = tangled.packages.x86_64-linux; 59 - commonArgs = import ./common/ssh.nix; 60 - }; 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; 61 34 }; 62 35 63 - defaults = { pkgs, ... }: { 64 - environment.systemPackages = [ 65 - pkgs.curl 66 - ]; 67 - }; 68 - appview = { pkgs, ... }: { 36 + # Helper function to create colmena host 37 + mkColmenaHost = hostname: targetHost: targetPort: extraModules: 38 + { 69 39 deployment = { 70 - targetHost = "alpha.tangled.sh"; 71 - targetPort = 22; 40 + inherit targetHost; 41 + inherit targetPort; 72 42 targetUser = "tangler"; 73 43 buildOnTarget = true; 74 44 }; 75 - nixpkgs.system = "x86_64-linux"; 45 + nixpkgs.system = system; 46 + time.timeZone = "Europe/Helsinki"; 76 47 imports = [ 77 48 disko.nixosModules.disko 49 + ./hosts/${hostname}/configuration.nix 50 + ] ++ extraModules; 51 + }; 52 + 53 + # Host configurations 54 + hosts = { 55 + appview = { 56 + modules = [ 78 57 tangled.nixosModules.appview 79 - ./hosts/appview/configuration.nix 80 58 ./hosts/appview/services/appview.nix 81 - ./hosts/appview/services/nginx-alpha.nix 59 + ./hosts/appview/services/nginx.nix 82 60 ]; 83 - time.timeZone = "Europe/Helsinki"; 61 + target = "95.111.205.38"; 84 62 }; 85 - pds = { pkgs, ... }: { 86 - deployment = { 87 - targetHost = "tngl.sh"; 88 - targetPort = 22; 89 - targetUser = "tangler"; 90 - buildOnTarget = true; 91 - }; 92 - nixpkgs.system = "x86_64-linux"; 93 63 94 - imports = [ 95 - disko.nixosModules.disko 96 - ./hosts/pds/configuration.nix 64 + pds = { 65 + modules = [ 97 66 ./hosts/pds/services/nginx.nix 98 67 ./hosts/pds/services/pds.nix 99 68 ]; 100 - time.timeZone = "Europe/Helsinki"; 69 + target = "tngl.sh"; 101 70 }; 102 71 103 - nixery = { pkgs, ... }: { 104 - deployment = { 105 - targetHost = "nixery.tangled.sh"; 106 - targetPort = 22; 107 - targetUser = "tangler"; 108 - buildOnTarget = true; 109 - }; 110 - nixpkgs.system = "x86_64-linux"; 111 - 112 - imports = [ 113 - disko.nixosModules.disko 72 + nixery = { 73 + modules = [ 114 74 tangled.nixosModules.spindle 115 - ./hosts/nixery/configuration.nix 116 75 ./hosts/nixery/services/nginx.nix 117 76 ./hosts/nixery/services/openbao/openbao.nix 118 77 ./hosts/nixery/services/openbao/proxy.nix 119 78 ./hosts/nixery/services/nixery.nix 120 79 ]; 121 - time.timeZone = "Europe/Helsinki"; 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"; 122 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 104 + { 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; 112 + }; 113 + 114 + # colmena uses this 115 + colmenaHive = colmena.lib.makeHive { 116 + meta = { 117 + nixpkgs = nixpkgs.legacyPackages.${system}; 118 + specialArgs = { 119 + inherit commonArgs; 120 + nixery-pkgs = import nixery-flake.outPath { 121 + pkgs = import nixpkgs { inherit system; }; 122 + }; 123 + }; 124 + }; 125 + 126 + defaults = { pkgs, ... }: { 127 + environment.systemPackages = [ pkgs.curl ]; 128 + }; 129 + 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; 123 135 }; 124 136 }; 125 137 }
+5
hosts/appview/configuration.nix
··· 19 19 networking.hostName = "appview-arn"; 20 20 services = { 21 21 openssh.enable = true; 22 + openssh.ports = [2222]; 22 23 }; 24 + 25 + # networking.extraHosts = '' 26 + # 85.9.211.103 knot1.tangled.sh 27 + # ''; 23 28 24 29 25 30 nix = {
+6
hosts/appview/services/litestream.nix
··· 1 + { 2 + services.litestream = { 3 + enable = true; 4 + environmentFile = "/etc/secrets/litestream.env" 5 + }; 6 + }
+38 -8
hosts/appview/services/nginx.nix
··· 7 7 recommendedOptimisation = true; 8 8 recommendedGzipSettings = true; 9 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 + 10 35 virtualHosts = { 11 36 # Redirect tangled.sh โ†’ tangled.org 12 37 "tangled.sh" = { ··· 44 69 enableACME = true; 45 70 46 71 extraConfig = '' 72 + if ($block_bot) { 73 + return 403; 74 + } 75 + 47 76 # Redirect www โ†’ bare domain 48 77 if ($host = www.tangled.org) { 49 78 return 301 https://tangled.org$request_uri; ··· 53 82 ''; 54 83 55 84 locations."~ ^/@tangled\\.sh(/.*)?$" = { 56 - return = "301 https://tangled.org/@tangled.org$1$is_args$args"; 85 + extraConfig = '' 86 + rewrite ^/@tangled\.sh(.*)$ https://tangled.org/@tangled.org$1 permanent; 87 + ''; 57 88 }; 58 89 59 90 locations."~ ^/tangled\\.sh(/.*)?$" = { 60 - return = "301 https://tangled.org/tangled.org$1$is_args$args"; 91 + extraConfig = '' 92 + rewrite ^/tangled\.sh(.*)$ https://tangled.org/tangled.org$1 permanent; 93 + ''; 61 94 }; 95 + 62 96 63 97 locations."~ /logs$" = { 64 98 proxyPass = "http://127.0.0.1:3000"; ··· 71 105 locations."/" = { 72 106 proxyPass = "http://127.0.0.1:3000"; 73 107 extraConfig = '' 74 - proxy_set_header Host $host; 75 - proxy_set_header X-Real-IP $remote_addr; 76 - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 77 - proxy_set_header X-Forwarded-Proto $scheme; 78 - include ${config.services.nginx.package}/conf/mime.types; 108 + client_max_body_size 100M; 79 109 ''; 80 110 }; 81 111 }; ··· 83 113 }; 84 114 85 115 # Open firewall ports 86 - networking.firewall.allowedTCPPorts = [ 80 443 ]; 116 + networking.firewall.allowedTCPPorts = [ 80 443 2222 22 ]; 87 117 88 118 # ACME configuration for Let's Encrypt 89 119 security.acme = {
+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 + }
+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 + }