mirror of
https://github.com/hanzoai/gateway.git
synced 2026-10-30 15:04:44 +00:00
* feat(middleware): own JWT validation + identity-strip + trust helper Moves Auth + StripIdentityHeaders from zip/middleware to gateway/middleware. Adds gateway/trust.go exporting AssertGatewayMinted for downstream subsystems to verify the request flowed through gateway. The unified cloud binary's pipeline now reads: zip generic middleware (Recover, Logger, RequestID, Telemetry, ...) gateway.middleware.StripIdentityHeaders gateway.middleware.Auth(verifier) ... downstream subsystem handlers ... * feat(gateway): HIP-0110 ZAP wire envelopes + BuildApp entry * feat(gateway): cmd/gateway HIP-0110 edge-process main * refactor(gateway): tighten HIP-0110 surface — 588 LOC across 4 files * build(gateway): fix module pins, MsgType uint16 overflow, drop legacy jaeger - go.mod: hanzoai/cloud v0.0.0 → v0.1.0, hanzoai/zip v0.0.0 → v0.1.1 (zip v0.1.1 carries the json/v2-at-edge work merged in zip#3; cloud v0.1.0 carries the types-leaf + clients refactor). - zap_wire.go: MsgTypeForward/Push/Subscribe were 0x1010/0x1020/0x1030 (uint16) but zap.FinishWithFlags expects the type in the upper byte (type << 8). Anything > 0xFF overflows uint16. Repacked into 0x80/0x81/0x82 — still upper-half of the byte (no collision with base's 100-103 IDs). - executor.go: dropped krakend-opencensus/v2/exporter/jaeger blank import. The exporter's Thrift API is incompatible with current jaeger-client-go; inter-service tracing is migrating to OTel (kotel) + ZAP anyway. - cmd/gateway/main.go: c.SendString → c.Fiber().SendString (zip.Ctx exposes Send via the underlying fiber.Ctx; matches the pattern used for binary responses). Verified: go build -mod=mod ./... clean. Binary at /tmp/gateway = 108 MiB. TestStrip+TestAuth+TestSecure all pass. * test(gateway): per-conn memory profile — 8 KiB/conn, 1:1 goroutine Raw-TCP-dial loadgen (not http.Client, which spawns 3 client-side goroutines that contaminate the gateway-side measurement). Spins up zip.App on a local listener, dials N raw TCP conns each holding /hold, samples runtime.MemStats.HeapAlloc delta. Apple M1 Max, Go 1.26, zip on Fiber v3 v3.2.0: 1,000 conns: 8.05 KiB/conn, 1.00 goroutine/conn, 14 MiB heap 8,454 conns: 8.02 KiB/conn, 1.00 goroutine/conn, 72 MiB heap (linear) Discipline: fasthttp gives us 1 goroutine per accepted conn already. The HIP-0110 architecture preserves that — ZAP-forward to cloud is sync from the gateway goroutine, no fan-out. Reverse-push lookups are O(1) on a per-replica sync.Map, no extra goroutines. Extrapolation: 10k conns → ~80 MiB / replica 100k conns → ~800 MiB (1 GiB pod ceiling) 10 replicas behind ingress → 1M concurrent client conns Cloud side has zero per-conn cost — worker pool dispatches N ZAP requests per pod. This is why a stateless gateway LB in front of cloud maximizes compute efficiency: 10-100× fewer cloud replicas than gateway replicas required for the same client population. * deps: bump zip v0.1.1 → v0.2.0; pin cloud at resolvable pseudo-version cloud cannot be tagged yet due to upstream GOPROXY blockers (kms repo >500MB, vfs missing root package at v0.3.1). Pin cloud at the pseudo-version derived from main HEAD so consumers can resolve via proxy.golang.org without local replace dirs.
75 lines
2.2 KiB
Go
75 lines
2.2 KiB
Go
// Copyright © 2026 Hanzo AI. MIT License.
|
|
|
|
// build_app.go is the gateway router entrypoint per HIP-0110.
|
|
// gateway.BuildApp returns the public *zip.App that cmd/gateway/main
|
|
// wires onto :8080. Auth middleware (JWT validate + identity-header
|
|
// mint) is wired by feat/own-auth-middleware in
|
|
// github.com/hanzoai/gateway/middleware and added to this Use chain
|
|
// when that branch lands. The catch-all ZAP forwarder lives in
|
|
// forwarder.go on this branch.
|
|
package gateway
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
luxlog "github.com/luxfi/log"
|
|
zaplib "github.com/luxfi/zap"
|
|
|
|
"github.com/hanzoai/zip"
|
|
"github.com/hanzoai/zip/middleware"
|
|
)
|
|
|
|
// RouterDeps is the set of dependencies BuildApp needs.
|
|
type RouterDeps struct {
|
|
Logger luxlog.Logger
|
|
ZAPNode *zaplib.Node
|
|
CloudAddr string
|
|
BaseAddr string
|
|
}
|
|
|
|
// BuildApp returns the public *zip.App for the gateway's :8080 port.
|
|
func BuildApp(deps RouterDeps) (*zip.App, error) {
|
|
if deps.Logger == nil {
|
|
return nil, errors.New("gateway.BuildApp: nil Logger")
|
|
}
|
|
if deps.ZAPNode == nil {
|
|
return nil, errors.New("gateway.BuildApp: nil ZAPNode")
|
|
}
|
|
app := zip.New(zip.Config{
|
|
Logger: deps.Logger,
|
|
ServerHeader: "hanzoai",
|
|
DisableStartupMessage: true,
|
|
AppName: "gateway",
|
|
})
|
|
app.Use(
|
|
middleware.Recover(),
|
|
middleware.RequestID(),
|
|
middleware.Logger(deps.Logger),
|
|
middleware.CORS(middleware.CORSConfig{
|
|
AllowOrigins: []string{"*"},
|
|
AllowHeaders: []string{"Content-Type", "Authorization", "X-Request-Id"},
|
|
}),
|
|
)
|
|
// Catch-all forwarder placeholder until forwarder.go lands.
|
|
app.All("/*", func(c *zip.Ctx) error {
|
|
return c.JSON(http.StatusServiceUnavailable, map[string]string{
|
|
"error": "gateway forwarder not yet wired",
|
|
"backend": pickBackend(c.Path(), deps),
|
|
})
|
|
})
|
|
deps.Logger.Info("gateway.BuildApp ready",
|
|
"cloud_zap", deps.CloudAddr, "base_zap", deps.BaseAddr)
|
|
return app, nil
|
|
}
|
|
|
|
// pickBackend chooses the ZAP peer for path. /v1/base/* → base;
|
|
// everything else → cloud (which routes to per-HIP-0106 subsystems).
|
|
func pickBackend(path string, deps RouterDeps) string {
|
|
if strings.HasPrefix(path, "/v1/base/") {
|
|
return deps.BaseAddr
|
|
}
|
|
return deps.CloudAddr
|
|
}
|