Files
zeekayandHanzo Dev 613ca999c0 fix(fleet): serve HTTP, not ZAP
zip.Listen picks its transport from the address scheme, and a bare ":8000"
means ZAP. The dashboard and the kubelet both speak HTTP, so the readiness
probe's "GET " arrived as a ZAP frame header — frame size 1195725856, which is
the ASCII — and the pod never went Ready. The address now carries its scheme.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 19:36:37 -07:00

77 lines
2.2 KiB
Go

// fleet serves the read-only luxd fleet overview the lux.cloud dashboard polls.
//
// It runs inside the cluster that hosts the fleet, reads every luxd pod over
// that node's own /v1 HTTP RPC, and answers one document:
//
// GET /health liveness
// GET /v1/fleets/lux/overview the fleet, optionally ?networks=a,b
//
// The response is public chain state — node IDs, versions, heights, peer
// counts — so it carries no auth and no secrets: the lux.cloud landing page
// renders it for signed-out visitors.
package main
import (
"os"
"strings"
luxlog "github.com/luxfi/log"
"github.com/zap-proto/zip"
"github.com/zap-proto/zip/middleware"
"github.com/hanzoai/bootnode/fleet"
)
func main() {
log := luxlog.New("module", "fleet")
cluster, err := fleet.NewInCluster()
if err != nil {
log.Error("cluster unreachable", "err", err)
os.Exit(1)
}
app := newApp(log, &fleet.Collector{Cluster: cluster, Prober: fleet.NewRPC(), Markets: fleet.NewGraph()})
// The scheme picks the transport: a bare address would serve ZAP, and the
// dashboard and the kubelet both speak HTTP.
addr := os.Getenv("FLEET_ADDR")
if addr == "" {
addr = "http://:8000"
}
log.Info("lux fleet listening", "addr", addr)
if err := app.Listen(addr); err != nil {
log.Error("serve failed", "err", err)
os.Exit(1)
}
}
// newApp is the whole HTTP surface: two routes, one of them a Group.
func newApp(log luxlog.Logger, c *fleet.Collector) *zip.App {
app := zip.New(zip.Config{Logger: log, AppName: "lux-fleet"})
app.Use(
middleware.Recover(),
middleware.RequestID(),
middleware.CORS(middleware.CORSConfig{AllowMethods: []string{"GET", "OPTIONS"}}),
)
app.Get("/health", func(ctx *zip.Ctx) error {
return ctx.JSON(200, map[string]any{"status": "ok", "k8s_enrichment": fleet.Available()})
})
app.Group("/v1/fleets/lux").Get("/overview", func(ctx *zip.Ctx) error {
return ctx.JSON(200, c.Overview(ctx.Context(), networks(ctx.Query("networks"))))
})
return app
}
// networks parses the comma-separated ?networks= filter; empty means all.
func networks(q string) []string {
var out []string
for _, n := range strings.Split(q, ",") {
if n = strings.TrimSpace(n); n != "" {
out = append(out, n)
}
}
return out
}