Help us shape the future of Go! Take the 2026 Go Developer Survey

hostconfig

package
v0.15.1 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Index

Constants

View Source
const MaxCommandQueueSize = 65536

MaxCommandQueueSize bounds the command queue. A queue is bounded because an unbounded one converts backpressure into memory growth and turns a slow consumer into an OOM three minutes later, which is harder to diagnose than a refusal.

View Source
const MaxReconcileBatch = 4096

MaxReconcileBatch bounds one reconciliation pass, so a Host that has drifted far does not attempt to converge in a single unbounded sweep.

View Source
const MinClaimAttemptsBeforeDeadline = 2

MinClaimAttemptsBeforeDeadline is how many claim lifetimes must fit inside the apply deadline.

A claim that lapses mid-apply is retried by another Host. If only one claim lifetime fits inside the deadline, the first lapse spends the entire budget and the command misses its deadline with no attempt left; two means a lapsed claim still has a whole attempt in front of it.

THIS IS STRICTER THAN THE RUNBOOK, and the difference is not derived from anything written down. 04-host.md says "claim TTL below apply deadline" — merely below — and reserves the words "safely below" for the heartbeat rule alone. That asymmetry is deliberate on the runbook's part, so this constant is an engineering choice and not a reading of the spec: it will REFUSE DEPLOYMENTS THE RUNBOOK PERMITS, such as a 30s claim under a 45s deadline.

The failure model it is chosen against: at 1.5x, a claim that lapses at the end of its life leaves half an attempt before the deadline, so the retry is guaranteed to be cut off and the command fails having consumed two Hosts. At 2x the retry has a full attempt. If a deployment needs the looser rule the runbook allows, change THIS CONSTANT — do not weaken validateTiming, which is what makes the boundary testable.

View Source
const MinHeartbeatsBeforeExpiry = 3

MinHeartbeatsBeforeExpiry is how many heartbeat intervals must fit inside the registry expiry.

"Safely below" is a MARGIN, not `<`, and the difference is the whole rule. A heartbeat one nanosecond under the expiry satisfies `<` and loses the registry entry on the first scheduling hiccup, GC pause or network retry — which is to say, immediately, in production. Three means two consecutive heartbeats may be late or lost before Factory stops seeing this Host, which is the smallest number that tolerates a single miss AND the retry of it.

Variables

View Source
var ErrUndiallableEndpoint = errors.New("host: the internal endpoint's authority cannot be dialled")

ErrUndiallableEndpoint is the cause of an InternalEndpoint refusal whose authority no client could dial — an empty port ("ws://h:"), a port outside 1..65535 ("ws://h:99999", "ws://h:0"), or more than one port ("ws://h:80:90") — although Core's InternalEndpoint.Validate and HostLinkEndpoint both accept it. Reach it with errors.Is.

Functions

This section is empty.

Types

type AuthVerifier

type AuthVerifier interface {
	// VerifyTenant reports whether a presented credential acts for the tenant.
	VerifyTenant(context.Context, sessionwire.TenantID, string) error
}

AuthVerifier authenticates an inbound HostLink or command caller.

type Checkpointer

type Checkpointer interface {
	Checkpoint(ctx context.Context, tenant sessionwire.TenantID, session sessionwire.SessionID) error
}

Checkpointer commits the checkpoints a nonterminal release requires.

IT HAS NO IMPLEMENTATION IN THIS MODULE AND THAT IS A STATEMENT ABOUT WHERE THE KNOWLEDGE LIVES, not an omission to be filled in with a no-op. A release checkpoint is the runtime's conversation state and the workspace's contents, and Host holds neither: department.Runtime exposes no checkpoint method and WorkspaceProvider materializes a workspace without ever committing one. A composition that silently treated the step as done would report a drained Host whose sessions had lost whatever was not already durable, which is the one drain failure that destroys work.

So the seam is REQUIRED rather than optional. A Host composed without one does not start.

type Clock

type Clock interface {
	Now() time.Time
	NewTimer(time.Duration) *time.Timer
}

Clock is the time source.

It is FORWARD-DECLARED for O2.x's residency, heartbeat and drain timers, which is why NewTimer exists with no caller yet. An earlier version of this comment justified it as making "the timing rules below" testable without sleeping; those rules are pure arithmetic over the Options and never consult a clock, so the justification described a task that had not landed. Requiring it now is deliberate — a Host that reaches residency without a Clock would have to be reconstructed — but it is a forward dependency and says so.

type Host

type Host struct {
	// contains filtered or unexported fields
}

Host is the tenant-scoped Department runtime host.

It holds a RESOLVED configuration: every option was validated once at construction and nothing here changes afterwards. Residency, command consumption, HostLink and drain are separate tasks; what this type fixes is that a Host which exists is a Host whose configuration was coherent.

The fields are unexported and the accessors return values. Nothing mutable is handed out, and nothing the caller still holds can reach in: Options is taken BY VALUE, so a caller mutating its own copy afterwards changes nothing here. The two reference-typed fields are deliberate exceptions with reasons rather than oversights — Department is immutable by construction, so copying it would buy nothing, and the collaborator interfaces must be shared to be useful at all. Copying a SessionStore is not a defence; it is a different SessionStore.

func New

func New(options Options) (*Host, error)

New validates options and returns a Host.

It returns *InvalidOptionsError, carrying a stable OptionErrorCode, for every refusal. A Host with an incoherent configuration is never returned: there is no partially valid Host and no package-global default to fall back on, so a caller that ignores the error has nothing to use.

func (*Host) ApplyDeadline

func (h *Host) ApplyDeadline() time.Duration

ApplyDeadline returns the outer bound for applying an admitted command.

func (*Host) Auth

func (h *Host) Auth() AuthVerifier

Auth returns the credential verifier.

func (*Host) Capacity

func (h *Host) Capacity() uint64

Capacity returns how many sessions this Host may hold resident.

func (*Host) ClaimTTL

func (h *Host) ClaimTTL() time.Duration

ClaimTTL returns how long a command claim is held.

func (*Host) Clock

func (h *Host) Clock() Clock

Clock returns the time source.

func (*Host) CommandQueueSize

func (h *Host) CommandQueueSize() int

CommandQueueSize returns the inbound command buffer size.

func (*Host) Department

func (h *Host) Department() *department.Department

Department returns the immutable set of launch targets this Host serves.

func (*Host) FixedSessionID

func (h *Host) FixedSessionID() sessionwire.SessionID

FixedSessionID returns the session a dedicated Host is bound to, or empty for a pooled Host.

func (*Host) ID

func (h *Host) ID() sessionwire.HostID

ID returns the Host's identity.

func (*Host) InternalEndpoint

func (h *Host) InternalEndpoint() sessionwire.InternalEndpoint

InternalEndpoint returns the HostLink base address this Host advertises.

func (*Host) IsolationClass

func (h *Host) IsolationClass() sessionwire.HostIsolationClass

IsolationClass returns the advertised cross-tenant boundary, which is also this Host's pooled admission rule. There is no TenantID accessor beside it: see Options.IsolationClass for H8.

func (*Host) Placement

func (h *Host) Placement() sessionwire.HostPlacement

Placement returns the admission model this Host runs under.

func (*Host) ReconcileBatch

func (h *Host) ReconcileBatch() int

ReconcileBatch returns the bound on one reconciliation pass.

func (*Host) ReconcileInterval

func (h *Host) ReconcileInterval() time.Duration

ReconcileInterval returns how often reconciliation runs.

func (*Host) RegistryExpiry

func (h *Host) RegistryExpiry() time.Duration

RegistryExpiry returns how long an advertisement survives without a refresh.

func (*Host) RegistryHeartbeat

func (h *Host) RegistryHeartbeat() time.Duration

RegistryHeartbeat returns the advertisement refresh interval.

func (*Host) SessionStore

func (h *Host) SessionStore() SessionStore

SessionStore returns the durable session state collaborator.

func (*Host) WarmTTL

func (h *Host) WarmTTL() time.Duration

WarmTTL returns how long a released session stays warm before drain.

func (*Host) Workspaces

func (h *Host) Workspaces() WorkspaceProvider

Workspaces returns the workspace collaborator. It does not checkpoint; see WorkspaceProvider.

type InvalidOptionsError

type InvalidOptionsError struct {
	Code   OptionErrorCode
	Field  string
	Reason string
	Cause  error
}

InvalidOptionsError reports an option Host may not run with.

Code is what a caller branches on, Field names the option, Reason is for a person, and Cause carries the typed error a lower layer produced — Core's *RequestValidationError for an endpoint, for instance — so errors.As reaches it instead of a caller substring-matching a sentence.

func (*InvalidOptionsError) Error

func (e *InvalidOptionsError) Error() string

func (*InvalidOptionsError) Unwrap

func (e *InvalidOptionsError) Unwrap() error

Unwrap returns the typed error this one was built from, if any.

type OptionErrorCode

type OptionErrorCode string

OptionErrorCode is a stable, machine-readable reason an option was refused.

It exists for the reason department.DefinitionErrorCode does: Reason is free text a caller must not branch on, and a constructor that refuses a dozen ways is useless to a caller who cannot tell which one happened without matching a sentence that will be reworded.

const (
	OptionErrorCodeMissing         OptionErrorCode = "missing"
	OptionErrorCodeInvalid         OptionErrorCode = "invalid"
	OptionErrorCodeUnknownEnum     OptionErrorCode = "unknown_enum"
	OptionErrorCodeNotPositive     OptionErrorCode = "not_positive"
	OptionErrorCodeAboveBound      OptionErrorCode = "above_bound"
	OptionErrorCodeHeartbeatMargin OptionErrorCode = "heartbeat_margin"
	OptionErrorCodeClaimMargin     OptionErrorCode = "claim_margin"
	OptionErrorCodeDedicatedFixed  OptionErrorCode = "dedicated_requires_fixed_session"
	OptionErrorCodeDedicatedCap    OptionErrorCode = "dedicated_requires_capacity_one"
	OptionErrorCodePooledFixed     OptionErrorCode = "pooled_rejects_fixed_session"
)

