a more proper nixos module for the tangled knotserver

fix: compat with latest @tangled.sh/core

+10
changelog.md
···
··· 1 + # changelog 2 + 3 + ## 2025-06-23 4 + 5 + this update fixes usage with newer `@tangled.sh/core` revisions. 6 + 7 + - replaced wrapped packages with just a wrapped keyfetch binary 8 + - added `cfg.openFirewall` option to automatically open firewall ports (currently ssh only). 9 + - reject module usage with old `@tangled.sh/core` revisions. 10 + - (**tests**) also test-run the wrapped `keyfetch` binary
+66 -27
flake.lock
··· 33 "url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js" 34 } 35 }, 36 - "ia-fonts-src": { 37 "flake": false, 38 "locked": { 39 - "lastModified": 1686932517, 40 - "narHash": "sha256-2T165nFfCzO65/PIHauJA//S+zug5nUwPcg8NUEydfc=", 41 - "owner": "iaolo", 42 - "repo": "iA-Fonts", 43 - "rev": "f32c04c3058a75d7ce28919ce70fe8800817491b", 44 - "type": "github" 45 }, 46 "original": { 47 - "owner": "iaolo", 48 - "repo": "iA-Fonts", 49 - "type": "github" 50 } 51 }, 52 "indigo": { 53 "flake": false, 54 "locked": { 55 - "lastModified": 1738491661, 56 - "narHash": "sha256-+njDigkvjH4XmXZMog5Mp0K4x9mamHX6gSGJCZB9mE4=", 57 "owner": "oppiliappan", 58 "repo": "indigo", 59 - "rev": "feb802f02a462ac0a6392ffc3e40b0529f0cdf71", 60 "type": "github" 61 }, 62 "original": { ··· 65 "type": "github" 66 } 67 }, 68 "lucide-src": { 69 "flake": false, 70 "locked": { 71 - "narHash": "sha256-5ipNSxTlQ7627lGgsyZxk7vS1sr9RkrlR8/QMj2Zg6s=", 72 - "type": "file", 73 - "url": "https://unpkg.com/lucide@0.482.0" 74 }, 75 "original": { 76 - "type": "file", 77 - "url": "https://unpkg.com/lucide@0.482.0" 78 } 79 }, 80 "nixpkgs": { 81 "locked": { 82 - "lastModified": 1742069588, 83 - "narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=", 84 "owner": "NixOS", 85 "repo": "nixpkgs", 86 - "rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5", 87 "type": "github" 88 }, 89 "original": { ··· 99 "tangledCore": "tangledCore" 100 } 101 }, 102 "tangledCore": { 103 "inputs": { 104 "gitignore": "gitignore", 105 "htmx-src": "htmx-src", 106 - "ia-fonts-src": "ia-fonts-src", 107 "indigo": "indigo", 108 "lucide-src": "lucide-src", 109 "nixpkgs": [ 110 "nixpkgs" 111 - ] 112 }, 113 "locked": { 114 - "lastModified": 1742252247, 115 - "narHash": "sha256-4P2cyFbmfi4vO+cH5Rs8T0HjyIM9my7DDehMavPQ4ms=", 116 "ref": "refs/heads/master", 117 - "rev": "d27004cb9772260c5a2005fe1f53f1e44884f79e", 118 - "revCount": 336, 119 "type": "git", 120 "url": "https://tangled.sh/@tangled.sh/core" 121 },
··· 33 "url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js" 34 } 35 }, 36 + "htmx-ws-src": { 37 "flake": false, 38 "locked": { 39 + "narHash": "sha256-XbUFiv94ZPB6VVULoTWOsje5Gq1I+IT72lMc4CpUYrY=", 40 + "type": "file", 41 + "url": "https://unpkg.com/htmx.org@2.0.4/dist/ext/ws.js" 42 + }, 43 + "original": { 44 + "type": "file", 45 + "url": "https://unpkg.com/htmx.org@2.0.4/dist/ext/ws.js" 46 + } 47 + }, 48 + "ibm-plex-mono-src": { 49 + "flake": false, 50 + "locked": { 51 + "lastModified": 1731402384, 52 + "narHash": "sha256-OwUmrPfEehLDz0fl2ChYLK8FQM2p0G1+EMrGsYEq+6g=", 53 + "type": "tarball", 54 + "url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip" 55 }, 56 "original": { 57 + "type": "tarball", 58 + "url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip" 59 } 60 }, 61 "indigo": { 62 "flake": false, 63 "locked": { 64 + "lastModified": 1745333930, 65 + "narHash": "sha256-83fIHqDE+dfnZ88HaNuwfKFO+R0RKAM1WxMfNh/Matk=", 66 "owner": "oppiliappan", 67 "repo": "indigo", 68 + "rev": "e4e59280737b8676611fc077a228d47b3e8e9491", 69 "type": "github" 70 }, 71 "original": { ··· 74 "type": "github" 75 } 76 }, 77 + "inter-fonts-src": { 78 + "flake": false, 79 + "locked": { 80 + "lastModified": 1731687360, 81 + "narHash": "sha256-5vdKKvHAeZi6igrfpbOdhZlDX2/5+UvzlnCQV6DdqoQ=", 82 + "type": "tarball", 83 + "url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip" 84 + }, 85 + "original": { 86 + "type": "tarball", 87 + "url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip" 88 + } 89 + }, 90 "lucide-src": { 91 "flake": false, 92 "locked": { 93 + "lastModified": 1742302029, 94 + "narHash": "sha256-OyPVtpnC4/AAmPq84Wt1r1Gcs48d9KG+UBCtZK87e9k=", 95 + "type": "tarball", 96 + "url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip" 97 }, 98 "original": { 99 + "type": "tarball", 100 + "url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip" 101 } 102 }, 103 "nixpkgs": { 104 "locked": { 105 + "lastModified": 1750506804, 106 + "narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", 107 "owner": "NixOS", 108 "repo": "nixpkgs", 109 + "rev": "4206c4cb56751df534751b058295ea61357bbbaa", 110 "type": "github" 111 }, 112 "original": { ··· 122 "tangledCore": "tangledCore" 123 } 124 }, 125 + "sqlite-lib-src": { 126 + "flake": false, 127 + "locked": { 128 + "lastModified": 1706631843, 129 + "narHash": "sha256-bJoMjirsBjm2Qk9KPiy3yV3+8b/POlYe76/FQbciHro=", 130 + "type": "tarball", 131 + "url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip" 132 + }, 133 + "original": { 134 + "type": "tarball", 135 + "url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip" 136 + } 137 + }, 138 "tangledCore": { 139 "inputs": { 140 "gitignore": "gitignore", 141 "htmx-src": "htmx-src", 142 + "htmx-ws-src": "htmx-ws-src", 143 + "ibm-plex-mono-src": "ibm-plex-mono-src", 144 "indigo": "indigo", 145 + "inter-fonts-src": "inter-fonts-src", 146 "lucide-src": "lucide-src", 147 "nixpkgs": [ 148 "nixpkgs" 149 + ], 150 + "sqlite-lib-src": "sqlite-lib-src" 151 }, 152 "locked": { 153 + "lastModified": 1750580269, 154 + "narHash": "sha256-Y8WCUQYknbPPBN2nKCnL2B7rqFpIJxZ9dGB8ahmPPnA=", 155 "ref": "refs/heads/master", 156 + "rev": "fa3beeb9d299bbcf86440e77899773506e654e29", 157 + "revCount": 861, 158 "type": "git", 159 "url": "https://tangled.sh/@tangled.sh/core" 160 },
+30 -15
module.nix
··· 9 let 10 inherit (lib) 11 mkOption 12 types 13 mkIf 14 optional ··· 16 cfg = config.services.tangled-knotserver; 17 tangledPkgs = tangledFlake.packages.${pkgs.system}; 18 19 - wrapped-packages = 20 pkgs.runCommandCC "tangled-packages-wrapped" { nativeBuildInputs = [ pkgs.makeBinaryWrapper ]; } 21 '' 22 mkdir -p $out/bin 23 24 - makeBinaryWrapper ${lib.getExe' tangledPkgs.repoguard "repoguard"} $out/bin/repoguard \ 25 - --add-flags -internal-api=http://${cfg.server.internalListenAddr} 26 - # other flags are set by keyfetch 27 - 28 - makeBinaryWrapper ${lib.getExe' tangledPkgs.keyfetch "keyfetch"} $out/bin/keyfetch \ 29 - --add-flags "-repoguard-path=$out/bin/repoguard" \ 30 - --add-flags "-internal-api=http://${cfg.server.internalListenAddr}" \ 31 - --add-flags "-git-dir=${cfg.repo.scanPath}" \ 32 - --add-flags "-log-path=/var/log/knotserver/repoguard.log" 33 ''; 34 35 in ··· 40 41 options = { 42 services.tangled-knotserver = { 43 - enable = mkOption { 44 type = types.bool; 45 default = false; 46 - description = "Enable a tangled knotserver"; 47 }; 48 49 appviewEndpoint = mkOption { ··· 70 description = "Git email address for git operations that requires one."; 71 }; 72 }; 73 74 repo = { 75 scanPath = mkOption { ··· 166 tangled-knotserver: development mode is enabled. This is not recommended in production as signature checks are disabled. 167 ''; 168 169 environment.systemPackages = with pkgs; [ git ]; 170 171 users.users.${cfg.user} = { 172 home = cfg.repo.scanPath; 173 group = cfg.user; 174 isSystemUser = true; ··· 198 serviceConfig = { 199 User = cfg.user; 200 WorkingDirectory = cfg.repo.scanPath; 201 - ExecStart = lib.getExe' tangledPkgs.knotserver "knotserver"; 202 Restart = "always"; 203 204 StateDirectory = mkIf (lib.hasPrefix "/var/lib/tangled-knot" cfg.repo.scanPath) "tangled-knot"; ··· 222 }; 223 224 services.openssh = { 225 - enable = true; 226 extraConfig = '' 227 Match User ${cfg.user} 228 AuthorizedKeysCommand ${config.security.wrapperDir}/keyfetch ··· 236 owner = "root"; 237 group = config.users.groups.${cfg.user}.name; 238 permissions = "u+rx,go+x"; 239 - source = lib.getExe' wrapped-packages "keyfetch"; 240 }; 241 }; 242 }
··· 9 let 10 inherit (lib) 11 mkOption 12 + mkEnableOption 13 types 14 mkIf 15 optional ··· 17 cfg = config.services.tangled-knotserver; 18 tangledPkgs = tangledFlake.packages.${pkgs.system}; 19 20 + keyfetch-wrapped = 21 pkgs.runCommandCC "tangled-packages-wrapped" { nativeBuildInputs = [ pkgs.makeBinaryWrapper ]; } 22 '' 23 mkdir -p $out/bin 24 25 + makeBinaryWrapper ${lib.getExe' tangledPkgs.knot "knot"} $out/bin/keyfetch \ 26 + --add-flags "keys" \ 27 + --append-flags "-output authorized-keys" \ 28 + --append-flags "-internal-api=http://${cfg.server.internalListenAddr}" \ 29 + --append-flags "-git-dir=${cfg.repo.scanPath}" \ 30 + --append-flags "-log-path=/var/log/knotserver/repoguard.log" 31 ''; 32 33 in ··· 38 39 options = { 40 services.tangled-knotserver = { 41 + enable = mkEnableOption "a tangled knot server"; 42 + 43 + openFirewall = mkOption { 44 type = types.bool; 45 default = false; 46 + description = "Whether to automatically configure the firewall to open necessary ports."; 47 }; 48 49 appviewEndpoint = mkOption { ··· 70 description = "Git email address for git operations that requires one."; 71 }; 72 }; 73 + 74 + # TODO: should a `stateDirectory` option be added? 75 76 repo = { 77 scanPath = mkOption { ··· 168 tangled-knotserver: development mode is enabled. This is not recommended in production as signature checks are disabled. 169 ''; 170 171 + assertions = [ 172 + { 173 + assertion = tangledPkgs ? knot; 174 + message = "tangled-knotserver: your version of tangled flake is not compatible with this version of the knotserver module. please consider updating the pinned @tangled.sh/core version."; 175 + } 176 + ]; 177 + 178 environment.systemPackages = with pkgs; [ git ]; 179 180 users.users.${cfg.user} = { 181 + createHome = true; 182 home = cfg.repo.scanPath; 183 group = cfg.user; 184 isSystemUser = true; ··· 208 serviceConfig = { 209 User = cfg.user; 210 WorkingDirectory = cfg.repo.scanPath; 211 + ExecStart = if (tangledPkgs ? knotserver) # compat 212 + then lib.getExe' tangledPkgs.knotserver "knotserver" 213 + else "${lib.getExe' tangledPkgs.knot "knot"} server"; 214 Restart = "always"; 215 216 StateDirectory = mkIf (lib.hasPrefix "/var/lib/tangled-knot" cfg.repo.scanPath) "tangled-knot"; ··· 234 }; 235 236 services.openssh = { 237 + enable = true; # required for the module to actually function 238 extraConfig = '' 239 Match User ${cfg.user} 240 AuthorizedKeysCommand ${config.security.wrapperDir}/keyfetch ··· 248 owner = "root"; 249 group = config.users.groups.${cfg.user}.name; 250 permissions = "u+rx,go+x"; 251 + source = lib.getExe' keyfetch-wrapped "keyfetch"; 252 }; 253 + 254 + # open firewall ports if configured 255 + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [22]; 256 }; 257 }
+29
readme.md
··· 19 - logging to `/var/log/knotserver` prevents other users from messing around with repoguard, possibly a dos by just creating `/tmp/repoguard.log`. 20 - these changes should not affect usage at all, just implementation details. 21 22 ## stability 23 24 this module is stable to use.
··· 19 - logging to `/var/log/knotserver` prevents other users from messing around with repoguard, possibly a dos by just creating `/tmp/repoguard.log`. 20 - these changes should not affect usage at all, just implementation details. 21 22 + ## usage 23 + 24 + add this flake to your inputs, then import the module to your nixos config. 25 + 26 + whilst not required, it is recommended to add the core flake and let the module flake follow that input, since i don't have all day updating this repo. 27 + 28 + ```nix 29 + { 30 + inputs = { 31 + # ...other stuff like nixpkgs 32 + tangled.url = "git+https://tangled.sh/@tangled.sh/core"; 33 + knotserver-module = { 34 + url = "git+https://tangled.sh/@soopy.moe/knotserver-module"; 35 + inputs.tangledCore.follows = "tangled"; 36 + }; 37 + }; 38 + 39 + outputs = {nixpkgs, knotserver-module, ...}: { 40 + nixosConfigurations.saturday = nixpkgs.lib.nixosSystem { 41 + modules = [ 42 + knotserver-module.nixosModules.default 43 + ./configuration.nix 44 + ]; 45 + }; 46 + }; 47 + } 48 + ``` 49 + 50 + 51 ## stability 52 53 this module is stable to use.
+1
test.nix
··· 21 machine.wait_for_unit("knotserver.service") 22 machine.wait_for_open_port(5555) 23 machine.succeed('curl -f http://127.0.0.1:5555 | grep "This is a knot server"') 24 ''; 25 }
··· 21 machine.wait_for_unit("knotserver.service") 22 machine.wait_for_open_port(5555) 23 machine.succeed('curl -f http://127.0.0.1:5555 | grep "This is a knot server"') 24 + machine.succeed('/run/wrappers/bin/keyfetch') 25 ''; 26 }