Documentation
¶
Overview ¶
Package mountkit mounts a forge Connect service onto an http.ServeMux.
Why a library ¶
Earlier forge versions emitted a per-service Mount closure in the generated inventory: each closure called svc.Register(mux, opts...), then conditionally svc.RegisterHTTP(...) and svc.RegisterWebhookRoutes(...) — the same handful of lines repeated once per service, gated by codegen flags (HasWebhooks, HasAuthorizer). That body is uniform across every service and every project, so it belongs in one tested library function rather than in N generated closures.
mountkit.RegisterService replaces that closure. It is interface-driven: it asks the service value which capabilities it has (required Connect registration, optional plain-HTTP routes, optional webhook routes) by type-asserting small capability interfaces, and invokes whatever is present. No per-service codegen, no hard dependency on any consumer's concrete service type.
What it does NOT do ¶
mountkit does not add per-service authorization. In the registry-DI redesign, descriptor-driven authz becomes a single chain-level interceptor applied once when the shared opts are built, ABOVE the per-service mount. RegisterService threads the caller's opts straight through to Register and never appends its own interceptors.
mountkit does not build the HTTP middleware stack the optional registrars consume — that stack (recovery / logging / audit, e.g. forge/pkg/middleware.HTTPStack) is wired from the project's middleware package, so the caller constructs it and passes it via WithHTTPStack. When no stack is supplied, optional HTTP/webhook routes are mounted with an identity (pass-through) stack.
The boundary with serverkit ¶
serverkit takes an already-composed http.Handler and owns the runtime lifecycle; it knows nothing about service names or mounting. mountkit sits one level ABOVE serverkit, in the mux-composition step the generated cmd-server shim performs before handing the finished handler to serverkit.Run. They are deliberately separate packages so neither has to import the other's concerns.
Usage in generated code ¶
The generated cmd-server shim (or the data-only inventory's Mount closure, during migration) calls RegisterService once per selected service:
stack := fmw.HTTPStack(logger, middleware.ClaimsFromContext)
for _, svc := range selectedServices {
mountkit.RegisterService(mux, svc, opts, mountkit.WithHTTPStack(stack))
}
opts is the shared []connect.HandlerOption (observe interceptors, read/send limits, and — once it lands — the single descriptor-driven authz interceptor) already composed once for the whole process.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterService ¶
func RegisterService(mux *http.ServeMux, svc any, opts []connect.HandlerOption, mountOpts ...Option)
RegisterService mounts a single forge Connect service onto mux.
svc MUST implement Registrar; RegisterService asserts it and panics with a clear message if absent — handing a value without Register to the mount loop is a programming error caught at boot, not a recoverable runtime condition. It then calls Register(mux, opts...), threading the caller's shared opts through UNCHANGED (mountkit adds no interceptors of its own — see the package doc on authz).
After the required Connect registration, RegisterService type-asserts each OPTIONAL capability (HTTPRegistrar, WebhookRegistrar) and invokes any the value implements, passing the stack from WithHTTPStack (or an identity stack when none was supplied). A service implementing only Registrar mounts exactly its Connect handler and nothing else.
Types ¶
type HTTPRegistrar ¶
type HTTPRegistrar interface {
RegisterHTTP(mux *http.ServeMux, stack func(http.Handler) http.Handler)
}
HTTPRegistrar is an OPTIONAL capability for services that expose plain (non-Connect) HTTP routes — OAuth callbacks, hand-rolled REST, webhook receivers wired by hand. The signature mirrors the generated service.go's RegisterHTTP method exactly:
RegisterHTTP(mux *http.ServeMux, stack func(http.Handler) http.Handler)
stack is the project's HTTP middleware (recovery / logging / audit, e.g. forge/pkg/middleware.HTTPStack) that the route should be wrapped in. mountkit does not build the stack — the caller supplies it via WithHTTPStack; when omitted, an identity (pass-through) stack is used so the route still mounts.
Auth is intentionally NOT part of this stack: plain-HTTP routes commonly authenticate differently from Connect RPCs (webhook-signature vs JWT), so per-route auth stays the service's responsibility.
type Option ¶
type Option func(*options)
Option configures a RegisterService call.
func WithHTTPStack ¶
WithHTTPStack supplies the HTTP middleware stack passed to the optional HTTPRegistrar / WebhookRegistrar capabilities. The same stack feeds both. A nil stack is treated as absent (identity pass-through).
When a service implements neither optional capability, the stack is never consulted, so callers that mount only pure-Connect services may omit it.
type Registrar ¶
type Registrar interface {
// Register mounts the service's Connect handler on mux, applying the
// supplied handler options (the shared interceptor chain + payload
// limits). RegisterService passes the caller's opts through unchanged.
Register(mux *http.ServeMux, opts ...connect.HandlerOption)
}
Registrar is the REQUIRED capability every mountable forge service implements: it registers its Connect handler on the mux. The generated service.go's Register method (which calls the protoc-gen-connect-go New<Svc>Handler and mux.Handle's the result) satisfies it directly.
A service value that does not implement Registrar cannot be mounted — RegisterService panics, because that is a boot-time programming error (a non-service was handed to the mount loop), not a runtime condition to recover from.
type WebhookRegistrar ¶
type WebhookRegistrar interface {
RegisterWebhookRoutes(mux *http.ServeMux, stack func(http.Handler) http.Handler)
}
WebhookRegistrar is an OPTIONAL capability for services that own forge.yaml-declared webhook routes. forge generates the RegisterWebhookRoutes method (in webhook_routes_gen.go) for exactly those services; its signature is identical to RegisterHTTP:
RegisterWebhookRoutes(mux *http.ServeMux, stack func(http.Handler) http.Handler)
It is kept as a SEPARATE capability from HTTPRegistrar (rather than folded into it) because the two are generated independently — a service may declare webhooks without overriding RegisterHTTP, or vice versa — and RegisterService invokes whichever subset the value implements. The same caller-supplied stack feeds both.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package inventory owns the DATA-ONLY service descriptor type and reader helpers behind the generated internal/app/inventory_gen.go's `var Inventory = []inventory.ComponentInfo{...}`.
|
Package inventory owns the DATA-ONLY service descriptor type and reader helpers behind the generated internal/app/inventory_gen.go's `var Inventory = []inventory.ComponentInfo{...}`. |