type Options

type Options struct {
	// HostID identifies this Host to Factory and in HostLink bindings.
	HostID sessionwire.HostID

	// InternalEndpoint is the credential-free HostLink BASE address Factory
	// derives each tenant's address from; see validateEndpointBase.
	InternalEndpoint sessionwire.InternalEndpoint

	// IsolationClass is advertised with capacity so Factory can place
	// cross-tenant work correctly, AND IT IS THIS HOST'S POOLED ADMISSION RULE.
	// Pooled placement alone does not establish the boundary.
	//
	// THERE IS NO TenantID BESIDE IT, and that is human gate H8, answered
	// 2026-09-04 as option (a). A Host used to be constructed with a fixed
	// tenant and the residency manager refused every request naming another, so
	// a pooled deployment needed one Host Deployment per tenant and this field
	// — which spec §12 says decides the question — had no reader that could
	// matter. A Host advertising cross_tenant_isolated may hold several tenants
	// resident; one advertising tenant_exclusive is exclusive by PLACEMENT, not
	// by construction, which is §12's own wording: "Factory placement enforces
	// the restriction". internal/service's admission ledger holds the local
	// backstop for it, atomically, because the ledger's lock is the only
	// Host-wide critical section an admission passes through.
	IsolationClass sessionwire.HostIsolationClass

	// Department is the immutable set of launch targets this Host serves.
	Department *department.Department

	// SessionStore, Workspaces, Clock and Auth are Host's collaborators.
	SessionStore SessionStore
	Workspaces   WorkspaceProvider
	Clock        Clock
	Auth         AuthVerifier

	// Placement is the admission model this Host runs under.
	Placement sessionwire.HostPlacement

	// Capacity is how many sessions this Host may hold resident. Dedicated
	// placement requires exactly one.
	Capacity uint64

	// FixedSessionID binds a dedicated Host to one session for its lifetime. It
	// is REQUIRED for dedicated placement and REJECTED for pooled.
	FixedSessionID sessionwire.SessionID

	// WarmTTL is how long a POOLED session stays resident after it is observed
	// whole-session idle before this Host warm-releases it: nonterminally, so
	// the next command re-places and restores it. A session with a gate open,
	// a turn in flight or durable command work outstanding is never released.
	// A dedicated Host does not warm-release; its controller's drain ends the
	// session.
	WarmTTL time.Duration

	// RegistryHeartbeat and RegistryExpiry are the advertisement lifetime.
	RegistryHeartbeat time.Duration
	RegistryExpiry    time.Duration

	// ClaimTTL and ApplyDeadline bound command application.
	ClaimTTL      time.Duration
	ApplyDeadline time.Duration

	// CommandQueueSize is the inbound command buffer.
	CommandQueueSize int

	// ReconcileInterval and ReconcileBatch bound reconciliation.
	ReconcileInterval time.Duration
	ReconcileBatch    int
}

Options configures a Host. Every field is required unless its documentation says otherwise; nothing here is discovered, and there is no package-global default Host to fall back on.

type Registrar

type Registrar interface {
	Register(context.Context) ([]department.Registration, error)
}

Registrar produces the Department registrations one deployment serves.

IT IS CALLED ONCE, AT STARTUP, AND ITS RESULT IS IMMUTABLE. Department.New validates the registrations and fixes them for the life of the process, so a Registrar that answered differently later would be answering a question nobody asks again. It takes a context because building a launch target may require I/O — reading a model catalogue, resolving a runtime build — and a startup that can block must be cancellable.

type RegistrarFunc

type RegistrarFunc func(context.Context) ([]department.Registration, error)

RegistrarFunc adapts a function to Registrar.

func (RegistrarFunc) Register

Register calls f.

type SessionStore

type SessionStore interface {
	// LoadSession returns the durable record for a session, or an error the
	// caller can classify.
	LoadSession(context.Context, sessionwire.TenantID, sessionwire.SessionID) ([]byte, error)
}

SessionStore is the durable session state Host reads and writes.

type WorkspaceProvider

type WorkspaceProvider interface {
	// EnsureWorkspace makes a workspace available and returns its root.
	EnsureWorkspace(context.Context, sessionwire.TenantID, sessionwire.SessionID) (string, error)
}

WorkspaceProvider materializes session workspaces.

It does NOT checkpoint them. An earlier version of this comment said it did, which is the doc-versus-reality drift host/CLAUDE.md exists to stop and the third time this lane has shipped a comment promising more than the code holds. Checkpointing arrives with O4.x/O6.x and will be its own method or its own interface; until then this says only what EnsureWorkspace does.

Jump to

Keyboard shortcuts

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