Documentation
¶
Overview ¶
Package serve is the importable serve band: the config→listener composition that turns a loaded configuration into a running Harbor Protocol server.
Distinct from internal/server ¶
internal/server is the Protocol server's request-handling core (scope authorizers, bootstrap handler, search-scope adapters). This package sits ABOVE it: it assembles the runtime (via internal/runtime/assemble), mounts the Protocol surfaces (via BuildMux), binds the listener, and owns the serve/close lifecycle. In short — internal/server answers requests; this package composes the process that listens for them.
One constructor, production-shaped by construction ¶
Boot REQUIRES a non-nil auth-validator factory: identity is mandatory, so a nil factory is a loud construction error, never an unauthenticated listener. Boot mounts ONLY the surfaces every caller shares. Dev-only surfaces (the bootstrap-token endpoint, draft scaffolding, the token-rotate surface, the Console static build, fixture seeding, the mock-LLM snapshot override) are composed CALLER-SIDE through the injection seams on Options — the extra pre-CORS routes, the auth-surface builder, the LLM-snapshot builder, and the post-boot hook. The dev signer never reaches this package.
Lifecycle ¶
Boot returns a Handle whose Serve binds the listener and runs until ctx cancels, and whose Close drains every subsystem in reverse dependency order. On any boot error every already-opened subsystem is Closed before returning.
cmd/harbor/session_ensurer.go — adapts the concrete sessions.Registry to the protocol.SessionEnsurer seam.
The Protocol ControlSurface owns the create-on-first-use behaviour on `start` but must not import the sessions package (it depends only on the error-only `protocol.SessionEnsurer` interface). This adapter lives at the cmd/harbor wiring boundary — the one place allowed to know both the concrete registry and the Protocol surface — and translates the registry's lifecycle sentinels into the protocol-side sentinels the surface's error mapper understands.
Index ¶
- Variables
- func BootDeclaredMCPServerNames(cfg *config.Config) []string
- func BootDeclaredMCPServerSet(cfg *config.Config) map[string]struct{}
- func BootDeclaredOAuthProviderNames(cfg *config.Config) []string
- func InstanceID(prefix string) string
- func MCPAddStdioAllowlist(cfg *config.Config) []string
- type ApprovalChecker
- type AuthSurfaceBuilder
- type AuthValidatorFactory
- type BuiltMux
- type Enricher
- func (e *Enricher) Cost(_ context.Context, _ identity.Identity, _ string) prototypes.TaskCostRollup
- func (e *Enricher) ParentSession(ctx context.Context, id identity.Identity, _ string) prototypes.TaskParentSessionRef
- func (e *Enricher) PlannerSnapshot(_ context.Context, _ identity.Identity, _ string) *prototypes.TaskPlannerSnapshotRef
- func (e *Enricher) Trajectory(_ context.Context, _ identity.Identity, taskID string) *prototypes.TaskTrajectoryRef
- type EnricherOption
- type ExtraRoutesFunc
- type Handle
- type LLMProviderInstaller
- func (i *LLMProviderInstaller) BootConnectPrimary(ctx context.Context, brokerName string) error
- func (i *LLMProviderInstaller) BrokerNames() []string
- func (i *LLMProviderInstaller) Close(context.Context) error
- func (i *LLMProviderInstaller) InstallLLMProvider(_ context.Context, _, _ string, desc agentcfg.LLMProviderDescriptor) error
- func (i *LLMProviderInstaller) UninstallLLMProvider(_ context.Context, name string) error
- type LLMSnapshotBuilder
- type MCPConnectionAttacher
- func (a *MCPConnectionAttacher) Attach(ctx context.Context, req agentcfgprotocol.AttachRequest) error
- func (a *MCPConnectionAttacher) Close(ctx context.Context) error
- func (a *MCPConnectionAttacher) SetOAuthDiscoveryOrigins(ctx context.Context, name string, origins []string) (prev []string, err error)
- type MCPConnectionDetacher
- func (d *MCPConnectionDetacher) AttachedSources(_ context.Context, owner toolauth.Owner) []string
- func (d *MCPConnectionDetacher) Detach(ctx context.Context, source string) error
- func (d *MCPConnectionDetacher) SetOAuthDiscoveryOrigins(ctx context.Context, name string, origins []string) (prev []string, err error)
- type MuxInput
- type OAuthProviderInstaller
- func (i *OAuthProviderInstaller) InstallProvider(ctx context.Context, tenant, agentID string, ...) error
- func (i *OAuthProviderInstaller) InstalledFor(_ context.Context, owner toolauth.Owner) []string
- func (i *OAuthProviderInstaller) UninstallProvider(ctx context.Context, tenant, agentID, name string) error
- type Options
- type PostBootFunc
- type PostBootHandles
- type RouteMount
- type RunLoopDriver
- type RunLoopDriverOptions
- type SessionEnsurerAdapter
- type TenantOverrideResolver
Constants ¶
This section is empty.
Variables ¶
var ErrAuthValidatorFactoryRequired = errors.New("serve: auth-validator factory is required (identity is mandatory; nil is never an unauthenticated listener)")
ErrAuthValidatorFactoryRequired is returned by Boot when Options carries a nil AuthValidatorFactory. Identity is mandatory: the serve band never stands up an unauthenticated listener. Callers compare via errors.Is.
var ErrAuthValidatorRequired = errors.New("serve: auth-validator factory returned a nil validator (identity is mandatory; nil is never an unauthenticated listener)")
ErrAuthValidatorRequired is returned by Boot when the configured AuthValidatorFactory returns a nil Validator with a nil error. A nil Validator is the test-kit auth opt-out that BuildMux reads as transports.WithoutValidator(); the serve band never takes that path, so Boot fails construction here instead. Callers compare via errors.Is.
var ErrRunLoopDriverMisconfigured = errors.New("dev: per-task RunLoop driver missing a mandatory dependency")
ErrRunLoopDriverMisconfigured fires when NewRunLoopDriver is called with a nil bus / RunLoop / planner. Driver invariant: all three are mandatory.
var ErrRuntimeAddOwnerMissing = errors.New("serve: runtime-added mcp connection is missing its (tenant, agent) owner")
ErrRuntimeAddOwnerMissing is returned by MCPConnectionAttacher.Attach when a runtime-added connection reaches the attach path without a full (tenant, agent) owner. It is a fail-closed guard: a runtime-added entry with no owner tag would be an unreconcilable orphan, so the attach is rejected loudly rather than registering an untagged runtime-add.
var ErrWireDescriptorDisabled = errors.New("serve: wire-carried oauth provider not rebuilt — tools.allow_wire_oauth_descriptor is off (the dev opt-in is a kill-switch: a wire provider installed while it was on is not rebuilt after a restart with it off)")
ErrWireDescriptorDisabled — a wire-carried provider (TokenURL set) was presented for build while the fail-closed wire-descriptor opt-in is OFF. The install path never reaches this (the agent-config handler gates the write), so it fires only on the run-start RECONCILE of a wire provider persisted during a prior opt-in-ON window: the kill-switch refuses to rebuild it live after a restart with the opt-in off.
Functions ¶
func BootDeclaredMCPServerNames ¶
BootDeclaredMCPServerNames returns the names of every MCP server declared in the boot yaml (`tools.mcp_servers[].name`) — the set the remove verb rejects and the run-start reconcile never detaches (boot-declared servers are not revisioned state).
func BootDeclaredMCPServerSet ¶
BootDeclaredMCPServerSet returns the boot-declared MCP server names as a set for the run-start reconcile's O(1) skip check.
func BootDeclaredOAuthProviderNames ¶ added in v1.14.0
BootDeclaredOAuthProviderNames returns the names of every OAuth provider declared in the boot yaml (`tools.oauth_providers[].name`) — the set the set/remove_oauth_provider verbs reject (boot wins; edit yaml + restart).
func InstanceID ¶
InstanceID mints a stable-per-process instance identifier of the form "<prefix>-<hostname>" (bare prefix when the hostname is unavailable). A Console attached to multiple Runtimes keys each attachment by it. Shared by every production serve caller so the id shape stays uniform.
func MCPAddStdioAllowlist ¶
MCPAddStdioAllowlist projects the operator's stdio allowlist out of config for the runtime add-connection gate. A nil block yields a nil allowlist (fail-closed — every stdio add is rejected).
Types ¶
type ApprovalChecker ¶ added in v1.14.0
type ApprovalChecker struct {
// contains filtered or unexported fields
}
ApprovalChecker is the production tasks.list approval-gate seam — it answers "does this task's run hold an open HITL / tool-approval gate?" by reading the pause coordinator, scoped to the task's own identity + run (the projection-completeness gate). It satisfies the tasks/protocol.ApprovalChecker interface structurally, keeping the pause package out of tasks/protocol's import graph (§4.4 seam).
safe for concurrent reuse: immutable after construction — it holds only the coordinator reference (itself safe for concurrent reuse) and reads all per-call state from its arguments.
func NewApprovalChecker ¶ added in v1.14.0
func NewApprovalChecker(pauses pauseresume.Coordinator) *ApprovalChecker
NewApprovalChecker builds the coordinator-backed approval checker. A nil coordinator returns nil so the caller leaves the seam unwired (the row's has_pending_approval stays at its honest false — the projection gate's Half-B prod-wiring test proves production wires a non-nil coordinator).
func (*ApprovalChecker) HasPendingApproval ¶ added in v1.14.0
func (c *ApprovalChecker) HasPendingApproval(ctx context.Context, taskIdentity identity.Identity, runID string) bool
HasPendingApproval reports whether the task's run holds at least one PAUSED pause record with the ApprovalRequired reason, scoped to the task's own (tenant, user, session) [+ run]. The read is identity-scoped so session A's open gate never bleeds into session B's row. A read error or an incomplete triple returns false (honest — the row shows no gate rather than a fabricated one; the failure is logged nowhere hotter than the coordinator's own diagnostics).
type AuthSurfaceBuilder ¶
type AuthSurfaceBuilder func(red audit.Redactor, bus events.EventBus, logger *slog.Logger) (*auth.RotateSurface, error)
AuthSurfaceBuilder builds the optional token-rotate surface after the runtime is assembled (it needs the redactor + bus the assembly produces). The dev caller injects a builder that closes over its dev signer; the production caller leaves it nil (no in-runtime token issuer).
type AuthValidatorFactory ¶
type AuthValidatorFactory func(ctx context.Context, cfg *config.Config, red audit.Redactor, bus events.EventBus, logger *slog.Logger) (auth.Validator, error)
AuthValidatorFactory builds the Protocol auth validator from the loaded config plus the assembled redactor / bus / logger. The production caller injects a JWKS-backed factory; the dev caller injects one built from its ephemeral dev signer. It is REQUIRED — a nil factory fails Boot loud.
func NewJWKSAuthValidatorFactory ¶
func NewJWKSAuthValidatorFactory() AuthValidatorFactory
NewJWKSAuthValidatorFactory returns THE production auth-validator factory: it projects the operator's identity config onto a JWKS-backed Validator (URL or file source), wiring the assembled redactor / bus / logger. The initial JWKS fetch runs synchronously inside the projection, so a bad source fails the boot loud. Every production serve caller (the serve subcommand and the external-serving facade) injects this one factory — a second hand-rolled copy is the drift this shared constructor exists to prevent.
type BuiltMux ¶
type BuiltMux struct {
// Mux is the transports mux the caller mounts under /v1/.
Mux *http.ServeMux
// FlowRegistry is the empty registry the flows surface serves. Nil when
// the flows surface was not mounted (no artifact store / task registry).
FlowRegistry *flow.Registry
}
BuiltMux is the result of BuildMux: the mounted Protocol mux plus the flow registry the flows surface serves (the caller seeds the same registry).
type Enricher ¶
type Enricher struct {
// contains filtered or unexported fields
}
Enricher is the production tasks.get Enricher for the dev stack. It provides parent-session / planner-snapshot / trajectory enrichment from in-memory runtime state.
safe for concurrent reuse: the enricher is immutable after construction — the trajectory accessor is a pure function, the session lister is itself safe for concurrent reuse, and no mutable receiver state is added.
func NewEnricher ¶
func NewEnricher(trajectoryFn func(tasks.TaskID) *planner.Trajectory, opts ...EnricherOption) *Enricher
NewEnricher builds the tasks.get Enricher over a trajectory accessor. The accessor is a pure function (the run-loop driver's TrajectoryByTaskID), so the returned Enricher is safe for concurrent reuse.
func (*Enricher) Cost ¶
func (e *Enricher) Cost(_ context.Context, _ identity.Identity, _ string) prototypes.TaskCostRollup
Cost returns a zero-valued cost rollup. Per-task cost aggregation from the `llm.cost.recorded` event stream is DEFERRED to the tools annotator follow-up (a documented risk-note deferral): the sessions cost helper is session-scoped and not extractable for a task-scoped rollup without a divergent second reader, so the un-stub lands with the tools annotator work rather than forking the cost path. This is display-only (no facet/sort operates over the task cost rollup), so the honest zero is gate-clean.
func (*Enricher) ParentSession ¶
func (e *Enricher) ParentSession(ctx context.Context, id identity.Identity, _ string) prototypes.TaskParentSessionRef
ParentSession returns the parent-session card for the task — the session's lifecycle status + start/last-activity timestamps, read from the session lister scoped to the caller's own identity (the task's session is the caller's session; GetTask already scoped the lookup).
AgentName is left EMPTY on purpose: no single-valued session→agent binding exists in V1 (a session may run several agents over its life), so its absence is representable (omitted) rather than a fabricated value — the same honest-omission the sessions projection takes for its agent fields (the silent-absence class rule). The projector overlays only the non-zero fields, so the SessionID baseline it set is preserved.
func (*Enricher) PlannerSnapshot ¶
func (e *Enricher) PlannerSnapshot(_ context.Context, _ identity.Identity, _ string) *prototypes.TaskPlannerSnapshotRef
PlannerSnapshot returns nil — planner-checkpoint references are deferred to the checkpoint store.
func (*Enricher) Trajectory ¶
func (e *Enricher) Trajectory(_ context.Context, _ identity.Identity, taskID string) *prototypes.TaskTrajectoryRef
Trajectory projects the planner's in-memory reasoning trace onto the Protocol wire. Steps with empty ReasoningTrace are filtered out. Returns nil when the task's trajectory is unavailable (evicted or the run-loop didn't store one).
type EnricherOption ¶ added in v1.14.0
type EnricherOption func(*Enricher)
EnricherOption configures NewEnricher.
func WithSessionLister ¶ added in v1.14.0
func WithSessionLister(l sessions.SessionLister) EnricherOption
WithSessionLister wires the session lister the parent-session card reads the parent session's status + timestamps from. A nil lister is treated as "not supplied".
type ExtraRoutesFunc ¶
ExtraRoutesFunc mounts caller-side pre-CORS routes and returns any closers they registered. Called after the boot's validator and bind address are known and before the CORS wrap. On error, the closers accumulated up to the failing step MUST still be returned — Boot appends them to its rollback chain before inspecting the error, so a partially-mounted seam never leaks an already-constructed subsystem.
type Handle ¶
type Handle struct {
// Cfg / Bus are exported so a supervising caller (the dev hot-reload
// loop) can read the reload policy and publish reload lifecycle events.
Cfg *config.Config
Bus events.EventBus
// contains filtered or unexported fields
}
Handle is the running serve band a successful Boot returns. Serve binds the listener and runs until ctx cancels; Close drains every subsystem in reverse dependency order.
func (*Handle) BindAddr ¶
BindAddr reports the address the listener is (or will be) bound to. After Serve binds an ephemeral port it reflects the OS-assigned address. Internally synchronized — safe to call while Serve runs.
func (*Handle) Close ¶
Close runs every subsystem's Close in reverse dependency order. Idempotent: the closer slice is drained exactly once; a second Close is a no-op. Close also fires a "closed before bind" readiness signal so a WaitReady waiter on a handle whose Serve never bound (or was cancelled before binding) returns promptly instead of blocking until its own ctx expires.
func (*Handle) Handler ¶
Handler returns the composed CORS-wrapped router the listener serves. It lets a caller drive the Protocol surface (and any caller-mounted pre-CORS route) through httptest without binding a port — the posture split between the dev and serve compositions is asserted this way.
func (*Handle) WaitReady ¶ added in v1.15.0
WaitReady blocks until the listener binds (returning the actual OS-assigned address) or until the bind fails / ctx cancels (returning the error). It is the race-safe one-shot readiness contract a co-launched client (the TUI, an operator script, an embedder) waits on before dialing the server — no polling, no second listener lifecycle.
Safe to call from multiple goroutines before, during, or after Serve binds: the first successful bind delivers the address to every waiter through the buffered channel; a late caller after a successful bind returns immediately. If Serve was never called or was cancelled before binding, WaitReady blocks until ctx cancels (callers SHOULD scope their wait with a deadline). Close fires a "closed before bind" signal so a waiter on an abandoned handle returns promptly.
type LLMProviderInstaller ¶ added in v1.17.0
type LLMProviderInstaller struct {
// contains filtered or unexported fields
}
LLMProviderInstaller manages the runtime-level set of Protocol-installed inference provider bindings. Because a provider KEY is a runtime-level credential (not identity-scoped), the set is keyed by binding NAME, not by owner tuple — the install/uninstall verbs feed the one shared LiveKey.
Concurrent reuse: the immutable config is set once at construction; the managed-source map is guarded by mu. Per-run identity is never read from the installer.
func NewLLMProviderInstaller ¶ added in v1.17.0
func NewLLMProviderInstaller(liveKey *llm.LiveKey, primaryProvider string, brokers []config.InferenceBrokerConfig, bus events.EventBus, redactor audit.Redactor, principal string, logger *slog.Logger) *LLMProviderInstaller
NewLLMProviderInstaller builds the production installer. liveKey is mandatory (the shared holder the bifrost Account reads); a nil returns a nil installer so the caller leaves the verb unwired (→ 501 at the wire edge) rather than nil-panicking. primaryProvider is the runtime's configured provider — a binding for any other provider is rejected (V1 is single-provider). principal is the per-runtime service-principal id for the pull audit event.
func (*LLMProviderInstaller) BootConnectPrimary ¶ added in v1.17.0
func (i *LLMProviderInstaller) BootConnectPrimary(ctx context.Context, brokerName string) error
BootConnectPrimary connects a config-declared brokered PRIMARY (`llm.credential_source: remote`) SYNCHRONOUSLY and fails loud on broker-unreachable — the boot exits non-zero naming the broker + missing config (never a silent empty LiveKey). It then starts the refresh scheduler. Called once at boot when the primary is brokered.
func (*LLMProviderInstaller) BrokerNames ¶ added in v1.17.0
func (i *LLMProviderInstaller) BrokerNames() []string
BrokerNames returns the boot-declared inference-broker names (for the agent-config service's WithInferenceBrokers unknown-broker gate).
func (*LLMProviderInstaller) Close ¶ added in v1.17.0
func (i *LLMProviderInstaller) Close(context.Context) error
Close stops every managed binding's refresh goroutine + closes its source (zeroing the LiveKey). Joined on runtime shutdown.
func (*LLMProviderInstaller) InstallLLMProvider ¶ added in v1.17.0
func (i *LLMProviderInstaller) InstallLLMProvider(_ context.Context, _, _ string, desc agentcfg.LLMProviderDescriptor) error
InstallLLMProvider installs (upserts) / rotates the named binding: it builds the broker-pull source over the shared LiveKey and starts an async first pull + refresh scheduler. A re-install by the same name replaces (closes) the prior binding (the rotate path). The pull is best-effort at install time and fail-loud in the loop (parity with the OAuth lazy credential resolution) — the binding is installed immediately.
func (*LLMProviderInstaller) UninstallLLMProvider ¶ added in v1.17.0
func (i *LLMProviderInstaller) UninstallLLMProvider(_ context.Context, name string) error
UninstallLLMProvider removes the named binding and CLOSES its source — the live key is zeroed so a subsequently-bound call fails loud rather than serving the old key. A missing name is an idempotent no-op.
type LLMSnapshotBuilder ¶
type LLMSnapshotBuilder func(cfg *config.Config) (*llm.ConfigSnapshot, error)
LLMSnapshotBuilder projects the loaded config onto the LLM ConfigSnapshot the runtime opens the client with. The dev caller injects a builder that runs the mock-LLM gate and overrides the driver when the escape hatch fired; a nil builder uses the default llm.SnapshotFromConfig projection.
type MCPConnectionAttacher ¶
type MCPConnectionAttacher struct {
// contains filtered or unexported fields
}
MCPConnectionAttacher implements agentcfgprotocol.ConnectionAttacher by reusing the boot-time mcpdrv.Attach lifecycle (dial → initialize → discover → register) against the LIVE catalog + registry + bus. It owns a mutex-guarded closer chain so a runtime-added server's transport drains on stack teardown.
Concurrent reuse: the collaborators (catalog / registry / bus / logger / defaultIdentity) are set once at construction; the only mutable state is the closers slice, guarded by mu (documented internally-synchronised).
func NewMCPConnectionAttacher ¶
func NewMCPConnectionAttacher(catalog tools.ToolCatalog, registry *mcpdrv.Registry, bus events.EventBus, logger *slog.Logger, defaultIdentity identity.Identity, oauthProviders map[string]toolauth.OAuthProvider, oauthProviderSet toolauth.ProviderSet) *MCPConnectionAttacher
NewMCPConnectionAttacher builds the production attacher. catalog, registry, and bus are mandatory (mcpdrv.Attach validates them too). oauthProviders may be nil when no provider is declared; oauthProviderSet, when non-nil, is the runtime set (boot seed + installs) resolution consults first.
func (*MCPConnectionAttacher) Attach ¶
func (a *MCPConnectionAttacher) Attach(ctx context.Context, req agentcfgprotocol.AttachRequest) error
Attach drives the real MCP attach for one runtime-added connection. The operator-supplied auth Headers flow to the live transport ONLY (the agent-config service never persists them). A partial-attach failure is drained by mcpdrv.Attach's own closer-chain handling; this method merges the per-add closers into its master chain under the lock so teardown drains the subprocess. An auth-required condition is surfaced by wrapping agentcfgprotocol.ErrAuthRequired so the service parks on the unified pause/resume primitive.
func (*MCPConnectionAttacher) Close ¶
func (a *MCPConnectionAttacher) Close(ctx context.Context) error
Close drains every runtime-added server's transport in reverse order. Wired into the boot closer chain so a runtime-added subprocess does not outlive the stack.
func (*MCPConnectionAttacher) SetOAuthDiscoveryOrigins ¶ added in v1.14.0
func (a *MCPConnectionAttacher) SetOAuthDiscoveryOrigins(ctx context.Context, name string, origins []string) (prev []string, err error)
SetOAuthDiscoveryOrigins applies a runtime-added connection's OAuth-discovery cross-origin allow-list to the LIVE MCP registry (the live half of `agent_config.set_mcp_discovery_origins`), returning the prior set. It satisfies the agent-config service's DiscoveryOriginApplier seam by delegating to the process-global bare-name registry (identity-mandatory for authorization; the registry is never re-keyed by identity). The revoke-prune of any recorded requirement lives in the registry mutator.
type MCPConnectionDetacher ¶
type MCPConnectionDetacher struct {
// contains filtered or unexported fields
}
MCPConnectionDetacher implements projection.ConnectionDetacher against the LIVE catalog + registry. Concurrent reuse: the collaborators are set once at construction; the type holds no mutable state (the registry + catalog own their own synchronisation).
func NewMCPConnectionDetacher ¶
func NewMCPConnectionDetacher(catalog tools.ToolCatalog, registry *mcpdrv.Registry, logger *slog.Logger) *MCPConnectionDetacher
NewMCPConnectionDetacher builds the production detacher. catalog and registry are mandatory (the reconcile is a no-op without them).
func (*MCPConnectionDetacher) AttachedSources ¶
AttachedSources returns the reconciling OWNER's runtime-added source ids — the owner-scoped reconcile VIEW (Registry.RuntimeAddedSources), NOT the process-global Registry.SourceIDs enumeration. Boot-declared servers stay untagged and every OTHER owner's runtime-adds carry a different owner tag, so neither appears in the view: one owner's run-start reconcile can never detach a boot server or another owner's connection. The registry + catalog stay process-global and deployment-shared (resolution + dispatch by bare name); only the reconcile VIEW is owner-scoped.
func (*MCPConnectionDetacher) Detach ¶
func (d *MCPConnectionDetacher) Detach(ctx context.Context, source string) error
Detach deregisters the source's tools from the catalog (when the catalog supports source deregistration — the optional CatalogSourceDeregisterer companion) and from the MCP registry, closing its transport gracefully. An already-gone source is a no-op (ErrServerNotFound is swallowed — idempotent).
func (*MCPConnectionDetacher) SetOAuthDiscoveryOrigins ¶ added in v1.14.0
func (d *MCPConnectionDetacher) SetOAuthDiscoveryOrigins(ctx context.Context, name string, origins []string) (prev []string, err error)
SetOAuthDiscoveryOrigins re-applies a runtime-added connection's OAuth-discovery cross-origin allow-list to the live registry — the run-start allowance-reconcile leg's live effect (the rollback path for the discovery-allowance write). It delegates to the process-global bare-name registry (identity-mandatory for authorization). Satisfies projection.DiscoveryOriginReconciler alongside AttachedSources.
type MuxInput ¶
type MuxInput struct {
Cfg *config.Config
Surface *protocol.ControlSurface
Bus events.EventBus
// Redactor / Logger / Metrics feed every surface's audit + posture legs.
Redactor audit.Redactor
Logger *slog.Logger
Metrics *telemetry.MetricsRegistry
// LLMSnapshot is the resolved snapshot the posture provider projects.
LLMSnapshot llm.ConfigSnapshot
// Core subsystem handles.
Tasks tasks.TaskRegistry
Sessions *sessions.Registry
Agents *agentregistry.Registry
Artifacts artifacts.ArtifactStore
Memory memory.MemoryStore
Catalog tools.ToolCatalog
Coordinator pauseresume.Coordinator
MCPRegistry *mcpdrv.Registry
MCPToolContext *mcpconsole.ToolContextStore
State state.StateStore
Skills skills.SkillStore
// Control-plane handles.
AgentConfig agentcfg.Registry
AgentConfigID string
SessionOverlay sessionoverlay.Store
RunsStore *runsprotocol.Store
// RunLoopDriver backs the tasks.get trajectory enricher. Nil leaves
// tasks reads un-enriched (a stack without a run loop).
RunLoopDriver *RunLoopDriver
OAuthProviders map[string]toolauth.OAuthProvider
// Governance handles. TenantOverridePolicy backs governance.set/get
// tenant overrides; KeyRotator backs governance.rotate_key. Both are
// built caller-side (the policy is shared with the run-loop driver; the
// rotator's lifecycle is the assembly's).
TenantOverridePolicy *governance.TenantOverridePolicy
// SetPosturePolicy backs governance.set_posture — the admin identity-tier
// policy WRITE surface (the StateStore-backed effective-policy record the
// posture read layers over). Built caller-side; nil leaves the
// set_posture route at 501 (the partial-build convention).
SetPosturePolicy *governance.SetPosturePolicy
KeyRotator *llm.ProviderKeyRotator
ValidModels []string
// MCPAttacher backs agent_config.add_mcp_connection. Built caller-side so
// its Close joins the caller's closer chain; nil leaves the add verb
// unwired.
MCPAttacher agentcfgprotocol.ConnectionAttacher
MCPStdioAllowlist []string
BootDeclaredMCP []string
// OAuthProviderInstaller backs agent_config.set_oauth_provider /
// remove_oauth_provider (the Protocol-installed, zero-URL broker-pull
// provider). Built caller-side; nil leaves the install verbs unwired (→ 501).
OAuthProviderInstaller agentcfgprotocol.ProviderInstaller
// BootDeclaredOAuth is the set of boot-declared OAuth provider names
// (tools.oauth_providers[].name); an install/uninstall of one of these is
// refused (boot wins).
BootDeclaredOAuth []string
// AllowWireOAuthDescriptor is the effective DEV-ONLY, fail-closed opt-in that
// permits set_oauth_provider / add_mcp_connection to carry a FULL OAuth
// provider binding over the wire (token_url / audience / scopes / remote{}).
// The caller passes (tools.allow_wire_oauth_descriptor) OR (the captured
// HARBOR_ALLOW_WIRE_OAUTH_DESCRIPTOR boot env). Default false / fail-closed.
AllowWireOAuthDescriptor bool
// AllowWireInjection is the effective DEV-ONLY, fail-closed opt-in that
// permits add_mcp_connection to carry a per-user credential-INJECTION mapping
// (the `injection` object) for a receiver-style MCP server over the wire. The
// caller passes (tools.allow_wire_injection) OR (the captured
// HARBOR_ALLOW_WIRE_INJECTION boot env). INDEPENDENT of
// AllowWireOAuthDescriptor. Default false / fail-closed.
AllowWireInjection bool
// LLMProviderInstaller backs agent_config.set_llm_provider (the
// Protocol-installed, zero-URL broker-pull inference provider). Built
// caller-side; nil leaves the verb unwired (→ 501).
LLMProviderInstaller agentcfgprotocol.LLMProviderInstaller
// InferenceBrokers is the set of boot-declared inference-broker names
// (llm.inference_brokers[].name); a set_llm_provider naming a broker outside
// this set is refused (→ 400 — no admin-writable field determines a sink).
InferenceBrokers []string
// Auth. Validator nil mounts the transports WithoutValidator (the
// explicit test-kit opt-out); AuthSurface nil leaves auth.rotate_token
// un-mounted (the production posture — no in-runtime token issuer).
Validator auth.Validator
AuthSurface *auth.RotateSurface
// Posture stamps.
DisplayName string
InstanceID string
BuildVersion string
BuildCommit string
// TopologyAvailable advertises the topology.snapshot capability (true
// only when the caller wired a topology accessor onto the surface).
TopologyAvailable bool
}
MuxInput carries the assembled subsystem handles and the caller-side posture stamps BuildMux mounts the Protocol surface over. Every handle is optional except the ControlSurface and the bus: a nil handle leaves the corresponding surface un-mounted (the route then 404s), exactly as the two callers already behave under their skip/absence paths.
type OAuthProviderInstaller ¶ added in v1.14.0
type OAuthProviderInstaller struct {
// contains filtered or unexported fields
}
OAuthProviderInstaller installs / uninstalls Protocol-installed providers by building them from the boot credential broker (auth.ProviderBuilder) and installing them owner-tagged into the shared auth.ProviderSet.
Concurrent reuse: builder + set + allowWireDescriptor are set once at construction; the builder + set are internally synchronised. The installer holds no mutable state.
func NewOAuthProviderInstaller ¶ added in v1.14.0
func NewOAuthProviderInstaller(builder *toolauth.ProviderBuilder, set toolauth.ProviderSet, allowWireDescriptor bool, logger *slog.Logger) *OAuthProviderInstaller
NewOAuthProviderInstaller builds the production installer. Both the builder and the set are mandatory; a nil either returns a nil installer so the caller leaves the install verbs unwired (→ 501 at the wire edge) rather than nil-panicking. allowWireDescriptor is the effective wire-descriptor opt-in (config flag OR boot env) — the kill-switch consulted on every build. A nil logger is replaced with the default so the kill-switch skip is never silent.
func (*OAuthProviderInstaller) InstallProvider ¶ added in v1.14.0
func (i *OAuthProviderInstaller) InstallProvider(ctx context.Context, tenant, agentID string, desc agentcfg.OAuthProviderDescriptor) error
InstallProvider resolves the descriptor's credential_broker against the boot broker set, builds the broker-pull provider, and installs it owner-tagged. Auth-package errors are wrapped into the agent-config service's client-error sentinels so the wire handler classifies them as 400s (an unknown broker / name collision is a bad request, not a server fault).
func (*OAuthProviderInstaller) InstalledFor ¶ added in v1.14.0
InstalledFor returns the owner's installed provider names — the owner-scoped reconcile view.
func (*OAuthProviderInstaller) UninstallProvider ¶ added in v1.14.0
func (i *OAuthProviderInstaller) UninstallProvider(ctx context.Context, tenant, agentID, name string) error
UninstallProvider removes the named provider from the owner-tagged set and CLOSES it (a still-bound connection's next call then fails loud). The (tenant, agentID) pair is the caller's resolved owner; the set refuses a cross-owner drop at its own boundary (ErrProviderOwnerCollision) — defense in depth independent of the handler's caller-side owner resolution.
type Options ¶
type Options struct {
ConfigPath string
// Config, when non-nil, is used directly instead of loading and
// validating ConfigPath — the entry point for a caller that already
// holds a validated (or programmatically-built) configuration.
// Boot re-runs the full-binary Validate on it so a hand-built config
// cannot bypass validation. When both Config and ConfigPath are set
// Config wins; when Config is nil ConfigPath is loaded.
Config *config.Config
Port int
BindAddr string
Logger *slog.Logger
Stderr io.Writer
SubcommandLabel string
// PreferConfigBindAddr opts into honoring the operator-configured
// `server.bind_addr` (which may name a non-loopback interface) when no
// explicit BindAddr override is given. ONLY the production serve caller
// sets it. The dev/console callers leave it false so they stay
// loopback-only on 127.0.0.1:<Port> — a dev boot against a serve-shaped
// yaml must never expose the dev-token stack off-box.
PreferConfigBindAddr bool
// AuthValidatorFactory is REQUIRED (nil fails Boot loud).
AuthValidatorFactory AuthValidatorFactory
// MCPDefaultIdentity is the identity a boot-time / runtime-added MCP
// connection dials under.
MCPDefaultIdentity identity.Identity
// DisplayName / InstanceID stamp the posture surface's runtime.info.
DisplayName string
InstanceID string
// BuildVersion / BuildCommit stamp runtime.info's build identity.
BuildVersion string
BuildCommit string
// DevAllowMock stamps the Serve start log's dev_allow_mock attribute —
// the dev caller sets it when the mock escape hatch fired so an operator
// reading the boot line sees the dev-only posture. A stamp only; the mock
// gate itself is caller policy inside BuildLLMSnapshot.
DevAllowMock bool
// RegisterCatalog forwards a compiled-tool registrar onto the
// assembly's pre-policy catalog seam (assemble.Options.RegisterCatalog):
// a tool it registers is wrapped with its declared approval / OAuth /
// policy shell before the run loop can dispatch it. Nil is a no-op —
// the stock serve caller passes nil; an external serving binary passes
// its agent's RegisterTools.
RegisterCatalog func(catalog tools.ToolCatalog) error
// Caller-side injection seams (all optional).
BuildLLMSnapshot LLMSnapshotBuilder
BuildAuthSurface AuthSurfaceBuilder
ExtraRoutes ExtraRoutesFunc
PostBoot PostBootFunc
}
Options bundles the inputs Boot consumes. ConfigPath (not a pre-loaded config) is carried so a hot-reload supervisor re-reads the file on each reboot by re-calling Boot with the same Options.
type PostBootFunc ¶
type PostBootFunc func(ctx context.Context, h PostBootHandles) error
PostBootFunc runs after the listener + surfaces are composed but before Serve. The dev caller uses it to seed fixture entities.
type PostBootHandles ¶
type PostBootHandles struct {
Sessions *sessions.Registry
Agents *agentregistry.Registry
Tasks tasks.TaskRegistry
Artifacts artifacts.ArtifactStore
Memory memory.MemoryStore
Tools tools.ToolCatalog
Flows *flow.Registry
Bus events.EventBus
Logger *slog.Logger
}
PostBootHandles carries the assembled subsystem handles the post-boot hook receives (the dev caller's fixture seeder writes through them).
type RouteMount ¶
type RouteMount struct {
Router *http.ServeMux
Validator auth.Validator
Bus events.EventBus
Redactor audit.Redactor
Logger *slog.Logger
// BindAddr is the resolved listen address (the bootstrap handler puts it
// in its connection envelope).
BindAddr string
}
RouteMount is handed to the ExtraRoutes seam so a caller can mount pre-CORS routes (draft scaffolding, the bootstrap-token endpoint, the Console static build) with access to the boot's validator, bus, and bound address. The caller returns any closers its routes registered (a draft store's Close).
type RunLoopDriver ¶
type RunLoopDriver struct {
// contains filtered or unexported fields
}
RunLoopDriver subscribes to `task.spawned` and drives a RunLoop per spawned foreground task. The driver is constructed by bootDevStack and Closed during stack teardown.
func NewRunLoopDriver ¶
func NewRunLoopDriver(opts RunLoopDriverOptions) (*RunLoopDriver, error)
NewRunLoopDriver validates the opts and returns a stopped driver. Call Start before serving; call Close to drain.
func (*RunLoopDriver) Close ¶
func (d *RunLoopDriver) Close(_ context.Context) error
Close cancels the subscription, waits for the subscribe-loop to drain, then waits for every in-flight RunLoop goroutine to return. Idempotent: a second Close walks no-ops. The supplied ctx is accepted for the closer-signature compatibility (closeFns takes a ctx); the driver's drain has its own bounded shape (every RunLoop observes d.subCtx cancellation and returns within one drain boundary). A pathological RunLoop that holds ctx-cancellation indefinitely would block Close; the dev cmd's serve loop applies the Server.ShutdownGracePeriod ceiling at the http boundary, so a blocked Close eventually surfaces as a graceless exit.
func (*RunLoopDriver) SessionOverridesStore ¶
func (d *RunLoopDriver) SessionOverridesStore() *runsprotocol.Store
SessionOverridesStore returns the pending-override Store the driver consumes at run start. It is the seam a caller wires the runs.set_overrides service to so a set reaches the run; exposed so a caller can assert the two share one Store.
func (*RunLoopDriver) Start ¶
func (d *RunLoopDriver) Start(ctx context.Context) error
Start opens the admin-scoped subscription and launches the subscribe-loop goroutine. Idempotent: a second Start is a no-op. The supplied ctx anchors the subscription's lifetime — when ctx cancels (e.g. boot was aborted before Close), the subscription cancels along with it.
func (*RunLoopDriver) TrajectoryByTaskID ¶
func (d *RunLoopDriver) TrajectoryByTaskID(taskID tasks.TaskID) *planner.Trajectory
TrajectoryByTaskID returns a defensive SNAPSHOT of a run's trajectory (its Query + a copy of the append-only Steps slice), or nil when the task's trajectory has been evicted or never existed. Safe for an out-of-band Protocol reader (the Enricher on a tasks.get) to call while the run is IN-FLIGHT: the snapshot is taken under the run's per-run trajMu — the SAME lock the steering RunLoop holds around each per-step append — so the returned Steps slice never observes a mid-append slice header (the data race the Enricher would otherwise hit against the run loop). Only the fields the Enricher reads (Query + Steps) are copied; the trajectory's shared maps are deliberately NOT aliased into the snapshot.
type RunLoopDriverOptions ¶
type RunLoopDriverOptions struct {
Logger *slog.Logger
Bus events.EventBus
RunLoop *steering.RunLoop
Planner planner.Planner
Tasks tasks.TaskRegistry // mandatory: the FSM the driver advances on Run exit
TaskKind tasks.TaskKind // KindForeground at V1; the driver spawns RunLoops for this kind
// driveBackground widens the driver to ALSO
// drive KindBackground tasks — the ones a planner-emitted SpawnTask
// creates. False (the default / legacy test path) keeps the
// foreground-only behaviour. Recursion is bounded at the spawn site
// by the dev executor's absolute_max_spawn_depth cap, not here.
DriveBackground bool
// RunContext consumer wiring. All three of
// memory / skillsDirectory / planningHints are OPTIONAL: a dev
// stack that did not open the respective subsystem hands nil; the
// driver projects the corresponding RunContext field to nil and
// the planner omits the wrapper. The skills
// surface is the `skills.Directory` — the bounded,
// pinned-then-recent, capability-filtered `<skills_context>`
// producer (the directory carries its own MaxEntries cap; the
// pre-111d SkillStore.Search + skillsContextMax pair is deleted).
Memory memory.MemoryStore
MemoryRecall memory.RecallSettings
SkillsDirectory *skills.Directory
PlanningHints *planner.PlanningHints
// tool dispatch + Catalog projection +
// Trajectory. The tool catalog is the shared catalog the rest of
// the dev stack already populated (in-process tools, MCP-discovered
// tools, etc.). MaxStepsRunLoop caps the runloop's outer step
// counter (separate from the planner-internal cap that goes onto
// react via PlannerConfig.MaxSteps).
Catalog tools.ToolCatalog
Executor steering.ToolExecutor
MaxStepsRunLoop int
// operator-declared GrantedScopes
// threaded into the per-run catalog view's CatalogFilter. Tools
// whose AuthScopes exceed this set are invisible to the planner.
// Nil / empty list means no scopes granted (the existing latent
// default before the plumb-through).
GrantedScopes []string
// the artifact store the multimodal
// materializer reads from. Required only when `task.InputArtifactIDs`
// is non-empty (text-only tasks never touch the store). A nil
// store with input artifacts on the task degrades gracefully —
// the materializer emits text-stub-only references the LLM
// routes via the catalog.
ArtifactStore artifacts.ArtifactStore
// trajectory compression. `tokenBudget`
// projects onto RunSpec.Base.Budget.TokenBudget (the per-run
// runtime budget — a run option, never
// planner state); `compression` is the assembly-built
// planner.CompressionRunner the runloop invokes at each step
// boundary when the budget is non-zero. Both zero/nil (the
// default) = compression off, byte-identical behaviour.
TokenBudget int
Compression *planner.CompressionRunner
// the per-agent attachment disposition policy
// decoded from `multimodal.disposition` (the middle precedence
// layer of the disposition resolution). Zero value = no agent
// policy; the runtime default applies.
DispositionPolicy planner.DispositionPolicy
// tenantOverrides resolves an admin-set tenant default for the run's
// LLM parameters at run start (model / extra-instructions /
// temperature / max-tokens / reasoning-effort). OPTIONAL — a nil
// resolver means "no tenant defaults to apply" (the run uses the
// agent/config defaults). When supplied, the driver reads it ONCE per
// run and pins the resolved snapshot into the run's RunContext so the
// swap lands on this run (next-turn relative to the admin's set), never
// mid-flight.
TenantOverrides TenantOverrideResolver
// sessionOverrides is the in-process pending-override Store the
// `runs.set_overrides` Service writes into. The driver Consumes the
// session's pending override at run start (one-shot) and composes it
// OVER the tenant default (session › tenant › config). OPTIONAL — a
// nil Store means "no session overrides" (the run uses tenant/config
// only). It is the SAME Store handed to the runs Service so a set and
// the consume meet.
SessionOverrides *runsprotocol.Store
// agentConfig resolves the agent's active config revision at run start
// (the agent-config control plane). OPTIONAL — a nil registry means
// "no agent-config projection" (the run uses every directory-visible
// skill). When supplied with a non-empty agentConfigID, the run reads
// the active revision ONCE at run start and pins the resolved
// skills-set into the per-run projection so a config edit applies on
// the NEXT run (next-turn-only), never mid-flight.
AgentConfig agentcfg.Registry
AgentConfigID string
// sessionOverlay resolves the SESSION-scoped safe-subset overlay (the
// non-admin lower tier) at run start: the session's user prompt layer,
// narrow-only source/tool disables (UNIONED into the admin exclusion set
// — can only narrow, never widen), and ephemeral personal skills. Keyed
// by the REAL (tenant, user, session) triple, so it is session-isolated.
// OPTIONAL — nil means "no session overlay" (the run uses the admin agent
// config only). The SAME store the session-safe Protocol verbs write into.
SessionOverlay sessionoverlay.Store
// runCompletionHook is the static `runtime.hooks.run_completion`
// projection (nil when unset). At run start the driver resolves the
// effective hook via the shared projection (agent-config over this yaml
// over none) and pins it into the RunSpec, so an edit lands next-run.
RunCompletionHook *steering.CompletionHookSpec
// connectionDetacher drives the DETACH leg of run-start reconciliation:
// at run start the driver detaches every MCP server that is attached but
// no longer declared by the agent's active config revision (a removed
// connection, or a rollback past an add), so the next run's projected
// catalog excludes it. OPTIONAL — nil means no reconcile (the
// backward-compatible path). Injected at the cmd/harbor boundary (it
// imports the concrete MCP driver; this driver stays driver-agnostic).
ConnectionDetacher projection.ConnectionDetacher
// bootDeclaredMCP is the set of boot-declared (yaml) MCP server names the
// reconcile MUST NEVER detach (they are not revisioned state). Nil/empty
// when no yaml server is declared.
BootDeclaredMCP map[string]struct{}
// OAuthProviderReconciler drives the run-start provider-reconcile leg: it
// makes the reconciling owner's installed OAuth providers match the current
// active revision (a rollback past an install uninstalls+closes; a rollback
// of a removal re-installs). OPTIONAL — nil means no provider reconcile.
// Owner-scoped exactly like the connection reconcile.
OAuthProviderReconciler projection.OAuthProviderReconciler
// namingDefault is the static `runtime.naming` fleet-default auto-naming
// policy; the driver resolves the effective policy per run (agent-config
// over this yaml over off). Opt-in, default off.
NamingDefault config.RuntimeNamingConfig
// sessionTitler is the session-registry seam the auto-naming trigger
// writes/reads through (RecordCompletedTurn / AutoNamingState /
// SetTitleAuto). The same *sessions.Registry the sessions Protocol routes
// project over. Nil ⇒ no auto-naming trigger even when a policy resolves.
SessionTitler steering.SessionTitler
// namingLLM is the run's wrapped LLM client the ONE naming Complete call
// flows through (governance/safety via ctx identity). Nil ⇒ no auto-naming
// trigger.
NamingLLM steering.NamingCompleter
}
RunLoopDriverOptions bundles the dependencies the driver consumes. Bus + RunLoop + Planner + TaskRegistry are all mandatory; a nil any of them returns ErrRunLoopDriverMisconfigured from NewRunLoopDriver. The TaskRegistry is what the driver calls MarkRunning / MarkComplete / MarkFailed on to advance the FSM (closes issue #123).
type SessionEnsurerAdapter ¶
type SessionEnsurerAdapter struct {
// contains filtered or unexported fields
}
SessionEnsurerAdapter wraps a *sessions.Registry as a protocol.SessionEnsurer. Immutable after construction; the wrapped registry is itself concurrency-safe, so the adapter is too.
func NewSessionEnsurerAdapter ¶
func NewSessionEnsurerAdapter(reg *sessions.Registry) *SessionEnsurerAdapter
NewSessionEnsurerAdapter builds the adapter. A nil registry would be a wiring bug; the caller (cmd_dev bootDevStack) always passes the constructed registry.
func (*SessionEnsurerAdapter) EnsureSession ¶
EnsureSession implements protocol.SessionEnsurer. It calls the registry's create-on-first-use EnsureOpen and translates the registry's sentinels into the protocol-side sentinels so the ControlSurface's mapSessionEnsureError reaches a stable Protocol code. An unclassified error is wrapped (not swallowed) so the surface's catch-all maps it to CodeRuntimeError (CLAUDE.md §5 — fail loud).
type TenantOverrideResolver ¶
type TenantOverrideResolver interface {
Get(ctx context.Context, tenant string) (governance.TenantOverrideSpec, bool, error)
}
TenantOverrideResolver is the narrow read seam the run loop uses to resolve an admin-set tenant default at run start. The `*governance.TenantOverridePolicy` concrete satisfies it.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package external is the production-only entry point behind the public sdk/server facade: it turns a validated configuration into a running Harbor Protocol server that an external Go binary can host, with its compiled in-process tools wrapped at the pre-policy catalog seam.
|
Package external is the production-only entry point behind the public sdk/server facade: it turns a validated configuration into a running Harbor Protocol server that an external Go binary can host, with its compiled in-process tools wrapped at the pre-policy catalog seam. |