transports

package
v1.21.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

func WithKeepalive(d time.Duration) Option

WithKeepalive overrides the SSE keepalive-comment interval on the stream transport. A non-positive value is ignored.

func WithLogger

func WithLogger(l *slog.Logger) Option

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

func WithRedactor(r audit.Redactor) Option

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

func WithValidator(v auth.Validator) Option

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL