Documentation
¶
Index ¶
- Constants
- Variables
- type AuthVerifier
- type Checkpointer
- type Clock
- type Host
- func (h *Host) ApplyDeadline() time.Duration
- func (h *Host) Auth() AuthVerifier
- func (h *Host) Capacity() uint64
- func (h *Host) ClaimTTL() time.Duration
- func (h *Host) Clock() Clock
- func (h *Host) CommandQueueSize() int
- func (h *Host) Department() *department.Department
- func (h *Host) FixedSessionID() sessionwire.SessionID
- func (h *Host) ID() sessionwire.HostID
- func (h *Host) InternalEndpoint() sessionwire.InternalEndpoint
- func (h *Host) IsolationClass() sessionwire.HostIsolationClass
- func (h *Host) Placement() sessionwire.HostPlacement
- func (h *Host) ReconcileBatch() int
- func (h *Host) ReconcileInterval() time.Duration
- func (h *Host) RegistryExpiry() time.Duration
- func (h *Host) RegistryHeartbeat() time.Duration
- func (h *Host) SessionStore() SessionStore
- func (h *Host) WarmTTL() time.Duration
- func (h *Host) Workspaces() WorkspaceProvider
- type InvalidOptionsError
- type OptionErrorCode
- type Options
- type Registrar
- type RegistrarFunc
- type SessionStore
- type WorkspaceProvider
Constants ¶
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.
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.
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.
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 ¶
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 ¶
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 ¶
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 ¶
ApplyDeadline returns the outer bound for applying an admitted command.
func (*Host) CommandQueueSize ¶
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) 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 ¶
ReconcileBatch returns the bound on one reconciliation pass.
func (*Host) ReconcileInterval ¶
ReconcileInterval returns how often reconciliation runs.
func (*Host) RegistryExpiry ¶
RegistryExpiry returns how long an advertisement survives without a refresh.
func (*Host) RegistryHeartbeat ¶
RegistryHeartbeat returns the advertisement refresh interval.
func (*Host) SessionStore ¶
func (h *Host) SessionStore() SessionStore
SessionStore returns the durable session state collaborator.
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 ¶
func (f RegistrarFunc) Register(ctx context.Context) ([]department.Registration, error)
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.