* wipe upstream zeekay/zip — rebuilding on Fiber v3
* core: zip on Fiber v3 — App, Ctx, Router, typed handlers, OpenAPI
zip wraps *fiber.App. Sinatra/Express idiom (app.Get/Post/...).
Generic zip.Get[I,O], zip.Post[I,O] etc. reflection-build OpenAPI 3.1
spec served at /.well-known/openapi.json + Swagger UI at /docs.
Ctx exposes Hanzo identity (Org/User/UserEmail/IsAdmin) from gateway
X-* headers per HIP-0026. luxfi/log per-request logger via c.Log().
Module(method+path, runtime, modulePath) mounts a HIP-0105 extension
as a Fiber route. Loader is duck-typed via zip/runtime.Loader so zip
does not pull hanzoai/base as a transitive dep — service binary
constructs *extruntime.Loader and injects via zip.Config.Loader.
go.mod: go 1.26.3 (forced by luxfi/log v1.4.3 floor; brief said
1.23.6 but luxfi canonical requires 1.26.x). Fiber v3.2.0, no major
bumps planned.
* middleware: Recover, RequestID, Logger, Timeout, MaxBody, CORS, Auth, RateLimit, Telemetry
Recover catches handler panics → luxfi/log + 500 JSON.
RequestID forwards X-Request-Id (or mints 16-byte hex).
Logger wraps each request with method/path/status/dur, enriches the
per-request luxfi/log Logger via c.SetLog().
Auth trusts gateway-minted X-* headers first; falls back to bearer
verification via injected AuthVerifier.
StripIdentityHeaders for non-gateway deployments.
RateLimit is per-org (or per-IP fallback) in-memory token bucket.
Telemetry plumbs to any O11ySink.
* adapters + wsx + zaprpc
AdaptNetHTTP/AdaptNetHTTPFunc/AdaptNetHTTPMiddleware via Fiber's
adaptor pkg. app.Mount(prefix, http.Handler) for legacy chi/gin/beego
migration. Each adapter doc'd as MIGRATION TOOL — costs ~5% vs native
Fiber dispatch.
wsx is fasthttp/websocket wrapped — wsx.Upgrade(fn) returns a
zip.Handler. Conn is *websocket.Conn from fasthttp/websocket; text/
binary/close/ping/pong message constants re-exported.
zaprpc ships Service + Registry + Dispatch. app.ZAPRegistry() returns
the per-App registry. app.ZAPListen(addr) reserves the port and logs
but the on-the-wire dispatcher is stubbed — full integration in a
follow-up PR pending zapc server type stabilization.
* examples + core tests
examples/
hello/ — minimal Sinatra-style API
zap-typed/ — generic typed handler + auto-OpenAPI + Swagger
subsystem-mount/ — HIP-0106 Mount(*App, deps) idiom
module-routes/ — app.Module() over a stub zip/runtime.Loader
websocket/ — wsx.Upgrade echo server
sse-streaming/ — c.SendStreamWriter Server-Sent Events
migrate-from-gin/ — gin→zip mechanical port
migrate-from-chi/ — chi→zip via AdaptNetHTTP
migrate-from-beego/— beego→zip via AdaptNetHTTP
Verified live: hello binary serves /hello + /health 200s; zap-typed
serves /v1/validate + /.well-known/openapi.json with reflection-
derived schemas. Tests pass: 5 cases (basic routing, HTTPError, typed
validation, groups, identity headers).
* docs: README, HANZO_CHANGES, MIGRATION, MIDDLEWARE, MODULE_HANDLERS
README pitches zip as canonical Hanzo Go framework on Fiber v3.
HANZO_CHANGES documents the diff from upstream zeekay/zip — every
file replaced; only the name kept.
MIGRATION covers gin/chi/beego/net-http with per-route recipes.
MIDDLEWARE covers Recover/RequestID/Logger/Auth/etc.
MODULE_HANDLERS covers app.Module() over HIP-0105 with the wire
envelope and per-runtime guidance.
* ctx.Bind validates via reflection tags; tighten AuthVerifier interface
c.Bind/BindQuery/BindURI now run validate() after parse and return
*HTTPError(400) directly — handler code is one line:
if err := c.Bind(&v); err != nil { return err }
AuthVerifier.Verify now takes context.Context (was an empty
interface{}). middleware import adjusted.
* module-routes: default Content-Type to application/json; add tests
Modules that don't set a Content-Type in their response envelope get
application/json; charset=utf-8 by default — the wire format is JSON
both directions.
Tests cover: happy path (POST /v1/eval echoed back), runtime mismatch
between caller and manifest, AllowedRuntimes refusal, nil Loader.
3.4 KiB
Migration guide
zip ships drop-in adapters for chi, gin, beego, and stdlib net/http.
Each adapter is documented as a migration tool — they exist so a
service can roll onto zip incrementally. Adapter dispatch costs ~5%
versus native Fiber routing; replace adapted routes with native zip
handlers when feasible.
From gin
gin handlers take *gin.Context; zip handlers take *zip.Ctx. The
shapes are deliberately close.
// gin
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
c.JSON(200, gin.H{"id": c.Param("id")})
})
// zip
app := zip.New(zip.Config{})
app.Get("/users/:id", func(c *zip.Ctx) error {
return c.JSON(200, map[string]string{"id": c.Param("id")})
})
Common mappings:
| gin | zip |
|---|---|
gin.H{...} |
map[string]any{...} or a typed struct |
c.Param("id") |
c.Param("id") |
c.Query("q") |
c.Query("q") |
c.GetHeader("X-Foo") |
c.Header("X-Foo") |
c.BindJSON(&v) |
c.Bind(&v) |
c.AbortWithStatusJSON(400, ...) |
return zip.ErrBadRequest("...") |
gin.Recovery() |
middleware.Recover() |
gin.Logger() |
middleware.Logger(app.Logger()) |
For routes that are too complex to port today, mount the gin engine as a legacy adapter:
gin := buildExistingGinApp()
app.Mount("/legacy/gin", gin)
From chi
chi routers implement http.Handler, so zip.AdaptNetHTTP works
unchanged.
chiRouter := chi.NewRouter()
chiRouter.Get("/users", listUsers)
app := zip.New(zip.Config{})
app.Mount("/legacy/chi", chiRouter)
Common mappings:
| chi | zip |
|---|---|
chi.NewRouter() |
zip.New(zip.Config{}) |
r.Route("/v1", fn) |
app.Route("/v1", fn) |
r.Group(fn) |
app.Group("/", h1, h2) |
chi.URLParam(r, "id") |
c.Param("id") |
| middleware (func(http.Handler) http.Handler) | middleware.AdaptNetHTTPMiddleware(mw) |
From beego
beego *web.HttpServer exposes its handler chain via BeeApp.Handlers,
which is an http.Handler. Mount it via app.Mount.
import "github.com/beego/beego/v2/server/web"
beeApp := web.NewHttpServer()
// ... existing beego config
app := zip.New(zip.Config{})
app.Mount("/legacy/iam", beeApp.Handlers)
Notes:
- beego's auto-routing magic (controllers reflected from struct names) has no zip equivalent — port to native zip handlers when feasible.
- beego's
app.confand beego-specific filters do not carry over; re-implement filters as zip middleware.
From net/http
The zero-config path: any http.Handler works.
existing := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
})
app.Mount("/legacy", existing)
For middleware that takes func(http.Handler) http.Handler:
mw := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Custom", "1")
next.ServeHTTP(w, r)
})
}
app.Use(zip.AdaptNetHTTPMiddleware(mw))
Replacing adapters with native handlers
Once a route's traffic warrants it, rewrite from Mount to native zip:
// before
app.Mount("/legacy/chi", chiRouter)
// after (per-route migration)
v1 := app.Group("/v1")
v1.Get("/users", listUsers) // was chiRouter.Get("/users", ...)
v1.Post("/users", createUser)
The ~5% perf delta compounds at high RPS and the adapter chain is a sharp edge for debugging — native handlers see fiber.Ctx directly, adapters bounce through fasthttp ↔ net/http translation.