HIP-122: zip — The ZAP-Native Application Server Core. Status Active. Hanzo's own standard — read this before implementing against it.
zip is the one Hanzo application server core: a high-performance Go framework (Fiber v3 / fasthttp derived) whose primary transport is ZAP (HIP-0114) and whose secondary transport is HTTP, selected as a value on one verb — app.Listen(":9653", "http://:8080"). Every Hanzo Go service is a thin composition over a zip.App; the unified cloud binary (HIP-0106) is itself the proof: its core is a measured 977 lines (serve.go 260 + subsystems/subsystems.go 312 + build.go 405) hosting 74,561 lines of mounted plugins under clients/ — a 76:1 plugin-to-core ratio. The server is not where the product lives; the server is what the product mounts on.
This HIP specifies the zip contract — App, Ctx, transport-as-value Listen, Mount, Module, typed handlers, identity accessors, and shutdown lifecycle — so that HIP-0106 (the host binary), HIP-0116 (the plugin/VM shapes), and every hanzoai/<repo> service compose on one substrate instead of each re-deciding a framework.
zip serves ONE fiber/fasthttp handler tree on any number of addresses. The transport is selected by the address scheme, never by a method name (transport.go):
app.Listen(":9653") // ZAP — bare address = ZAP (the primary)
app.Listen(":9653", "http://:8080") // ZAP primary + HTTP secondary, one call
app.Listen("http://:8080") // HTTP only (edge/interop shape)
DefaultScheme = "zap": the path of least resistance is ZAP-native.ZAP is the primary wire (TLS 1.3 + post-quantum; the gRPC replacement per HIP-0120); HTTP is the interop/edge secondary.
RegisterTransport(scheme, fn) is the ONLY extension point: a TransportFunc builds a Server{ListenAndServe, Close} for an address. New protocols never change the Listen API — this mirrors net.Listen(network, addr): the network is a value, not a ListenTCP/ListenUDP method explosion.
over every transport. Routes ARE the surface; there is no per-transport wiring. Streaming responses ride ZAP through the same framework path (zap-proto/http v0.2.0+).
| Entry point | Contract | Used by | |---|---|---| | app.Get/Post/...(path, fn) | Sinatra/Express idiom; the primary API | all native Hanzo handlers | | zip.GetIn, Out | typed handlers → OpenAPI 3.1 at /docs AND a free MCP tool projection at /mcp (JSON-RPC 2.0) | typed public surfaces | | app.Group(prefix) | route grouping | /v1/... service prefixes | | app.Mount(prefix, h http.Handler) | registers prefix+"/*" via AdaptNetHTTP; the handler receives the full original path (nothing stripped) | migration of chi/gin/beego/net-http code (HIP-0106 adapters); subsystem mounts that own their canonical prefix | | app.Module(method+path, runtime, path) | mounts a HIP-0105 extension (wasm/goja/pyvm/starlark/native) as a route — one mount API for every guest language | user-supplied and multi-language handlers |
Mount preserving the full path is load-bearing: a subsystem mounted at /v1/iam still sees /v1/iam/... — its routes are identical standalone, embedded, or as a plugin VM (HIP-0116's "one service, three shapes" depends on this).
Adapters (adapt.go: AdaptNetHTTP, AdaptNetHTTPFunc, AdaptNetHTTPMiddleware) are migration tools costing ~5% versus native dispatch, per HIP-0106: new code is written natively against zip; adapted routes are replaced when feasible.
zip.Ctx (ctx.go) carries the gateway-minted identity of HIP-0026 / HIP-0134 as first-class accessors — services never parse headers:
c.Org() // X-Org-Id (gateway-minted from the JWT owner claim)
c.User() // X-User-Id
c.UserEmail() // X-User-Email
c.IsAdmin() // X-User-IsAdmin == "true" (org-scoped; see HIP-0118)
c.RequestID() // X-Request-Id
Plus the one JSON entry point: c.Bind / c.JSON route through zip's internal jsonenc — stdlib encoding/json/v2 under GOEXPERIMENT=jsonv2, stdlib v1 otherwise. No third-party JSON library exists in the stack (HIP-0106 invariant; the measured json/v2 wins and the per-connection budget — 8.02 KiB and 1.00 goroutine per connection, 100k conns per 1 GiB replica — are documented in HIP-0106 "Per-replica capacity budget" and are not restated here).
Logging is luxfi/log via c.Log() — never slog, never uber/zap.
closers.go: Module() and any resource-owning mount register closers on the App; Shutdown() runs them once, first-error-wins. This is the same lifecycle the cloud host drives through RegisterWithShutdown (below) — one teardown discipline from the framework up.
The realized cloud binary demonstrates what "thin composition over zip" means, with measured numbers (non-test Go, hanzoai/cloud at v1.786.x, 2026-07):
| Layer | Files | LOC | |---|---|---:| | Core (serve + registry + subsystem table) | serve.go, subsystems/subsystems.go, build.go | 977 | | Plugins (mounted subsystems) | clients/* | 74,561 |
76:1. The core does exactly four things: build deps, mount registered subsystems in order onto one zip.App, serve the transports, and tear down in reverse order. Everything else — IAM, KMS, commerce, ML, fleet, console BFF, sixty-plus packages — is a plugin registered through the one registry API (build.go:351):
cloud.Register(name string, order int, mount MountFunc)
cloud.RegisterWithShutdown(name string, order int, mount MountFunc, shutdown ShutdownFunc)
A subsystem's init() calls Register; a blank import in cmd/cloud activates it; MountAll mounts onto the shared zip.App. This is the substrate HIP-0116's plugin VMs generalize: an embedded plugin is a mount in-process, a plugin VM is the same mount behind a ZAP hop — the zip.App surface is identical.
Requirement: every Hanzo Go service exposes its business surface as a mount against a zip.App (the HIP-0106 Mount(app, deps) contract). A service that spins up its own parallel framework — or adds a second server core — is nonconformant.
Why a framework HIP at all. Because the 76:1 measurement is the architecture. When the server core is one small, shared, boring layer, every product decision becomes a plugin decision — independently mountable, testable, and (per HIP-0116) independently packageable. The alternative — each service owning its own server — is how the pre-HIP-0106 estate accumulated N frameworks and N half-consistent identity parsers.
Why ZAP-primary rather than HTTP-primary with ZAP bolted on. Hanzo's internal traffic is ZAP (HIP-0106, HIP-0114, HIP-0120); HTTP exists for the external edge and interop. A server core whose default is the internal wire makes the correct thing the effortless thing — app.Listen(":9653") is conformant by default. The reverse default would make every service opt in to its own platform's transport.
Why transport-as-value. Decomplecting "what the handler does" from "how bytes arrive" is the same move HIP-0116 makes for packaging ("what the service does" vs "what process it lives in"). Values, not method explosions: one Listen, schemes as data, RegisterTransport as the single seam.
Why no escape hatches. zip deliberately has no .Fast() raw-mode bypass and no per-runtime mount APIs (app.ModuleWasm does not exist). Escape hatches are how second ways in; the profiled hot path (HIP-0106's measured budget) shows the framework does not need one.
Orthogonality. One HIP per layer:
three-protocol rule. zip terminates these; it does not define them.
Module, lifecycle.
policy, fail-closed embeds (its registry rides this HIP's App).
Module.Everything specified here is shipped: zip v1.2.1 (github.com/zap-proto/zip), one-verb Listen with ZAP default, streaming-over-ZAP, Mount/Module/typed handlers/MCP projection, and the 977-line cloud host consuming it in production. The one naming residue is transitional: hanzoai/zip v0.2.0 still appears as an indirect dependency in older go.mod graphs (e.g. gateway) until those repos rebase onto zap-proto/zip — same code lineage, one module path forward.
identity Ctx exposes)
behind Module)
table, adapters, measured capacity budget)
protocol set)
~/work/zap/zip — reference implementation (transport.go, adapt.go, ctx.go, closers.go, mcp.go, typed.go)
hanzoai/cloud — serve.go, subsystems/subsystems.go, build.go (the 977-line host; Register at build.go:351)
Copyright and related rights waived via CC0.