Tangled infrastructure definitions in Nix

nixify appview

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>

anirudh.fi 9d28d365 82e955aa

verified
+22 -22
flake.lock
··· 45 45 ] 46 46 }, 47 47 "locked": { 48 - "lastModified": 1751854533, 49 - "narHash": "sha256-U/OQFplExOR1jazZY4KkaQkJqOl59xlh21HP9mI79Vc=", 48 + "lastModified": 1766150702, 49 + "narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=", 50 50 "owner": "nix-community", 51 51 "repo": "disko", 52 - "rev": "16b74a1e304197248a1bc663280f2548dbfcae3c", 52 + "rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378", 53 53 "type": "github" 54 54 }, 55 55 "original": { ··· 108 108 "systems": "systems" 109 109 }, 110 110 "locked": { 111 - "lastModified": 1694529238, 112 - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 111 + "lastModified": 1731533236, 112 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 113 113 "owner": "numtide", 114 114 "repo": "flake-utils", 115 - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 115 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 116 116 "type": "github" 117 117 }, 118 118 "original": { ··· 130 130 ] 131 131 }, 132 132 "locked": { 133 - "lastModified": 1754078208, 134 - "narHash": "sha256-YVoIFDCDpYuU3riaDEJ3xiGdPOtsx4sR5eTzHTytPV8=", 133 + "lastModified": 1763982521, 134 + "narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=", 135 135 "owner": "nix-community", 136 136 "repo": "gomod2nix", 137 - "rev": "7f963246a71626c7fc70b431a315c4388a0c95cf", 137 + "rev": "02e63a239d6eabd595db56852535992c898eba72", 138 138 "type": "github" 139 139 }, 140 140 "original": { ··· 225 225 "nixery-flake": { 226 226 "flake": false, 227 227 "locked": { 228 - "lastModified": 1745149613, 229 - "narHash": "sha256-rcSnsnSWA0IUjmbG2iSpvVB0702tcR3zIyU3iFJBo0g=", 228 + "lastModified": 1762501370, 229 + "narHash": "sha256-WO2NvvFB3KkFfChE5F6ghog7mvBAVKpMsQMqwadZT4k=", 230 230 "owner": "tazjin", 231 231 "repo": "nixery", 232 - "rev": "c6d4692b1b6eb105c9abce0411d2ef4b8708a6e1", 232 + "rev": "be8a4005de3f27f95e677e7b61abef387d4a840d", 233 233 "type": "github" 234 234 }, 235 235 "original": { ··· 256 256 }, 257 257 "nixpkgs_2": { 258 258 "locked": { 259 - "lastModified": 1758427187, 260 - "narHash": "sha256-pHpxZ/IyCwoTQPtFIAG2QaxuSm8jWzrzBGjwQZIttJc=", 259 + "lastModified": 1767379071, 260 + "narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=", 261 261 "owner": "nixos", 262 262 "repo": "nixpkgs", 263 - "rev": "554be6495561ff07b6c724047bdd7e0716aa7b46", 263 + "rev": "fb7944c166a3b630f177938e478f0378e64ce108", 264 264 "type": "github" 265 265 }, 266 266 "original": { ··· 272 272 }, 273 273 "nixpkgs_3": { 274 274 "locked": { 275 - "lastModified": 1751984180, 276 - "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", 275 + "lastModified": 1766070988, 276 + "narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=", 277 277 "owner": "nixos", 278 278 "repo": "nixpkgs", 279 - "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", 279 + "rev": "c6245e83d836d0433170a16eb185cefe0572f8b8", 280 280 "type": "github" 281 281 }, 282 282 "original": { ··· 354 354 "sqlite-lib-src": "sqlite-lib-src" 355 355 }, 356 356 "locked": { 357 - "lastModified": 1763358301, 358 - "narHash": "sha256-i2fc5A2qoHvubTWhcpG5S6Hy42DlYOyEgJjR6TaQvxg=", 357 + "lastModified": 1767683698, 358 + "narHash": "sha256-MFrfNmTKTdOOsyXUvvqPwH6zqvDZZpURnd7QdJkVOgU=", 359 359 "ref": "refs/heads/master", 360 - "rev": "a17d65a1a78d8889e0b971be2bc5d32be445ce10", 361 - "revCount": 1666, 360 + "rev": "b31a2a3590fefc4c70817f94a20076df2428b4d3", 361 + "revCount": 1791, 362 362 "type": "git", 363 363 "url": "https://tangled.org/@tangled.org/core" 364 364 },
+30
flake.nix
··· 29 29 }; 30 30 nixosConfigurations.pds = nixpkgs.lib.nixosSystem { 31 31 system = "x86_64-linux"; 32 + specialArgs = { 33 + commonArgs = import ./common/ssh.nix; 34 + }; 32 35 modules = [ 33 36 disko.nixosModules.disko 34 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 35 48 ]; 36 49 }; 37 50 ··· 50 63 environment.systemPackages = [ 51 64 pkgs.curl 52 65 ]; 66 + }; 67 + appview = { pkgs, ... }: { 68 + deployment = { 69 + targetHost = "alpha.tangled.sh"; 70 + targetPort = 22; 71 + targetUser = "tangler"; 72 + buildOnTarget = true; 73 + }; 74 + nixpkgs.system = "x86_64-linux"; 75 + imports = [ 76 + disko.nixosModules.disko 77 + tangled.nixosModules.appview 78 + ./hosts/appview/configuration.nix 79 + ./hosts/appview/services/appview.nix 80 + ./hosts/appview/services/nginx-alpha.nix 81 + ]; 82 + time.timeZone = "Europe/Helsinki"; 53 83 }; 54 84 pds = { pkgs, ... }: { 55 85 deployment = {
+57
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 + }; 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/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 + }
+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 + }
+93
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 + virtualHosts = { 11 + # Redirect tangled.sh → tangled.org 12 + "tangled.sh" = { 13 + serverAliases = [ "www.tangled.sh" ]; 14 + locations."/" = { 15 + return = "301 https://tangled.org$request_uri"; 16 + }; 17 + forceSSL = true; 18 + enableACME = true; 19 + }; 20 + 21 + # Redirect strings.tangled.sh → tangled.org/strings/* 22 + "strings.tangled.sh" = { 23 + locations."/" = { 24 + return = "301 https://tangled.org/strings$request_uri"; 25 + }; 26 + forceSSL = true; 27 + enableACME = true; 28 + }; 29 + 30 + # Redirect strings.tangled.org → tangled.org/strings/* 31 + "strings.tangled.org" = { 32 + locations."/" = { 33 + return = "301 https://tangled.org/strings$request_uri"; 34 + }; 35 + forceSSL = true; 36 + enableACME = true; 37 + }; 38 + 39 + # Main app on tangled.org 40 + "tangled.org" = { 41 + serverAliases = [ "www.tangled.org" ]; 42 + 43 + forceSSL = true; 44 + enableACME = true; 45 + 46 + extraConfig = '' 47 + # Redirect www → bare domain 48 + if ($host = www.tangled.org) { 49 + return 301 https://tangled.org$request_uri; 50 + } 51 + 52 + client_max_body_size 100M; 53 + ''; 54 + 55 + locations."~ ^/@tangled\\.sh(/.*)?$" = { 56 + return = "301 https://tangled.org/@tangled.org$1$is_args$args"; 57 + }; 58 + 59 + locations."~ ^/tangled\\.sh(/.*)?$" = { 60 + return = "301 https://tangled.org/tangled.org$1$is_args$args"; 61 + }; 62 + 63 + locations."~ /logs$" = { 64 + proxyPass = "http://127.0.0.1:3000"; 65 + proxyWebsockets = true; 66 + extraConfig = '' 67 + proxy_read_timeout 86400; 68 + ''; 69 + }; 70 + 71 + locations."/" = { 72 + proxyPass = "http://127.0.0.1:3000"; 73 + 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; 79 + ''; 80 + }; 81 + }; 82 + }; 83 + }; 84 + 85 + # Open firewall ports 86 + networking.firewall.allowedTCPPorts = [ 80 443 ]; 87 + 88 + # ACME configuration for Let's Encrypt 89 + security.acme = { 90 + acceptTerms = true; 91 + defaults.email = "team@tangled.org"; 92 + }; 93 + }