diff --git a/VERSION b/VERSION index 097bc93..16b6bce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.28.4 +0.28.5 diff --git a/cmd/nomos/main.go b/cmd/nomos/main.go index 9017709..c1e3c22 100644 --- a/cmd/nomos/main.go +++ b/cmd/nomos/main.go @@ -28,6 +28,15 @@ func main() { fmt.Fprintln(os.Stderr, "usage: nomos serve") os.Exit(1) } + // Fast-path the Docker healthcheck BEFORE any Infisical/secrets init. The + // nomos runtime image is distroless (no shell/wget), so the container + // probes itself via `nomos healthcheck`. Secrets resolution retries + // Infisical ~4x per key when it's down (~30s), which would blow the 5s + // healthcheck timeout — so this must run first and stay trivial. + if os.Args[1] == "healthcheck" { + runHealthcheck() + return + } mcpURL := os.Getenv("NOMOS_MCP_URL") if mcpURL == "" { mcpURL = "http://localhost:8090/mcp" @@ -186,34 +195,35 @@ func main() { srv.Shutdown(context.Background()) clientPool.closeAll() - case "healthcheck": - // Self-probe for Docker healthcheck. The nomos runtime image is - // distroless (no shell/wget), so the container can't run wget — the - // binary probes its own /healthz instead. Exit 0 on 200, 1 otherwise. - addr := os.Getenv("NOMOS_LISTEN") - if addr == "" { - addr = ":8092" - } - host := addr - if strings.HasPrefix(host, ":") { - host = "127.0.0.1" + host - } - client := &http.Client{Timeout: 3 * time.Second} - resp, err := client.Get("http://" + strings.TrimPrefix(host, "http://") + "/healthz") - if err != nil { - os.Exit(1) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - os.Exit(1) - } - default: fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1]) os.Exit(1) } } +// runHealthcheck self-probes NOMOS_LISTEN/healthz and exits 0 on HTTP 200, +// 1 otherwise. Used by the Docker healthcheck (the distroless runtime image +// has no wget/shell). Must stay fast — call it before any secrets init. +func runHealthcheck() { + addr := os.Getenv("NOMOS_LISTEN") + if addr == "" { + addr = ":8092" + } + host := addr + if strings.HasPrefix(host, ":") { + host = "127.0.0.1" + host + } + client := &http.Client{Timeout: 3 * time.Second} + resp, err := client.Get("http://" + host + "/healthz") + if err != nil { + os.Exit(1) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + os.Exit(1) + } +} + func sseEvent(w http.ResponseWriter, flusher http.Flusher, event agentEvent) { data, _ := json.Marshal(event) fmt.Fprintf(w, "data: %s\n\n", data)