Documentation
¶
Overview ¶
Package transports is the Harbor Protocol wire-transport seam — the binding of RFC §5.4's resolved transport choice (SSE for the event stream + REST/JSON for the control surface) onto net/http.
The seam (CLAUDE.md §3, §4.4) ¶
Each wire transport is its own sub-package:
- internal/protocol/transports/control — REST/JSON over the transport-agnostic protocol.ControlSurface.
- internal/protocol/transports/stream — SSE over the events.EventBus.
This package composes them: NewMux wires both handlers into one *http.ServeMux a future server (the `harbor dev` subcommand) mounts. The layout is the §4.4-style seam read for transports rather than drivers: RFC §5.4 explicitly leaves WebSocket as an additive alternate transport. Adding it is a third sub-package (internal/protocol/transports/websocket) plus one more mux entry here — neither `control` nor `stream` is reshaped, and no caller outside this package changes. There is NO driver-registry / factory ceremony: the transport set is small, closed, and mounted in code at boot, not resolved by name from config — the same posture An earlier phase took for the ControlSurface.
Why no http.Server here ¶
Harbor ships the transport HANDLERS, not the server that listens. `harbor dev` owns the net.Listener, the graceful-shutdown lifecycle, and the /healthz + /readyz endpoints; it calls NewMux and serves the result. Keeping the listen/shutdown lifecycle out of this package means the transports are exercised end-to-end today via httptest (the package + integration tests) without waiting on the server phase — the same decoupling used to stay testable ahead of its wire binding.
Concurrent reuse ¶
The *http.ServeMux NewMux returns is immutable after construction and both mounted handlers are themselves safe for concurrent reuse compiled artifacts (control.Handler, stream.Handler). One mux serves N concurrent requests safely; concurrent_test.go pins it under -race.
Index ¶
- Variables
- func NewMux(cs *protocol.ControlSurface, bus events.EventBus, opts ...Option) (*http.ServeMux, error)
- type Option
- func WithAgentConfigService(s *agentcfgprotocol.Service) Option
- func WithAgentsService(s *agentsprotocol.Service) Option
- func WithAggregateClock(c events.AggregatorClock) Option
- func WithAppsSurface(s control.AppsSurface) Option
- func WithArtifactsSurface(s control.ArtifactsSurface) Option
- func WithAuthSurface(surface *auth.RotateSurface) Option
- func WithEventsList(arts artifacts.ArtifactStore) Option
- func WithFlows(surface *flowprotocol.Surface) Option
- func WithGovernanceKeyRotate(s *governanceprotocol.KeyRotateService) Option
- func WithGovernancePostureWrite(s *governanceprotocol.PostureWriteService) Option
- func WithGovernanceService(s *governanceprotocol.Service) Option
- func WithKeepalive(d time.Duration) Option
- func WithLogger(l *slog.Logger) Option
- func WithMCPSurface(s control.MCPSurface) Option
- func WithMemory(store memory.MemoryStore, driverName string) Option
- func WithPauseList(coord pauseresume.Coordinator, store artifacts.ArtifactStore, ...) Option
- func WithPostureSurface(s control.PostureSurface) Option
- func WithRedactor(r audit.Redactor) Option
- func WithRunsService(s *runsprotocol.Service) Option
- func WithSearch(s control.SearchSurface) Option
- func WithSessionsService(s *sessionsprotocol.Service) Option
- func WithStateHistory(bus events.EventBus, arts artifacts.ArtifactStore) Option
- func WithTasksService(s *tasksprotocol.Service) Option
- func WithToolsService(s *toolsprotocol.Service) Option
- func WithValidator(v auth.Validator) Option
- func WithoutValidator() Option
Constants ¶
This section is empty.
Variables ¶
var ErrMisconfigured = errors.New("transports: NewMux missing a mandatory dependency")
ErrMisconfigured — NewMux was called with a nil ControlSurface or a nil EventBus. Both are mandatory: the former feeds the REST control transport, the latter feeds the SSE event transport. Fails closed (CLAUDE.md §5) rather than mounting a half-built mux.
Functions ¶
func NewMux ¶
func NewMux(cs *protocol.ControlSurface, bus events.EventBus, opts ...Option) (*http.ServeMux, error)
NewMux composes the Protocol wire transports into a single *http.ServeMux:
- POST /v1/control/{method} — the REST/JSON control surface.
- GET /v1/events — the SSE event stream.
All three configuration choices are mandatory:
- a non-nil ControlSurface,
- a non-nil EventBus,
- and EITHER `WithValidator(v)` (the production posture — JWT bearer auth at the edge) OR `WithoutValidator()` (the explicit, test-only escape hatch for the trust-based posture).
A missing dependency — including the auth choice — fails loud with ErrMisconfigured rather than mounting a half-built mux or an unauthenticated production surface (CLAUDE.md §13 "Test stubs as production defaults on operator-facing seams"; PR #91).
The returned mux is immutable after construction and safe to share across N concurrent requests — a future server (`harbor dev`) mounts it and owns the listen / shutdown lifecycle.
Types ¶
type Option ¶
type Option func(*muxConfig)
Option configures NewMux.
func WithAgentConfigService ¶ added in v1.5.0
func WithAgentConfigService(s *agentcfgprotocol.Service) Option
WithAgentConfigService wires the admin-scoped agent-config control-plane handler into NewMux — the `POST /v1/agent_config/*` routes (the versioned desired-state registry + skills control).
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied (or supplied a nil service), the `/v1/agent_config/*` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport — the handler then gates on the verified `auth.ScopeAdmin` claim (the agent-config authorization model). A nil service is treated as "WithAgentConfigService not supplied".
func WithAgentsService ¶
func WithAgentsService(s *agentsprotocol.Service) Option
WithAgentsService wires the `agents.*` handler into NewMux — the eight Console Agents-page methods (`agents.list` / `agents.get` / `agents.tools` / `agents.memory` / `agents.governance` / `agents.skills` / `agents.permissions` / `agents.metrics`).
The service is OPTIONAL so existing call-sites compile unchanged. When unsupplied, the `POST /v1/agents/{method}` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev`) supplies it so the Console Agents page has a live surface. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport.
All eight `agents.*` methods are READ-ONLY projections of the Agent Registry — the five agent-control verbs (Pause / Drain / Restart / Force-Stop / Deregister) are the EXISTING shipped `registry.*` control verbs, not new methods (CLAUDE.md §13). A nil service is treated as "WithAgentsService not supplied".
func WithAggregateClock ¶
func WithAggregateClock(c events.AggregatorClock) Option
WithAggregateClock injects a deterministic clock into the events.aggregate handler's underlying *events.Aggregator. Production callers do not use this; the default real clock (UTC) is correct. Tests that exercise the aggregate path with backdated events use this to anchor the aggregator's "now" deterministically — the same posture WithKeepalive takes for the SSE keepalive interval.
func WithAppsSurface ¶ added in v1.4.0
func WithAppsSurface(s control.AppsSurface) Option
WithAppsSurface wires the MCP Apps dispatcher into the control transport. When supplied, the control handler routes the two MCP Apps methods (`mcp.servers.read_resource` / `mcp.apps.call_tool`) to the Apps surface instead of falling through to the task-control ControlSurface.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied, the control transport rejects MCP Apps calls with CodeUnknownMethod (HTTP 404) — the 404 → SKIP path the smoke script relies on. Production wiring (`harbor dev`) supplies it so the Console can render MCP Apps. A nil surface is treated as "WithAppsSurface not supplied".
func WithArtifactsSurface ¶
func WithArtifactsSurface(s control.ArtifactsSurface) Option
WithArtifactsSurface wires the artifacts dispatcher into the control transport. When supplied, the control handler routes the three artifacts methods — `artifacts.list`, `artifacts.put`, `artifacts.get_ref` — to the artifacts surface instead of falling through to the task-control ControlSurface.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied, the control transport rejects artifacts calls with CodeUnknownMethod (HTTP 404) — the 404 → SKIP path the smoke script relies on. Production wiring (`harbor dev`) supplies it so the Console Artifacts page has a live surface. A nil surface is treated as "WithArtifactsSurface not supplied".
func WithAuthSurface ¶
func WithAuthSurface(surface *auth.RotateSurface) Option
WithAuthSurface wires the `auth.*` handler into NewMux — the single net-new Console Settings-page method (`auth.rotate_token`). surface is the *auth.RotateSurface the `POST /v1/auth/rotate_token` route dispatches through.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied (or supplied a nil surface), the `auth.*` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev` / `harbor console`) supplies it so the Console Settings page "Rotate token" action has a live surface. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport — `auth.rotate_token` then gates on the verified `auth.ScopeAdmin` claim. A nil surface is treated as "WithAuthSurface not supplied".
func WithEventsList ¶ added in v1.13.0
func WithEventsList(arts artifacts.ArtifactStore) Option
WithEventsList wires the `events.list` durable, time-ranged, cross-session raw-event read handler into NewMux — the `POST /v1/events/list` route. The bus is the mandatory NewMux bus (it MUST implement events.HistoryReplayer to serve a non-error page; a bus without the capability surfaces CodeRuntimeError per request); arts is the ArtifactStore the heavy-payload refs are enriched from.
A nil store leaves the `events.list` route UN-mounted (the route's smoke `skip_if_404` keeps preflight green on a partial build). When supplied the route is mounted and, when WithValidator is also set, wrapped in auth.Middleware like every other transport.
events.list is READ-ONLY (CLAUDE.md §13) — it projects the durable event log; no mutation path is mounted.
func WithFlows ¶
func WithFlows(surface *flowprotocol.Surface) Option
WithFlows wires the Console Flows-page handler into NewMux. surface is the transport-agnostic flowprotocol.Surface the six `POST /v1/flows/*` routes dispatch through.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied (or supplied a nil surface), the Flows routes are NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev`) supplies it so the Console Flows page has a live surface.
Five of the six routes are read-only; `POST /v1/flows/run` mutates and the Surface gates it on the verified `auth.ScopeAdmin` claim (closed two-scope set — no new scope is minted). When WithValidator is also set, the handler is wrapped in auth.Middleware like every other transport.
func WithGovernanceKeyRotate ¶ added in v1.5.0
func WithGovernanceKeyRotate(s *governanceprotocol.KeyRotateService) Option
WithGovernanceKeyRotate wires the rotate-key service so the `POST /v1/governance/rotate_key` route is live (the admin LLM-provider key-rotation surface). OPTIONAL; built only alongside WithGovernance Service. A nil service leaves the route returning 501. When supplied AND WithValidator is set, the route inherits the same admin gate as the rest of `/v1/governance/*`.
func WithGovernancePostureWrite ¶ added in v1.17.0
func WithGovernancePostureWrite(s *governanceprotocol.PostureWriteService) Option
WithGovernancePostureWrite wires the set-posture service so the `POST /v1/governance/set_posture` route is live (the admin identity-tier policy WRITE surface — the write sibling of `governance.posture`). OPTIONAL; built only alongside WithGovernanceService. A nil service leaves the route returning 501. When supplied AND WithValidator is set, the route inherits the same admin gate as the rest of `/v1/governance/*` (auth.ScopeAdmin ONLY — a leaked read-only `console:fleet` token cannot widen a budget).
func WithGovernanceService ¶ added in v1.5.0
func WithGovernanceService(s *governanceprotocol.Service) Option
WithGovernanceService wires the admin-scoped governance tenant-override handler into NewMux — the `POST /v1/governance/{set,get}_tenant_overrides` routes.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied (or supplied a nil service), the `/v1/governance/*` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev` / `harbor console`) supplies it so an admin can change a tenant's default LLM parameters live. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport — the handler then gates on the verified `auth.ScopeAdmin` claim. A nil service is treated as "WithGovernanceService not supplied".
func WithKeepalive ¶
WithKeepalive overrides the SSE keepalive-comment interval on the stream transport. A non-positive value is ignored.
func WithLogger ¶
WithLogger sets the slog.Logger both transport handlers log to. A nil logger is ignored; the handlers fall back to slog.Default().
func WithMCPSurface ¶
func WithMCPSurface(s control.MCPSurface) Option
WithMCPSurface wires the MCP-Connections dispatcher into the control transport. When supplied, the control handler routes the twelve `mcp.servers.*` methods to the MCP surface instead of falling through to the task-control ControlSurface.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied, the control transport rejects MCP calls with CodeUnknownMethod (HTTP 404) — the 404 → SKIP path the smoke script relies on. Production wiring (`harbor dev`) supplies it so the Console MCP Connections page has a live surface. A nil surface is treated as "WithMCPSurface not supplied".
func WithMemory ¶
func WithMemory(store memory.MemoryStore, driverName string) Option
WithMemory wires the three `memory.*` read routes into NewMux. store is the memory.MemoryStore the Console Memory page projects from; driverName is the configured memory- driver name surfaced on each row. The memory handler reuses the ArtifactStore + heavy-content threshold supplied via WithPauseList for the heavy-value bypass — so WithPauseList must also be set for the three `memory.*` routes to mount.
store is OPTIONAL — when it is nil (or the ArtifactStore / heavyThreshold from WithPauseList are unset), the three `memory.*` routes are left UN-mounted (the route's smoke `skip_if_404` keeps preflight green on a partial build). When supplied correctly the routes `POST /v1/memory/list` / `/get` / `/health` are mounted and, when WithValidator is also set, wrapped in auth.Middleware like every other transport.
The three methods are READ-ONLY (CLAUDE.md §13) — they project the shipped MemoryStore surface; no mutation path is mounted.
func WithPauseList ¶
func WithPauseList(coord pauseresume.Coordinator, store artifacts.ArtifactStore, heavyThreshold int) Option
WithPauseList wires the `pause.list` snapshot handler into NewMux. coord is the unified pause/resume Coordinator the snapshot projects from; store is the ArtifactStore the heavy-content bypass routes oversized pause payloads through; heavyThreshold is the configured heavy-content byte size (cfg.Artifacts.HeavyOutputThresholdBytes).
All three are required together — supplying the option with a nil coord, a nil store, or a non-positive threshold leaves the `pause.list` route UN-mounted (the route's smoke `skip_if_404` keeps preflight green on a partial build). When supplied correctly the route `POST /v1/pause/list` is mounted and, when WithValidator is also set, wrapped in auth.Middleware like every other transport.
pause.list is READ-ONLY against the Coordinator (CLAUDE.md §7 rule 4 / §13) — it reads the shipped pause-coordinator state, it does not reinvent pause coordination.
func WithPostureSurface ¶
func WithPostureSurface(s control.PostureSurface) Option
WithPostureSurface wires the posture dispatcher into the control transport. When supplied, the control handler routes the seven posture methods — the five `runtime.*` / `metrics.*` reads plus `governance.posture` / `llm.posture` — to the posture surface instead of falling through to the task-control ControlSurface.
The option is OPTIONAL so existing call-sites compile unchanged. When not supplied, the control transport rejects posture calls with CodeUnknownMethod (HTTP 404) — the 404 → SKIP path the smoke script relies on. Production wiring (`harbor dev`) supplies it so the Console Settings page has a live surface. A nil surface is treated as "WithPostureSurface not supplied".
func WithRedactor ¶
WithRedactor wires the audit.Redactor into the control transport so the admin-impersonation gate can publish a redacted `audit.admin_scope_used` event onto the bus on every accepted impersonation. The bus is already mandatory at NewMux (it feeds the SSE event transport); the redactor is the second half of the pair the control transport needs to enable impersonation.
The option is OPTIONAL at the type level so existing call-sites compile unchanged. When the redactor is not supplied, the control transport refuses impersonation requests fail-closed with CodeRuntimeError (CLAUDE.md §13 "Silent degradation"). Production wiring (the `harbor dev` boot path) SHOULD supply it.
A nil redactor is treated as "WithRedactor not supplied".
func WithRunsService ¶
func WithRunsService(s *runsprotocol.Service) Option
WithRunsService wires the Console Playground-page handler into NewMux — the `POST /v1/runs/set_overrides` route.
The service is OPTIONAL so existing call-sites compile unchanged. When unsupplied, the `POST /v1/runs/{method}` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev`) supplies it so the Console Playground page can record next-message overrides. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport. A nil service is treated as "WithRunsService not supplied".
func WithSearch ¶
func WithSearch(s control.SearchSurface) Option
WithSearch wires the search dispatcher into the control transport. When supplied, the handler routes the five `search.*` methods to s.Dispatch instead of falling through to the task-control ControlSurface.
The surface is OPTIONAL so existing call-sites compile unchanged. When unsupplied (or supplied a nil surface), the control transport rejects search calls with CodeUnknownMethod — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev` / `harbor console`) supplies it so the five `search.*` methods serve live results. A nil surface is treated as "WithSearch not supplied".
func WithSessionsService ¶
func WithSessionsService(s *sessionsprotocol.Service) Option
WithSessionsService wires the Console Sessions-page handler into NewMux — the two `POST /v1/sessions/*` routes (`sessions.list` / `sessions.inspect`).
The service is OPTIONAL so existing call-sites compile unchanged. When unsupplied, the `POST /v1/sessions/{method}` routes are NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev`) supplies it so the Console Sessions page has a live surface. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport — a cross-tenant `sessions.list` filter then gates on the verified `auth.ScopeAdmin` claim. A nil service is treated as "WithSessionsService not supplied".
func WithStateHistory ¶ added in v1.6.0
func WithStateHistory(bus events.EventBus, arts artifacts.ArtifactStore) Option
WithStateHistory wires the `state.history` windowed event-replay handler into NewMux — the `POST /v1/state/history` route. bus is the durable event bus the windowed read scans (it MUST implement events.HistoryReplayer to serve a non-error page; a bus without the capability surfaces CodeRuntimeError per request); arts is the ArtifactStore the heavy-payload refs are enriched from.
Both are required together — supplying the option with a nil bus or a nil store leaves the `state.history` route UN-mounted (the route's smoke `skip_if_404` keeps preflight green on a partial build). When supplied correctly the route is mounted and, when WithValidator is also set, wrapped in auth.Middleware like every other transport.
state.history is READ-ONLY (CLAUDE.md §13) — it projects the durable event log; no mutation path is mounted.
func WithTasksService ¶
func WithTasksService(s *tasksprotocol.Service) Option
WithTasksService wires the `tasks.*` handler into NewMux — the two Console Tasks-page read methods (`tasks.list` / `tasks.get`).
The service is OPTIONAL so existing call-sites compile unchanged. When unsupplied, the `POST /v1/tasks/{method}` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev`) supplies it so the Console Tasks page has a live surface. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport — a cross-tenant `tasks.list` fan-in then gates on the verified `auth.ScopeAdmin` claim. A nil service is treated as "WithTasksService not supplied".
Both `tasks.*` methods are READ-ONLY (CLAUDE.md §13) — the Console Tasks page consumes the shipped task-control verbs for mutation; no `tasks.*` mutation path is mounted.
func WithToolsService ¶
func WithToolsService(s *toolsprotocol.Service) Option
WithToolsService wires the `tools.*` handler into NewMux — the seven Console Tools-page methods (`tools.list` / `tools.get` / `tools.describe` / `tools.metrics` / `tools.content_stats` / `tools.set_approval_policy` / `tools.revoke_oauth`).
The service is OPTIONAL so existing call-sites compile unchanged. When unsupplied, the `POST /v1/tools/{method}` route is NOT mounted — the smoke script's `skip_if_404` keeps preflight green on a partial build. Production wiring (`harbor dev`) supplies it so the Console Tools page has a live surface. When supplied AND WithValidator is set, the route is wrapped in auth.Middleware like every other transport — the two admin methods then gate on the verified `auth.ScopeAdmin` claim. A nil service is treated as "WithToolsService not supplied".
func WithValidator ¶
WithValidator wires the JWT auth.Validator into NewMux. BOTH transport handlers (REST control + SSE stream) are wrapped in auth.Middleware: every request must carry a verified `Authorization: Bearer <jwt>`; the middleware injects the verified identity + scopes into the request context.Context before the underlying handler runs.
A validator is **mandatory** — `NewMux` returns `ErrMisconfigured` when neither `WithValidator` nor `WithoutValidator` is supplied (PR #91 amendment; CLAUDE.md §13 "Test stubs as production defaults on operator-facing seams"). A nil validator is treated as "WithValidator not supplied"; tests that legitimately need the unauthenticated path use `WithoutValidator()` explicitly.
func WithoutValidator ¶
func WithoutValidator() Option
WithoutValidator is the explicit, test-only escape hatch for cases that legitimately need the trust-based posture (the REST handler inherits `ControlSurface.Dispatch`'s identity-from-body gate, the SSE handler resolves identity from the `X-Harbor-*` carrier headers via `resolveIdentity`). It is used by the own package tests + `test/integration/phase60_wire_transport_test.go` to assert the pre-auth transport surface still works.
PRODUCTION CODE MUST NEVER USE THIS OPTION. A Runtime that boots with `WithoutValidator` exposes an unauthenticated Protocol surface, which violates CLAUDE.md §13's "Test stubs as production defaults" rule. The option is named for grepability: an audit can find every production-side WithoutValidator call site at compile time.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package control is the Harbor Protocol REST/JSON control transport — the client→server half of the wire binding RFC §5.4 resolves to (SSE for events + REST/JSON for control).
|
Package control is the Harbor Protocol REST/JSON control transport — the client→server half of the wire binding RFC §5.4 resolves to (SSE for events + REST/JSON for control). |
|
Package cors is the Harbor Protocol CORS middleware — the security primitive that unlocks the multi-process Console+Runtime posture (Console on one origin attaches to a Runtime on a different origin) without weakening the browser-side enforcement contract.
|
Package cors is the Harbor Protocol CORS middleware — the security primitive that unlocks the multi-process Console+Runtime posture (Console on one origin attaches to a Runtime on a different origin) without weakening the browser-side enforcement contract. |
|
Package stream — addition: the admin-scoped `agent_config.*` agent-config control-plane HTTP handler.
|
Package stream — addition: the admin-scoped `agent_config.*` agent-config control-plane HTTP handler. |