Extract the shared transport middleware & resilience into go/transit¶
- Authors
- Matt Cockayne, Claude Opus 4.8 (AI drafting assistant)
- Date
- 2026-07-16
- Status
- IMPLEMENTED (2026-07-18) — shipped to main; originally APPROVED (O1–O4 resolved in review 2026-07-16)
Summary¶
go/transit is Phase 3 of the
transport-stack extraction plan — the
shared, transport-neutral middleware layer that every transport client and server sits
on. It bundles two things GTB currently spreads across pkg/http, pkg/grpc, and
internal/:
- Resilience primitives — the transport-neutral circuit breaker
(
internal/circuitbreaker) and rate limiter (internal/ratelimit). - Shared HTTP & gRPC middleware — request/response chaining, logging (with
go/redact), OTel instrumentation, the client-side round-trippers / client interceptors (retry, circuit-break), and the server-side handlers / server interceptors (rate-limit, logging).
Extracting it now (after the Phase 2 foundations tls/authn/observability) is what
lets Phase 4's light clients (go/httpclient, go/grpcclient) and Phase 5's server
stack (go/transport) all sit on one shared middleware module rather than each
re-implementing it.
The layer is already remarkably clean. Measured on the current tree, the candidate
files import no pkg/config, pkg/logger, pkg/authn, or pkg/tls. Their only
non-stdlib dependencies are go/redact (already a module), the gRPC SDK, the OTel
contrib instrumentation, golang.org/x/time/rate, and cockroachdb/errors — plus the
two internal/ primitives, which promote into this module. So transit is close
to a lift-and-shift, with the configuration of the middleware (thresholds, limits)
staying behind in GTB.
Target module¶
- Module path:
gitlab.com/phpboyscout/go/transit(name chosen 2026-07-16 to replace the working titletransportmw). - Package layout (D1): one module with sub-packages
transit/http—http.Handlermiddleware +http.RoundTripperclient middleware (chain, logging, OTel, retry, circuit-break, rate-limit).transit/grpc— unary/stream client interceptors + server interceptors + the OTel stats handler / dial options (chain, logging, OTel, circuit-break, rate-limit).transit/resilience— the transport-neutral circuit-breaker and rate-limiter primitives (D2: kept here, not a separatego/resilience).- Docs:
transit.go.phpboyscout.uk(microsite scope — O3). - Local dir:
~/workspace/phpboyscout/go/transit. - Reference scaffold:
~/workspace/phpboyscout/go/observability(the OTel-carrying sibling — its depfootprint guard shape is the closest precedent).
What moves vs. what stays¶
Moves into go/transit¶
- Resilience primitives:
internal/circuitbreaker/breaker.go,internal/ratelimit/store.go(+ tests) →transit/resilience. Zero GTB imports today. - HTTP middleware (from
pkg/http):chain.go,logging.go(usesgo/redact),otel.go,client_middleware.go,retry.go,circuitbreaker.go,ratelimit.go(+ tests) →transit/http. - gRPC middleware (from
pkg/grpc):chain.go,logging.go,otel.go,circuitbreaker.go,ratelimit.go(+ tests) →transit/grpc.
Stays in GTB¶
- The resilience/middleware configuration.
pkg/http/config_adapter.goandpkg/grpc/config_adapter.goread GTB config for rate-limit and circuit-breaker settings; they stay (they readconfig.Containable). They will configuretransit's primitives from typed settings. - The transport servers and clients themselves —
pkg/http/server.go/client.go, all ofpkg/grpcserver,pkg/gateway— which are Phase ⅘ targets. Until then they remain in GTB and consumetransitfor the middleware they wire. - Server-auth & security-headers (
pkg/http/auth.go,security_headers.go,pkg/grpcauth) — these depend ongo/authnand are server-stack concerns (Phase 5), not shared middleware.
Cut-over shape (O1)¶
Unlike the Phase 2 extractions, this carves a layer out of packages that stay
(pkg/http, pkg/grpc remain in GTB until Phase 5). Two options for how GTB then
consumes it:
- O1a — facade (recommended):
pkg/http/pkg/grpckeep re-exporting the moved middleware symbols (http.NewChain,http.LoggingMiddleware,http.OTelMiddleware, the grpc interceptors, …) as thin aliases overtransit. Zero churn for GTB's ownserver.go/client.go/gatewaywiring and for downstream tools and the docs examples that callhttp.OTelMiddlewareetc. Matches thetlsfacade; the aliases evaporate in Phase 5 when the servers/clients themselves move totransit. - O1b — full repoint: repoint every middleware reference in
pkg/http/pkg/grpc/pkg/gateway(and downstream) totransitdirectly, deleting the symbols from the transport packages. Cleaner break, but churns the transport packages we rewrite in Phase 5 anyway, and breaks downstreamhttp.OTelMiddleware-style call sites now rather than at Phase 5.
Because the transport packages persist through Phase 5 and have external callers, the
facade (O1a) keeps this phase minimal and low-risk; the clean break happens naturally
when the servers/clients extract. (This is the opposite recommendation from
observability, where the only consumer was internal — here there are downstream
callers.)
Repository framework (playbook §3)¶
Bootstrap from the go/observability scaffold, with gRPC + OTel allowed:
depfootprint_test.go: allowgoogle.golang.org/grpc,go.opentelemetry.io/*,golang.org/x/time; forbidgo-tool-base,spf13/viper,spf13/pflag,spf13/cobra,charmbracelet, and the cloud SDKs.go/redactis an allowed sibling-module dependency.- OTel + gRPC versions pinned in lockstep with GTB (and with
go/observability); watch thegoogle.golang.org/genprotosplit ambiguity (pingoogleapis/api+rpc+ grpc, asgo/observabilitydoes). - CI
@v0.22.0;.golangci.yamlv2, local prefixgitlab.com/phpboyscout/go/transit; ≥90% coverage (the middleware suites are already thorough);cockroachdb/errors;*slog.Loggerseam for logging middleware.
Documentation (playbook §4)¶
- Author
transit.go.phpboyscout.uk(scope per O3): overview, a how-to (compose a client round-tripper chain + a server handler chain), and an explanation of the resilience model (circuit-breaker states, rate-limit token bucket) and the client/server middleware split. - Pages custom domain + Cloudflare DNS (DNS-only) +
pages_access_level=public+pages_primary_domain=https://…per the.golearnings. - GTB side: the transport-middleware concept/component pages link out to the microsite and keep the GTB config-cascade + chain-ordering story; migration note; report tick.
Sequencing within the transport stack¶
transit unblocks the rest of the transport plan:
- This spec —
transit(resilience + shared middleware). - Phase 4 —
go/httpclient+go/grpcclient: the light hardened client factories, wiringtransit/http/transit/grpcclient middleware +go/tls. - Phase 5 —
go/transport: thehttpserver +grpcserver +gateway, wiringtransitserver middleware +go/authn+go/observability+go/controls.
Resolved decisions (review 2026-07-16)¶
- O1 — Facade (O1a).
pkg/http/pkg/grpcre-exporttransit's middleware as thin aliases, so GTB's server/client/gateway wiring and downstream callers (which usehttp.OTelMiddleware,http.NewChain, etc.) are unchanged. The aliases evaporate in Phase 5 when the servers/clients themselves move totransit. (Opposite ofobservability's full-repoint, because here the transport packages persist and have external callers.) - O2 — One
transit/resiliencesub-package holding both the circuit breaker and the rate limiter (they are small and always travel together). - O3 — Light microsite at
transit.go.phpboyscout.uk: overview + a compose-a-chain how-to + a resilience-model / client-server-split explanation. Reference → pkg.go.dev. - O4 —
transitowns both halves. It holds the client round-trippers / client interceptors and the server handlers / server interceptors; the Phase 4 client modules (go/httpclient,go/grpcclient) hold only the hardened factory + wiring. The measured client/server file split makes a single shared home clean and keeps the resilience primitives in one place.
Acceptance criteria¶
go/transitbuilds withtransit/http,transit/grpc,transit/resiliencesub-packages;depfootprint_test.gopasses (gRPC/OTel/redact allowed; no go-tool-base/framework/cloud-SDK); ≥90% coverage; race/lint/vet/govulncheck green;v0.1.0published and proxy-resolvable.- GTB consumes
go/transit v0.1.0;internal/circuitbreaker+internal/ratelimitand the moved middleware files are gone from GTB; per O1, either thepkg/http/pkg/grpcfacades re-export it (no churn) or all references repoint;just cigreen (modulo the knowninternal/agenttest). - Docs live per O3; migration note added; extraction-report checklist ticks
transit; the transport-stack plan's Phase 3 is marked done.