Kieran's opinionated (and probably slightly dumb) nix config

feat: add extra service defs

dunkirk.sh dd6e4cdf 01e32d95

verified
+63 -1
+1
docs/src/services/README.md
··· 42 42 | herald | herald.dunkirk.sh | Git SSH hosting + email | 43 43 | knot | knot.dunkirk.sh | Tangled git hosting | 44 44 | spindle | spindle.dunkirk.sh | Tangled CI | 45 + | triage-agent | triage.dunkirk.sh | AI-powered service triage webhook | 45 46 | n8n | n8n.dunkirk.sh | Workflow automation | 46 47 47 48 ## Services manifest
+56 -1
lib/services.nix
··· 136 136 data = { sqlite = null; postgres = null; files = []; }; 137 137 }) enabled; 138 138 139 - serviceList = (lib.mapAttrsToList mkEntry standardServices) ++ emojibotInstances; 139 + # Custom services that don't use mkService but should appear in the manifest 140 + customServices = let 141 + noData = { sqlite = null; postgres = null; files = []; }; 142 + mkCustom = name: attrs: { inherit name; data = noData; } // attrs; 143 + in lib.concatLists [ 144 + (lib.optional ((allServices.herald.enable or false) && (allServices.herald ? domain)) (mkCustom "herald" { 145 + description = "RSS-to-Email via SSH"; 146 + domain = allServices.herald.domain; 147 + port = allServices.herald.httpPort or 8085; 148 + runtime = "go"; 149 + repository = null; 150 + health_url = "https://${allServices.herald.domain}"; 151 + })) 152 + (lib.optional ((allServices.triage-agent.enable or false) && (allServices.triage-agent ? domain)) (mkCustom "triage-agent" { 153 + description = "AI-powered service triage webhook"; 154 + domain = allServices.triage-agent.domain; 155 + port = allServices.triage-agent.port or 3200; 156 + runtime = "bun"; 157 + repository = null; 158 + health_url = "https://${allServices.triage-agent.domain}/health"; 159 + })) 160 + (lib.optional ((allServices.frps.enable or false) && (allServices.frps ? domain)) (mkCustom "bore" { 161 + description = "HTTP/TCP/UDP tunnel proxy"; 162 + domain = allServices.frps.domain; 163 + port = allServices.frps.vhostHTTPPort or 7080; 164 + runtime = "go"; 165 + repository = null; 166 + health_url = "https://${allServices.frps.domain}"; 167 + })) 168 + (lib.optional (config.services.tangled.knot.enable or false) (mkCustom "knot" { 169 + description = "Tangled git hosting"; 170 + domain = config.services.tangled.knot.server.hostname or "knot.dunkirk.sh"; 171 + port = 5555; 172 + runtime = "go"; 173 + repository = null; 174 + health_url = "https://${config.services.tangled.knot.server.hostname or "knot.dunkirk.sh"}"; 175 + })) 176 + (lib.optional (config.services.tangled.spindle.enable or false) (mkCustom "spindle" { 177 + description = "Tangled CI"; 178 + domain = config.services.tangled.spindle.server.hostname or "spindle.dunkirk.sh"; 179 + port = 6555; 180 + runtime = "go"; 181 + repository = null; 182 + health_url = "https://${config.services.tangled.spindle.server.hostname or "spindle.dunkirk.sh"}"; 183 + })) 184 + (lib.optional (config.services.n8n.enable or false) (mkCustom "n8n" { 185 + description = "Workflow automation"; 186 + domain = config.services.n8n.environment.N8N_HOST or "n8n.dunkirk.sh"; 187 + port = 5678; 188 + runtime = "node"; 189 + repository = null; 190 + health_url = "https://${config.services.n8n.environment.N8N_HOST or "n8n.dunkirk.sh"}/healthz"; 191 + })) 192 + ]; 193 + 194 + serviceList = (lib.mapAttrsToList mkEntry standardServices) ++ emojibotInstances ++ customServices; 140 195 in 141 196 lib.sort (a: b: a.name < b.name) serviceList; 142 197
+6
modules/nixos/services/triage-agent.nix
··· 100 100 Bun.serve({ 101 101 port: PORT, 102 102 async fetch(req) { 103 + const url = new URL(req.url); 104 + 105 + if (req.method === "GET" && url.pathname === "/health") { 106 + return Response.json({ status: "ok" }); 107 + } 108 + 103 109 if (req.method !== "POST") { 104 110 return new Response("method not allowed", { status: 405 }); 105 111 }