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 a codegen flag (HasWebhooks). 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 adds no interceptors of its own: RegisterService threads the caller's opts straight through to Register.
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) 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).
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{...}`. |