mirror of
https://github.com/zap-proto/zip.git
synced 2026-11-02 16:37:53 +00:00
HIP-0106's plugin contract, in the framework rather than in 116 repositories. Declaration projects the LIVE ROUTER — every method+pattern, complete by construction, so a route a plugin serves and does not publish is still declared. That is the analytics-ingestion outage class: the published paths were listed, the four ingestion doors were not, and every product beacon in the fleet answered 405. Declaration/Route/(*App).Declaration what a host discovers /.well-known/zip/plugin.json the same value off a running child (*App).Described `<bin> openapi <f>` / `<bin> declare <f>` Tenant the tenancy read, with the real rule Delegate a continuation for another org Ask dial a peer by name, invoke one op (*App).Peer peer ops on their own app, own socket (*App).Ops /healthz + /readyz + /metrics Config.Eager the one fact a router cannot show App.claim a second claim of a prefix is an ERROR zip/runtime -> zip/js a language runtime is an adaptor Four things the reference plugin falsified while being built: - Describe was already taken. zip.Describe(methodPath, Doc) is the doc registry generated code calls. The projection writer is (*App).Described, one call, because argv is read in one place and a main cannot forget to forward it. - zip's control plane is NOT all under /.well-known/. /docs and the MCP path are not, so a prefix filter let a child declare them — paths outside /v1/ that every plugin serves and whose first claimant takes the HOST's document and agent door. App.control now registers and records in one call, so leaking one is impossible rather than unlikely. - OPS_PORT appeared in no Go file in the fleet. HIP-0119 named the paths and nobody implemented them, so conformance meant 116 repos hand-rolling three routes and a listener. It is one sibling app now, brought up by Listen. - There is deliberately NO default ops port. A default made every second app in a process die on "address already in use" — a plugin has two apps in it and a test process has many. The deployment states OPS_PORT; a host states none on a child, so a child correctly owns none. Load's duplicate claim used to return nil while the first registration kept answering: the losing plugin's whole surface served another plugin's 404 and nothing said so. It is now an error naming both claimants, all-or-nothing across a plugin's prefixes, taken before anything is spawned. a.ops was the op registry and is now a.registry — the ONE registry the OpenAPI document, the MCP tool, the CLI command and the plane op are all projected from. a.ops is the ops sibling. Co-authored-by: Hanzo Dev <[email protected]>
5.0 KiB
5.0 KiB
HANZO_CHANGES.md — diff from upstream zeekay/zip
zeekay/zip was a tiny net/http wrapper (~250 LOC) experimenting with
Sinatra-style routing in Go circa 2014. Hanzo adopted the name and
rebuilt the framework from scratch on Fiber v3 / fasthttp for the
canonical Hanzo Go web framework.
What's gone
Every file from upstream has been deleted. We kept the name.
handlers.go— old net/http handler registry. Replaced byzip.App.Get/Post/...plus internalrouterAdapterover fiber.Router.request.go,response.go— oldReq/Restypes. Replaced by*zip.Ctxwrappingfiber.Ctx.websocket.go— old gorilla/websocket adapter. Replaced by packagewsxoverfasthttp/websocket.examples/{hello,json,websocket}— old examples. Replaced by 9 new examples covering Sinatra-style, typed handlers, OpenAPI, module routes, websocket, SSE, and three migration paths.- Module path:
zeekay.io/zip→github.com/hanzoai/zip.
What's new
| Area | Implementation |
|---|---|
| Core | *fiber.App under the hood; zip.App is the public type. |
| Ctx | fiber.Ctx interface wrapped + Org/User/UserEmail/IsAdmin/RequestID/Host accessors. |
| Typed handlers | Generic zip.Get[In, Out](on, path, fn) etc. with reflection-built OpenAPI 3.1. on is the App, any Group of it, or app.With(mw…). |
| Validation | ~120 LOC reflection: required, min/max, minlen/maxlen struct tags. |
| OpenAPI | Auto-generated at /.well-known/openapi.json; Swagger UI at /docs. |
| Extension routes | app.Module(method+path, runtime, dir) — HIP-0105 surface. Loader is duck-typed (no hanzoai/base dep). |
| Middleware | Recover, Logger, RequestID, Timeout, MaxBody, CORS, RateLimit, Telemetry, ProductionHeaders. Auth + StripIdentityHeaders moved to hanzoai/gateway/middleware (HIP-0106). |
| Production headers | ProductionHeaders(cfg) stamps the Stripe/CF/GitHub-grade posture on every response (success/error/404): Server = white-label brand by Host (injected Brand func(host) string, never the framework name), X-Api-Version (brand-neutral), X-Content-Type-Options: nosniff, and Strict-Transport-Security when HSTS. X-Request-Id stays owned by RequestID. Never emits X-Powered-By or any framework/version string. |
| Adapters | AdaptNetHTTP / AdaptNetHTTPMiddleware; front a foreign subtree via app.All(prefix+"/*", zip.AdaptNetHTTP(h)). A bare func adapts as AdaptNetHTTP(http.HandlerFunc(fn)) — http.HandlerFunc IS an http.Handler, so there is no separate func adapter. |
| WebSocket | wsx.Upgrade(fn) over fasthttp/websocket. |
| Streaming | c.SendStream(reader) + c.SendStreamWriter(fn). |
| Transport | ONE verb app.Listen(addrs...); the address scheme selects the transport (:9653=ZAP default, http://:8080=HTTP, any RegisterTransport'd proto). app.Listen(":9653", "http://:8080") serves both from one call. Routes ARE the surface over every transport. |
| Free MCP | Typed handlers auto-project to a Model Context Protocol tool surface at /mcp (JSON-RPC 2.0: initialize/tools/list/tools/call). Same op registry as OpenAPI — one schema, five projections (REST·OpenAPI·MCP·CLI·call plane) — served over every transport, so ZAP-native MCP is free. Config.MCP.Disabled to suppress. |
Dependencies
github.com/gofiber/fiber/v3 v3.2.0— Fiber v3.github.com/valyala/fasthttp v1.70.0— transport.github.com/fasthttp/websocket v1.5.12— WebSocket.github.com/luxfi/log v1.4.3— canonical Hanzo logging. Forces Go 1.26.3 floor.
No uber-go/zap, no log/slog, no chi/gin/echo deps. Adapters use
Fiber's own middleware/adaptor package which is part of the v3 core.
Policy adherence
- luxfi/log everywhere — zero
zaporslogimports. - No
.capnpanywhere — ZAP only on the RPC plane. - No major version bumps — all deps pinned to specific patches on existing major lines.
- luxfi canonical packages preferred — log was already on luxfi; no other Hanzo-canonical alternative existed for the Fiber surface.
- Go 1.26.3 floor — bumped from the brief's 1.23.6 only because luxfi/log canonical (the mandatory dep) requires it. Every other dep is on its current patch.
- HIP-0026 identity headers —
c.Org/User/UserEmail/IsAdminmap directly to gateway-mintedX-Org-Id / X-User-Id / X-User-Email / X-User-IsAdmin. - HIP-0105 extension contract — duck-typed via
zip.Loaderat the root, so the framework stays decoupled fromhanzoai/baseAND from a JS toolchain: the goja/esbuild implementation is the separate opt-in importzip/js. - HIP-0106 Mount() contract —
examples/subsystem-mountis the reference; the binary composes N subsystems via the sameMount(app, deps) errorsignature commerce/checkout uses today.
Backwards compatibility
None. zeekay/zip and hanzoai/zip share only the name. The module path
changed (zeekay.io/zip → github.com/hanzoai/zip) so go-modules
treats them as distinct packages — no transitive collision is possible.