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

v1

package
v0.13.2 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: Apache-2.0 Imports: 16 Imported by: 22

Documentation

Overview

Package v1 defines version 1 of Looprig's transport-neutral session wire contract.

It owns the shared identity and protocol-version vocabulary without coupling the contract to a particular host, transport, broker, or user interface.

A Host's advertised internal_endpoint (HostLinkCapacityReport, HostLinkRegistryObservation) is a BASE: a ws or wss scheme and an authority. A Host serves one HostLink per tenant, and the per-tenant HostLink address is derived with HostLinkEndpoint(base, tenant); a Factory holds one link per (Host, tenant) pair. The records carry no marker saying whether an endpoint is a base, and must not grow one, because their decoders are strict.

Compatibility window: host v0.2.1 advertises its configured endpoint verbatim, and working deployments configured a per-tenant address because an older Factory dials the advertised endpoint verbatim. A Host that advertises a base, dialled that way, answers 404; a deriving Factory refuses a per-tenant endpoint with code base_names_tenant (base_not_bare behind an ingress path prefix). A host v0.2.1 reconfigured to advertise a bare base already works with a deriving Factory, with no Host upgrade. The advertised value and the Factory move together.

Index

Examples

Constants

View Source
const (
	// HostLinkMethodBind carries a HostLinkBindRequest.
	HostLinkMethodBind = "hostlink.bind"
	// HostLinkMethodUnbind carries a HostLinkUnbindRequest.
	HostLinkMethodUnbind = "hostlink.unbind"
	// HostLinkMethodAttach carries a HostLinkAttachRequest.
	HostLinkMethodAttach = "hostlink.attach"
	// HostLinkMethodDrain carries a HostLinkDrainRequest and initiates a drain.
	HostLinkMethodDrain = "hostlink.drain"
	// HostLinkMethodDrainStatus observes a drain without initiating one.
	HostLinkMethodDrainStatus = "hostlink.drain_status"
)
  • Connect framing
  • Capability tokens

HostLink framing names. These are plain strings shared by the Factory and Host ends of a HostLink; no transport type or adapter enters Core.

Connect framing

A HostLink begins with one version negotiation, carried in the transport's connect exchange. Its framing is frozen here because the two ends once disagreed on it: Factory wrapped both records as {"version_negotiation":{...}} while Host sent and expected them bare. That disagreement had two failure modes, one behind the other. Against a shipped Host the connect failed LOUDLY: Host's strict request decoder refused the wrapped Data as an unknown member and disconnected with "unsupported wire version". Had a Host accepted the wrapper instead, the failure would have been SILENT on Factory's side: its wrapped decoder reads the bare reply {"version":1} through a struct expecting the wrapper, finds no version_negotiation member, and sees version 0. Both modes are pinned as refusals by the helpers below.

The capability signal is deliberately one-directional. Every reserved HostLink method is Factory→Host, so only the reply carries hostlink_methods; there is no Host-side gate a Factory→Host capability would inform. It also could not be added additively: VersionNegotiationRequest is STRICT, so a new request member is refused by every Host already shipped, before the Factory can learn that Host's version. If a request-side capability is ever wanted, the Host-side request decoder must first be relaxed to tolerate it in an earlier release, and only then may Factories begin emitting it.

  • The connect request's Data is the BARE VersionNegotiationRequest: {"supported_versions":[1]}. No wrapper, no envelope member.
  • The connect reply's Data is the BARE VersionNegotiationResponse: {"version":1} at minimum, plus "hostlink_methods" when the Host advertises capabilities.

EncodeHostLinkConnectRequest, DecodeHostLinkConnectRequest, EncodeHostLinkConnectReply and DecodeHostLinkConnectReply are those two shapes by name. They carry no transport type: each takes or returns the Core record and the raw bytes a transport's connect event or reply carries. Either end may equally call encoding/json on the record directly; the helpers exist so the framing is a named API a test can pin rather than a convention a module can drift from.

A HostLink RPC names either a reserved method or a channel. The reserved methods below are matched exactly, and none begins with HostLinkChannelPrefix, so a receiver can tell a method from a channel without parsing either.

COMMAND DELIVERY HAS NO METHOD NAME OF ITS OWN: its RPC method IS the bound session's channel, HostLinkChannel(tenant, session), and its body is a HostLinkCommandDelivery. Any method that is not reserved is resolved as a channel against the routes the link holds, so a delivery names its binding by the method alone.

Capability tokens

hostlink_methods (see VersionNegotiationResponse) carries two different kinds of string: reserved RPC method names, above, AND capability tokens such as HostLinkCapabilityGateResponse. A token is never dispatched — it names no method and is never sent as one, and a token routed as an RPC falls to the same channel arm any other unrecognised name would. It is read only with Supports/HostLinkMethods, to answer a question a method name alone cannot: "can this peer do X", independent of any single request.

  • hostlink.command.<kind> means the Host applies runtime command kind <kind> (a runtimecommand.Kind) through the disposition path. The v0.1.0 baseline kinds — input, interrupt and restore — are not advertised: every shipped Host already applies them, so the token would add a check with no Host it could ever be false against.
  • hostlink.<area>.<feature> is for a capability that is not a command kind. It is added only when a Factory actually branches on it — advertising a feature nothing reads is dead signalling. HostLinkCapabilityAttributionPrincipal ("hostlink.attribution.principal") is such a feature, in the attribution area.

Every token a Factory gates on must be named as a Core constant before either end ships it, the same discipline as a reserved method: a token invented ad hoc by one side cannot be relied on by the other. A token must equal none of the HostLinkMethod* constants and must not begin with HostLinkChannelPrefix ("hostlink.v1."), so it can never be mistaken for a method or a channel.

View Source
const (
	MaxMetadataFields     = 16
	MaxMetadataKeyBytes   = 64
	MaxMetadataValueBytes = 1024
	MaxMetadataBytes      = 4096
)
View Source
const HostLinkCapabilityAttributionPrincipal = "hostlink.attribution.principal"

HostLinkCapabilityAttributionPrincipal is advertised by a Host that reads principal on all five command kinds and metadata on create and input. One token covers both fields because they ship in one Host release. A Factory must gate commands carrying either member on the owner's Supports result; it must never infer the capability from a Host version or lease epoch.

View Source
const HostLinkCapabilityGateResponse = "hostlink.command.gate_response"

HostLinkCapabilityGateResponse is advertised in hostlink_methods by a Host that applies the gate_response runtime command through the disposition path (host >= v0.4.0, harness >= v0.35.0). It is a capability token, not an RPC method: nothing dispatches on it, and a Factory must not admit a gate_response for a Host whose reply does not Support it.

View Source
const HostLinkCapabilityPayloadReference = "hostlink.payload.reference"

HostLinkCapabilityPayloadReference is advertised by a Host that applies a command whose private body is stored by reference (an inbox record carrying PayloadRef, written by Factory for a body above SessionStore's inline bound). Such a Host reads the object from the session's scope, verifies its size and digest, and applies it exactly as an inline body. A Factory must place or wake a session holding a pending referenced body only on a Host whose reply Supports this token; an incapable Host refuses the body and the command would never settle.

View Source
const HostLinkChannelPrefix = "hostlink.v1."

HostLinkChannelPrefix is the leading segment of every HostLink session channel.

View Source
const HostLinkPathPrefix = "/hostlink/"

HostLinkPathPrefix is the path under which a Host serves each tenant's HostLink: a tenant's link is HostLinkPathPrefix followed by exactly one path segment naming that tenant. It is the value host v0.2.1 serves at.

View Source
const MaxIDBytes = 256

MaxIDBytes is the largest permitted UTF-8 byte length of a sessionwire identity. IDs are opaque strings: this limit applies to encoded bytes, not runes, and no UUID grammar is implied.

Variables

This section is empty.

Functions

func CanonicalDeadlineUnixMillis

func CanonicalDeadlineUnixMillis(deadline time.Time) string

CanonicalDeadlineUnixMillis returns deadline as a canonical signed count of Unix milliseconds: zero is "0", negatives use a single leading '-', and positive values have no leading zeroes.

func EncodeHostLinkConnectReply added in v0.9.0

func EncodeHostLinkConnectReply(response VersionNegotiationResponse) ([]byte, error)

EncodeHostLinkConnectReply returns the bytes a HostLink connect reply carries as its Data: the bare VersionNegotiationResponse, with hostlink_methods present only when the Host advertises capabilities.

func EncodeHostLinkConnectRequest added in v0.9.0

func EncodeHostLinkConnectRequest(request VersionNegotiationRequest) ([]byte, error)

EncodeHostLinkConnectRequest returns the bytes a HostLink connect carries as its Data: the bare VersionNegotiationRequest. It refuses an offer that VersionNegotiationRequest.Validate refuses.

func HostLinkChannel added in v0.8.0

func HostLinkChannel(tenantID TenantID, sessionID SessionID) string

HostLinkChannel returns the channel one session's HostLink publications and command deliveries travel on: HostLinkChannelPrefix, the tenant ID, a '.', and the session ID, each identifier base64url-encoded WITHOUT padding (RFC 4648 section 5 alphabet).

The encoding makes the mapping injective: identifiers are opaque and may contain '.', so plain concatenation would let two distinct sessions share a channel. The result's alphabet is A-Za-z0-9-_ plus the prefix and separator.

It does not validate its arguments; callers validate identifiers where they accept them.

Types

type AgentCapabilitySummary

type AgentCapabilitySummary struct {
	AgentID                AgentID  `json:"agent_id"`
	RuntimeCompatibilityID string   `json:"runtime_compatibility_id"`
	Capabilities           []string `json:"capabilities,omitempty"`
	// contains filtered or unexported fields
}

AgentCapabilitySummary advertises one Department member's stable identity and explicit runtime compatibility boundary without exposing launch internals.

func (AgentCapabilitySummary) AdditionalFields

func (s AgentCapabilitySummary) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible capability members.

func (AgentCapabilitySummary) MarshalJSON

func (s AgentCapabilitySummary) MarshalJSON() ([]byte, error)

func (*AgentCapabilitySummary) UnmarshalJSON

func (s *AgentCapabilitySummary) UnmarshalJSON(data []byte) error

func (AgentCapabilitySummary) Validate

func (s AgentCapabilitySummary) Validate() error

Validate reports whether the capability summary identifies a launchable agent.

type AgentID

type AgentID string

AgentID identifies an agent participating in a session.

func (AgentID) Validate

func (id AgentID) Validate() error

Validate reports whether id is a permitted opaque agent identity.

type CommandEnvelope

type CommandEnvelope struct {
	Version   WireVersion `json:"version"`
	CommandID CommandID   `json:"command_id"`
}

CommandEnvelope is common to every state-changing V1 request. CommandID is client-generated and retry-stable; Version is the Looprig wire version, not a transport or broker version.

func (CommandEnvelope) Validate

func (e CommandEnvelope) Validate() error

Validate reports whether the envelope can participate in V1 durable command admission.

type CommandID

type CommandID string

CommandID identifies a client command. It is an opaque, non-empty UTF-8 string of at most MaxIDBytes bytes; in particular, it is not a UUID.

func DeterministicCommandID

func DeterministicCommandID(domain string, fields ...string) (CommandID, error)

DeterministicCommandID derives a canonical CommandID from a domain followed by zero or more fields. The domain is field zero in the framed input. All fields must be valid UTF-8; their raw UTF-8 bytes, without Unicode normalization, determine the result.

func (*CommandID) UnmarshalJSON

func (id *CommandID) UnmarshalJSON(data []byte) error

UnmarshalJSON rejects malformed JSON string encodings before they can be normalized into a different command identity by encoding/json.

func (CommandID) Validate

func (id CommandID) Validate() error

Validate reports whether id is a permitted opaque command identity.

type CommandIDFramingCode

type CommandIDFramingCode string

CommandIDFramingCode is a stable machine-readable reason a deterministic command ID cannot be framed.

const (
	// CommandIDFramingCodeTooManyFields reports a field count larger than a
	// uint32 can represent.
	CommandIDFramingCodeTooManyFields CommandIDFramingCode = "too_many_fields"
	// CommandIDFramingCodeFieldTooLong reports a UTF-8 field larger than a
	// uint32 can represent.
	CommandIDFramingCodeFieldTooLong CommandIDFramingCode = "field_too_long"
)

type CommandIDFramingError

type CommandIDFramingError struct {
	Code CommandIDFramingCode
}

CommandIDFramingError reports a deterministic command ID input that cannot be represented by the version 1 framing. Code is stable for callers that need to distinguish framing failures without matching Error.

func (*CommandIDFramingError) Error

func (e *CommandIDFramingError) Error() string

type CommandState

type CommandState string

CommandState records the durable command-admission lifecycle visible to a caller. Accepted means the inbox commit succeeded; it does not promise that a Host has already applied the command.

const (
	CommandStateAccepted CommandState = "accepted"
	CommandStatePending  CommandState = "pending"
	CommandStateApplied  CommandState = "applied"
	CommandStateRejected CommandState = "rejected"
)

type CommandStatus

type CommandStatus struct {
	CommandID     CommandID    `json:"command_id"`
	State         CommandState `json:"status"`
	AcceptedOrder uint64       `json:"accepted_order,omitempty"`
	Error         *ErrorDetail `json:"error,omitempty"`
	// contains filtered or unexported fields
}

CommandStatus is the forward-compatible public representation of an accepted command. Rejected commands carry a stable ErrorDetail rather than a provider or runtime cause.

func (CommandStatus) AdditionalFields

func (s CommandStatus) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible response members.

func (CommandStatus) MarshalJSON

func (s CommandStatus) MarshalJSON() ([]byte, error)

func (*CommandStatus) UnmarshalJSON

func (s *CommandStatus) UnmarshalJSON(data []byte) error

func (CommandStatus) Validate

func (s CommandStatus) Validate() error

Validate reports whether the public command state is structurally coherent.

type CreateRequest

type CreateRequest struct {
	CommandEnvelope
	SessionID SessionID       `json:"session_id"`
	AgentID   AgentID         `json:"agent_id"`
	Blocks    json.RawMessage `json:"blocks,omitempty"`
	Metadata  MessageMetadata `json:"metadata,omitempty"`
	Principal *Principal      `json:"principal,omitempty"`
}

CreateRequest admits a new durable session. SessionID is supplied by the client so a retry after an unknown outcome can reuse the same (SessionID, CommandID) pair; it is intentionally not server-minted here. Metadata is client-supplied; Principal is the optional Factory-stamped sender.

func (*CreateRequest) UnmarshalJSON

func (r *CreateRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON strictly decodes a create request. State-changing request records fail closed on unknown, duplicate, missing, or structurally invalid required members.

func (CreateRequest) Validate

func (r CreateRequest) Validate() error

Validate reports whether the create request has the identities needed for retry-safe admission. Blocks are optional for an idle create.

type Cursor

type Cursor string

Cursor is an opaque, server-issued page token. Clients may retain and return it but must not derive ordering, tenancy, or authority from its contents.

type DepartmentCapabilitySummary

type DepartmentCapabilitySummary struct {
	Agents []AgentCapabilitySummary `json:"agents"`
	// contains filtered or unexported fields
}

DepartmentCapabilitySummary is the transport-neutral capability discovery response for a Host Department or a Factory projection of one.

func (DepartmentCapabilitySummary) AdditionalFields

func (s DepartmentCapabilitySummary) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible department members.

func (DepartmentCapabilitySummary) MarshalJSON

func (s DepartmentCapabilitySummary) MarshalJSON() ([]byte, error)

func (*DepartmentCapabilitySummary) UnmarshalJSON

func (s *DepartmentCapabilitySummary) UnmarshalJSON(data []byte) error

func (DepartmentCapabilitySummary) Validate

func (s DepartmentCapabilitySummary) Validate() error

Validate reports whether every advertised agent is structurally valid.

type EnduringPublication

type EnduringPublication struct {
	TenantID       TenantID        `json:"tenant_id"`
	SessionID      SessionID       `json:"session_id"`
	EventID        EventID         `json:"event_id"`
	JournalSeq     uint64          `json:"journal_seq"`
	CoveredThrough uint64          `json:"covered_through"`
	Body           json.RawMessage `json:"body"`
	// contains filtered or unexported fields
}

EnduringPublication is a committed public journal event carried over a live link. Body is already canonical public JSON and remains opaque to Core. The durable identity and sequence let a consumer join this fast path with a bounded journal read after reconnect or overflow.

func (EnduringPublication) AdditionalFields

func (p EnduringPublication) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible public publication members captured during JSON decoding.

func (EnduringPublication) MarshalJSON

func (p EnduringPublication) MarshalJSON() ([]byte, error)

MarshalJSON keeps the canonical public body opaque. Producers supply the canonical JSON spelling (including its standard escape form); a Host relays that committed body rather than decoding and synthesizing it anew.

func (EnduringPublication) RecordType

func (p EnduringPublication) RecordType() SessionRecordType

RecordType reports the stable session-channel record name this type is published under.

func (*EnduringPublication) UnmarshalJSON

func (p *EnduringPublication) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a public enduring publication while retaining unknown additive envelope members and the exact canonical event body bytes.

func (EnduringPublication) Validate

func (p EnduringPublication) Validate() error

Validate reports whether p can represent one committed public journal event. A live public event covers exactly the sequence it was committed at; later private records can only be covered by an authenticated journal page, not by this event's live publication.

type EphemeralPublication

type EphemeralPublication struct {
	TenantID  TenantID        `json:"tenant_id"`
	SessionID SessionID       `json:"session_id"`
	Body      json.RawMessage `json:"body"`
	// contains filtered or unexported fields
}

EphemeralPublication is a best-effort public live delta. It is deliberately not a durable event: it has no EventID, journal sequence, or coverage watermark and may be coalesced or dropped by a bounded delivery path.

func (EphemeralPublication) AdditionalFields

func (p EphemeralPublication) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible public publication members captured during JSON decoding.

func (EphemeralPublication) MarshalJSON

func (p EphemeralPublication) MarshalJSON() ([]byte, error)

func (EphemeralPublication) RecordType

func (p EphemeralPublication) RecordType() SessionRecordType

RecordType reports the stable session-channel record name this type is published under.

func (*EphemeralPublication) UnmarshalJSON

func (p *EphemeralPublication) UnmarshalJSON(data []byte) error

UnmarshalJSON rejects a durable identity or sequence on an ephemeral record rather than allowing an unsequenced delta to masquerade as replayable data.

func (EphemeralPublication) Validate

func (p EphemeralPublication) Validate() error

Validate reports whether p is a public, unsequenced live delta.

type ErrorCode

type ErrorCode string

ErrorCode is a stable public error discriminator. Factory maps transport and domain failures into this vocabulary without exposing implementation causes.

const (
	ErrorCodeInvalidRequest      ErrorCode = "invalid_request"
	ErrorCodeUnsupportedVersion  ErrorCode = "unsupported_version"
	ErrorCodeSessionNotFound     ErrorCode = "session_not_found"
	ErrorCodeCommandRejected     ErrorCode = "command_rejected"
	ErrorCodeGateResolved        ErrorCode = "gate_resolved"
	ErrorCodeGateNotResumable    ErrorCode = "gate_not_resumable"
	ErrorCodeGateExpired         ErrorCode = "gate_expired"
	ErrorCodeGateResponseInvalid ErrorCode = "gate_response_invalid"
	ErrorCodeRuntimeUnavailable  ErrorCode = "runtime_unavailable"
)

type ErrorDetail

type ErrorDetail struct {
	Code      ErrorCode `json:"code"`
	Message   string    `json:"message,omitempty"`
	Retryable bool      `json:"retryable"`
	// contains filtered or unexported fields
}

ErrorDetail is the client-safe body of a stable error envelope.

func (ErrorDetail) AdditionalFields

func (e ErrorDetail) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible error-detail members.

func (ErrorDetail) MarshalJSON

func (e ErrorDetail) MarshalJSON() ([]byte, error)

func (*ErrorDetail) UnmarshalJSON

func (e *ErrorDetail) UnmarshalJSON(data []byte) error

func (ErrorDetail) Validate

func (e ErrorDetail) Validate() error

Validate reports whether the public error has the stable code required for a client to branch without inspecting Message.

type ErrorEnvelope

type ErrorEnvelope struct {
	Error ErrorDetail `json:"error"`
	// contains filtered or unexported fields
}

ErrorEnvelope is a forward-compatible response envelope. Unknown additive top-level fields are retained so a Core consumer can proxy a newer response without silently dropping data it does not yet understand.

func (ErrorEnvelope) AdditionalFields

func (e ErrorEnvelope) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields captured while unmarshalling. Mutating the returned map or values cannot alter a later re-marshal of this envelope.

func (ErrorEnvelope) MarshalJSON

func (e ErrorEnvelope) MarshalJSON() ([]byte, error)

func (*ErrorEnvelope) UnmarshalJSON

func (e *ErrorEnvelope) UnmarshalJSON(data []byte) error

func (ErrorEnvelope) Validate

func (e ErrorEnvelope) Validate() error

Validate reports whether the envelope carries a valid public error detail.

type EventID

type EventID string

EventID identifies a published session event.

func (EventID) Validate

func (id EventID) Validate() error

Validate reports whether id is a permitted opaque event identity.

type GateAnswerability

type GateAnswerability string

GateAnswerability is the durable answerability projection. It is independent from whether a historical prompt can be rendered: an open event alone does not prove that an answer can still be accepted.

const (
	GateAnswerabilityResident    GateAnswerability = "resident"
	GateAnswerabilitySuspended   GateAnswerability = "suspended"
	GateAnswerabilitySubmitted   GateAnswerability = "submitted"
	GateAnswerabilityUnavailable GateAnswerability = "unavailable"
	GateAnswerabilityExpired     GateAnswerability = "expired"
)

type GateControl

type GateControl struct {
	Action string `json:"action"`
	Label  string `json:"label"`
	// contains filtered or unexported fields
}

GateControl is an exact action and display label supplied by the gate owner.

func (GateControl) AdditionalFields

func (c GateControl) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible control members.

func (GateControl) MarshalJSON

func (c GateControl) MarshalJSON() ([]byte, error)

func (*GateControl) UnmarshalJSON

func (c *GateControl) UnmarshalJSON(data []byte) error

type GateFieldKind

type GateFieldKind string

GateFieldKind identifies the presentation-safe shape of a prompt field.

const (
	GateFieldKindText        GateFieldKind = "text"
	GateFieldKindSelect      GateFieldKind = "select"
	GateFieldKindMultiSelect GateFieldKind = "multi_select"
	GateFieldKindConfirm     GateFieldKind = "confirm"
)

type GateID

type GateID string

GateID identifies a gate awaiting a response.

func (GateID) Validate

func (id GateID) Validate() error

Validate reports whether id is a permitted opaque gate identity.

type GatePage

type GatePage struct {
	JournalTip     uint64           `json:"journal_tip"`
	OpenGateCount  uint64           `json:"open_gate_count"`
	Gates          []GateProjection `json:"gates"`
	NextCursor     Cursor           `json:"next_cursor,omitempty"`
	PreviousCursor Cursor           `json:"previous_cursor,omitempty"`
	// contains filtered or unexported fields
}

GatePage is the bounded durable read of open public gates. JournalTip is a captured tip for the projection, and OpenGateCount can exceed len(Gates) when a later API pages a larger open-gate set.

func (GatePage) AdditionalFields

func (p GatePage) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields.

func (GatePage) MarshalJSON

func (p GatePage) MarshalJSON() ([]byte, error)

func (*GatePage) UnmarshalJSON

func (p *GatePage) UnmarshalJSON(data []byte) error

func (GatePage) Validate

func (p GatePage) Validate() error

Validate reports whether every page entry is public and deterministically ordered by its opening sequence, with the captured tip at or after every gate.

type GateProjection

type GateProjection struct {
	GateID           GateID            `json:"gate_id"`
	Kind             string            `json:"kind"`
	Prompt           GatePrompt        `json:"prompt"`
	OpenedEventID    EventID           `json:"opened_event_id"`
	OpenedJournalSeq uint64            `json:"opened_journal_seq"`
	Deadline         time.Time         `json:"deadline"`
	Answerability    GateAnswerability `json:"answerability"`
	// contains filtered or unexported fields
}

GateProjection is the complete public durable view of an open gate. It joins a presentation-safe prompt with its opening event, effective deadline, and current answerability; it contains no raw answer or private runtime data.

func (GateProjection) AdditionalFields

func (p GateProjection) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields.

func (GateProjection) MarshalJSON

func (p GateProjection) MarshalJSON() ([]byte, error)

func (*GateProjection) UnmarshalJSON

func (p *GateProjection) UnmarshalJSON(data []byte) error

func (GateProjection) Validate

func (p GateProjection) Validate() error

Validate reports whether the public projection has a durable opening identity and one of the explicit answerability states.

type GatePrompt

type GatePrompt struct {
	Title    string           `json:"title,omitempty"`
	Body     string           `json:"body,omitempty"`
	Origin   string           `json:"origin,omitempty"`
	Schema   GatePromptSchema `json:"schema"`
	Controls []GateControl    `json:"controls,omitempty"`
	// contains filtered or unexported fields
}

GatePrompt contains only presentation-safe data that a Factory can render. In particular, Origin is the trusted display origin when applicable—not an action URL—and no private payload or signed URL is present.

func (GatePrompt) AdditionalFields

func (p GatePrompt) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible prompt members.

func (GatePrompt) MarshalJSON

func (p GatePrompt) MarshalJSON() ([]byte, error)

func (*GatePrompt) UnmarshalJSON

func (p *GatePrompt) UnmarshalJSON(data []byte) error

func (GatePrompt) Validate

func (p GatePrompt) Validate() error

Validate reports whether the nested public schema and controls are renderable.

type GatePromptField

type GatePromptField struct {
	Name     string             `json:"name"`
	Label    string             `json:"label"`
	Kind     GateFieldKind      `json:"kind"`
	Required bool               `json:"required"`
	Options  []GatePromptOption `json:"options,omitempty"`
	Default  json.RawMessage    `json:"default,omitempty"`
	// contains filtered or unexported fields
}

GatePromptField is one public structured field. Default is JSON data intended for rendering, not a submitted response value.

func (GatePromptField) AdditionalFields

func (f GatePromptField) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible public field members.

func (GatePromptField) MarshalJSON

func (f GatePromptField) MarshalJSON() ([]byte, error)

func (*GatePromptField) UnmarshalJSON

func (f *GatePromptField) UnmarshalJSON(data []byte) error

func (GatePromptField) Validate

func (f GatePromptField) Validate() error

Validate reports whether a field has the public renderer vocabulary needed to display it. Unknown field kinds fail closed rather than being rendered as text.

type GatePromptOption

type GatePromptOption struct {
	Value string `json:"value"`
	Label string `json:"label"`
	// contains filtered or unexported fields
}

GatePromptOption is one presentation-safe selectable option.

func (GatePromptOption) AdditionalFields

func (o GatePromptOption) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible public option members.

func (GatePromptOption) MarshalJSON

func (o GatePromptOption) MarshalJSON() ([]byte, error)

func (*GatePromptOption) UnmarshalJSON

func (o *GatePromptOption) UnmarshalJSON(data []byte) error

type GatePromptSchema

type GatePromptSchema struct {
	Fields []GatePromptField `json:"fields,omitempty"`
	// contains filtered or unexported fields
}

GatePromptSchema groups the public prompt fields. It deliberately contains no private prepared payload or response data.

func (GatePromptSchema) AdditionalFields

func (s GatePromptSchema) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible schema members.

func (GatePromptSchema) MarshalJSON

func (s GatePromptSchema) MarshalJSON() ([]byte, error)

func (*GatePromptSchema) UnmarshalJSON

func (s *GatePromptSchema) UnmarshalJSON(data []byte) error

func (GatePromptSchema) Validate

func (s GatePromptSchema) Validate() error

Validate reports whether every public field is renderable.

type GateResponseRequest

type GateResponseRequest struct {
	CommandEnvelope
	SessionID              SessionID                  `json:"session_id"`
	GateID                 GateID                     `json:"gate_id"`
	Action                 string                     `json:"action"`
	Values                 map[string]json.RawMessage `json:"values"`
	ExpectedOpenEventID    EventID                    `json:"expected_open_event_id,omitempty"`
	ExpectedOpenJournalSeq uint64                     `json:"expected_open_journal_seq,omitempty"`
	Principal              *Principal                 `json:"principal,omitempty"`
	// contains filtered or unexported fields
}

GateResponseRequest admits a private response to one public gate projection. Values are intentionally raw JSON because Harness remains the semantic gate validator; Core only validates the safe structural boundary.

func (*GateResponseRequest) UnmarshalJSON

func (r *GateResponseRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON strictly decodes a gate response request.

func (GateResponseRequest) Validate

func (r GateResponseRequest) Validate() error

Validate reports whether the gate response has exactly one optimistic-open version (event identity or journal sequence), preventing an answer from being applied to a different incarnation of the same gate ID.

type HostID

type HostID string

HostID identifies the host responsible for a session.

func (HostID) Validate

func (id HostID) Validate() error

Validate reports whether id is a permitted opaque host identity.

type HostIsolationClass

type HostIsolationClass string

HostIsolationClass describes whether a Host target advertisement may accept sessions from different tenants. Pooled placement alone does not establish this boundary; Factory uses the advertised class when choosing capacity.

const (
	HostIsolationClassCrossTenantIsolated HostIsolationClass = "cross_tenant_isolated"
	HostIsolationClassTenantExclusive     HostIsolationClass = "tenant_exclusive"
)

type HostLinkAttachMode added in v0.8.0

type HostLinkAttachMode string

HostLinkAttachMode says whether an attach creates a session or restores one over existing durable state. It is a closed set.

const (
	// HostLinkAttachModeCreate launches a new session.
	HostLinkAttachModeCreate HostLinkAttachMode = "create"
	// HostLinkAttachModeRestore relaunches a session over its durable state.
	HostLinkAttachModeRestore HostLinkAttachMode = "restore"
)

type HostLinkAttachRequest added in v0.8.0

type HostLinkAttachRequest struct {
	Version                WireVersion        `json:"version"`
	TenantID               TenantID           `json:"tenant_id"`
	SessionID              SessionID          `json:"session_id"`
	HostID                 HostID             `json:"host_id"`
	HostGeneration         uint64             `json:"host_generation"`
	AgentID                AgentID            `json:"agent_id"`
	RuntimeCompatibilityID string             `json:"runtime_compatibility_id"`
	Mode                   HostLinkAttachMode `json:"mode"`
	ActorID                string             `json:"actor_id"`
	TraceID                string             `json:"trace_id,omitempty"`
	IdempotencyKey         string             `json:"idempotency_key"`
}

HostLinkAttachRequest asks one Host incarnation to make one session resident, sent as RPC method HostLinkMethodAttach.

HostID and HostGeneration name the Host incarnation the caller placed the session on (from that Host's HostLinkCapacityReport) and fence the attach as they fence bind, unbind and drain. A Host that is not that incarnation, such as a replacement serving the same endpoint or the same Host restarted, must refuse the attach before it acquires any lease, answering runtime_unavailable as it does for a bind addressed to another Host or an earlier incarnation. Without the fence such a Host would take the session lease and return an observation the caller cannot bind to, stranding the session with no route.

It carries no lease epoch. An attach to a session that is not resident acquires the session lease, which produces a new epoch. An attach to a session this Host already has resident acquires nothing and returns the existing residency with its existing epoch.

Mode rides the wire as the caller's statement of intent. A Host may refuse an attach whose mode contradicts durable state; the one such refusal a Host makes is a restore of a session with no durable state, or with no checkpoint when its target requires one, refused runtime_unavailable. Mode is NOT a create-identity guard. An attach to an already-resident session never reads it, so a create against a live session succeeds idempotently and returns that residency; and a create that meets existing durable state proceeds when the runtime build agrees. Whether a create may meet existing state at all is the create identity's idempotency question, not something this record decides.

ActorID is the requesting SERVICE identity, not an end user. Placement may be driven by a sweeper or reconciler with no live user behind it, so the field names who asked for residency, never whose authority a later command carries. TraceID is optional correlation context and confers nothing.

A Host answers an accepted attach with a HostLinkRegistryObservation as the reply body. Its host_id, host_generation and lease_epoch are what a following HostLinkBindRequest needs, so the caller can bind immediately with the returned epoch. Attach is therefore, like the drain methods, an exception to the empty body that accepts a bind or unbind: a Host must return the observation, not an empty body. A refused attach is answered with the existing HostLinkError vocabulary; the codes an attach may answer are:

  • epoch_mismatch: the session lease is held by another owner, so the registry the caller placed from was stale. Its current_lease_epoch is that OTHER holder's epoch, possibly on another Host, and never an epoch this attach produced: a caller must not bind with it.
  • runtime_mismatch: the request disagrees with what is running or registered. The runtime build differs from the target's or the durable state's, the target does not support this Host's placement, the session is already resident as a different agent, a dedicated Host is fixed to a different session, or the launched runtime is bound to a different session or agent.
  • no_capacity: the target's admission weight exceeds this Host's remaining capacity.
  • not_admitting: the Host is draining, or its isolation class forbids admitting this tenant alongside the ones already resident.
  • runtime_unavailable: the attach names another Host or an earlier incarnation of this one, the Host registers no usable target for the agent, a restore has no durable state or lacks a checkpoint its target requires, or the launched runtime had already stopped.

A failure that is not a placement outcome carries no HostLinkError code at all. That covers a store that failed, and a launch that fails outright (a runtime that refused to launch, produced nothing, or lacks a capability the Host requires): re-placing it onto another Host running the same build would be a stampede, not a recovery.

func (HostLinkAttachRequest) MarshalJSON added in v0.8.0

func (r HostLinkAttachRequest) MarshalJSON() ([]byte, error)

func (*HostLinkAttachRequest) UnmarshalJSON added in v0.8.0

func (r *HostLinkAttachRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON fails closed because an attach is authenticated control-plane input that acquires a lease; an unknown member is refused, not ignored.

func (HostLinkAttachRequest) Validate added in v0.8.0

func (r HostLinkAttachRequest) Validate() error

Validate reports whether the attach request names a complete session, the Host incarnation it was placed on, target runtime, closed mode, requesting service and retry-stable attach key.

type HostLinkBindRequest

type HostLinkBindRequest struct {
	Version                WireVersion `json:"version"`
	TenantID               TenantID    `json:"tenant_id"`
	SessionID              SessionID   `json:"session_id"`
	HostID                 HostID      `json:"host_id"`
	HostGeneration         uint64      `json:"host_generation"`
	LeaseEpoch             uint64      `json:"lease_epoch"`
	RuntimeCompatibilityID string      `json:"runtime_compatibility_id"`
	IdempotencyKey         string      `json:"idempotency_key"`
}

HostLinkBindRequest establishes one Factory-local route to a Host-owned session. It is a routing optimization, not proof of ownership: the Host validates the current tuple against its durable lease before accepting it.

func (HostLinkBindRequest) MarshalJSON

func (r HostLinkBindRequest) MarshalJSON() ([]byte, error)

func (*HostLinkBindRequest) UnmarshalJSON

func (r *HostLinkBindRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON fails closed because a bind is authenticated control-plane input; unknown members cannot become a future attach/create workflow.

func (HostLinkBindRequest) Validate

func (r HostLinkBindRequest) Validate() error

Validate reports whether the bind request carries the complete current ownership tuple and a retry-stable bind key.

type HostLinkCapacityReport

type HostLinkCapacityReport struct {
	Version                WireVersion        `json:"version"`
	HostID                 HostID             `json:"host_id"`
	HostGeneration         uint64             `json:"host_generation"`
	AgentID                AgentID            `json:"agent_id"`
	RuntimeCompatibilityID string             `json:"runtime_compatibility_id"`
	Placement              HostPlacement      `json:"placement"`
	InternalEndpoint       InternalEndpoint   `json:"internal_endpoint"`
	IsolationClass         HostIsolationClass `json:"isolation_class"`
	Accepting              bool               `json:"accepting"`
	AvailableCapacity      uint64             `json:"available_capacity"`
	ObservedAt             time.Time          `json:"observed_at"`
	ExpiresAt              time.Time          `json:"expires_at"`
}

HostLinkCapacityReport is a Host's current target-advertisement observation. AvailableCapacity may be zero; that is a valid observation a Factory uses to avoid new placement while still maintaining existing HostLinks.

func (HostLinkCapacityReport) MarshalJSON

func (r HostLinkCapacityReport) MarshalJSON() ([]byte, error)

func (*HostLinkCapacityReport) UnmarshalJSON

func (r *HostLinkCapacityReport) UnmarshalJSON(data []byte) error

func (HostLinkCapacityReport) Validate

func (r HostLinkCapacityReport) Validate() error

Validate reports whether the report is a fresh, bounded-capacity observation.

type HostLinkCommandDelivery

type HostLinkCommandDelivery struct {
	CommandID CommandID `json:"command_id"`
}

HostLinkCommandDelivery wakes consumption of an already accepted command on an existing binding. Its only body member is the public, retry-stable CommandID: runtime UUIDs, input blocks, gate answers, and other private payloads remain in the durable inbox and never cross this link.

func (HostLinkCommandDelivery) MarshalJSON

func (d HostLinkCommandDelivery) MarshalJSON() ([]byte, error)

func (*HostLinkCommandDelivery) UnmarshalJSON

func (d *HostLinkCommandDelivery) UnmarshalJSON(data []byte) error

func (HostLinkCommandDelivery) Validate

func (d HostLinkCommandDelivery) Validate() error

Validate reports whether the delivery contains a usable public command ID.

type HostLinkDrainObservation

type HostLinkDrainObservation struct {
	HostID          HostID             `json:"host_id"`
	HostGeneration  uint64             `json:"host_generation"`
	DrainGeneration uint64             `json:"drain_generation"`
	State           HostLinkDrainState `json:"state"`
	TenantID        TenantID           `json:"tenant_id,omitempty"`
	SessionID       SessionID          `json:"session_id,omitempty"`
}

HostLinkDrainObservation acknowledges a drain initiation and exposes its stable generation/state for a Factory to observe without inferring release completion from a transport close.

func (HostLinkDrainObservation) MarshalJSON

func (o HostLinkDrainObservation) MarshalJSON() ([]byte, error)

func (*HostLinkDrainObservation) UnmarshalJSON

func (o *HostLinkDrainObservation) UnmarshalJSON(data []byte) error

UnmarshalJSON stays strict because this internal observation is a control boundary, not a public projection that may proxy additive fields.

func (HostLinkDrainObservation) Validate

func (o HostLinkDrainObservation) Validate() error

Validate reports whether a drain status describes one Host/scope generation.

type HostLinkDrainRequest

type HostLinkDrainRequest struct {
	Version        WireVersion `json:"version"`
	HostID         HostID      `json:"host_id"`
	HostGeneration uint64      `json:"host_generation"`
	IdempotencyKey string      `json:"idempotency_key"`
	TenantID       TenantID    `json:"tenant_id,omitempty"`
	SessionID      SessionID   `json:"session_id,omitempty"`
}

HostLinkDrainRequest starts idempotent Host draining. A missing tenant/session scope means whole-Host drain; both present scopes identify the fixed session of a dedicated Host. This request does not contain user authority or a deadline.

func (HostLinkDrainRequest) MarshalJSON

func (r HostLinkDrainRequest) MarshalJSON() ([]byte, error)

func (*HostLinkDrainRequest) UnmarshalJSON

func (r *HostLinkDrainRequest) UnmarshalJSON(data []byte) error

func (HostLinkDrainRequest) Validate

func (r HostLinkDrainRequest) Validate() error

Validate reports whether the request has an exact whole-host or dedicated session scope and a retry-stable initiation key.

type HostLinkDrainState

type HostLinkDrainState string

HostLinkDrainState is the bounded public result of a drain observation.

const (
	// HostLinkDrainStateDraining means admission is stopped and durable release
	// work is in progress.
	HostLinkDrainStateDraining HostLinkDrainState = "draining"
	// HostLinkDrainStateDrained means the observed scope has finished release.
	HostLinkDrainStateDrained HostLinkDrainState = "drained"
)

type HostLinkEndpointCode added in v0.10.0

type HostLinkEndpointCode string

HostLinkEndpointCode is a stable, machine-readable reason HostLinkEndpoint could not derive a tenant's HostLink address. A caller branches on it rather than on error text; the set grows only additively.

const (
	// HostLinkEndpointCodeInvalidBase: the base fails
	// InternalEndpoint.Validate. The error wraps that *RequestValidationError.
	HostLinkEndpointCodeInvalidBase HostLinkEndpointCode = "invalid_base"
	// HostLinkEndpointCodeBaseNamesTenant: the base's decoded path is
	// "/hostlink" or begins with HostLinkPathPrefix, with or without a tenant
	// segment after it. This is how a working host v0.2.1 deployment spells
	// the one tenant's link it advertises; it is not a base, and appending a
	// second tenant to it could never route. The match is case-exact, as
	// Host's router is: "/HOSTLINK/x" names no tenant (host v0.2.1 answers
	// it 404) and is refused as base_not_bare instead.
	HostLinkEndpointCodeBaseNamesTenant HostLinkEndpointCode = "base_names_tenant"
	// HostLinkEndpointCodeBaseNotBare: the base carries something besides a
	// scheme, an authority and at most one trailing '/': any other path, or an
	// empty fragment marker ('#', which InternalEndpoint.Validate tolerates
	// because net/url drops an empty fragment, but which would swallow an
	// appended path).
	HostLinkEndpointCodeBaseNotBare HostLinkEndpointCode = "base_not_bare"
	// HostLinkEndpointCodeInvalidTenant: the tenant fails TenantID.Validate.
	// The error wraps that *IDValidationError.
	HostLinkEndpointCodeInvalidTenant HostLinkEndpointCode = "invalid_tenant"
	// HostLinkEndpointCodeUnroutableTenant: the tenant is a legal Core
	// identity that a Host's HostLink router cannot be relied on to resolve:
	// "." and "..", or any tenant containing '/'. See HostLinkEndpoint.
	HostLinkEndpointCodeUnroutableTenant HostLinkEndpointCode = "unroutable_tenant"
	// HostLinkEndpointCodeTooLong: the derived address is longer than
	// MaxIDBytes, so it would fail InternalEndpoint.Validate. The limit counts
	// the ESCAPED tenant, so a tenant well under MaxIDBytes can exceed it.
	HostLinkEndpointCodeTooLong HostLinkEndpointCode = "too_long"
)

type HostLinkEndpointError added in v0.10.0

type HostLinkEndpointError struct {
	Code HostLinkEndpointCode
	Err  error
}

HostLinkEndpointError reports why HostLinkEndpoint refused. Its text is exactly "sessionwire/v1: cannot derive HostLink endpoint: " followed by the code; it names neither the base, the tenant nor the wrapped cause, so it is safe to log. Err is the lower-level validation error for the invalid_base and invalid_tenant reasons and nil otherwise.

func (*HostLinkEndpointError) Error added in v0.10.0

func (e *HostLinkEndpointError) Error() string

func (*HostLinkEndpointError) Unwrap added in v0.10.0

func (e *HostLinkEndpointError) Unwrap() error

Unwrap returns the lower-level validation error, if any.

type HostLinkError

type HostLinkError struct {
	Code                   HostLinkErrorCode `json:"code"`
	CurrentLeaseEpoch      uint64            `json:"current_lease_epoch,omitempty"`
	RuntimeCompatibilityID string            `json:"runtime_compatibility_id,omitempty"`
}

HostLinkError is a safe typed HostLink refusal. CurrentLeaseEpoch is present only for an epoch mismatch; RuntimeCompatibilityID identifies the compatible boundary in a runtime mismatch, never credentials or a raw runtime payload.

func (HostLinkError) MarshalJSON

func (e HostLinkError) MarshalJSON() ([]byte, error)

func (*HostLinkError) UnmarshalJSON

func (e *HostLinkError) UnmarshalJSON(data []byte) error

func (HostLinkError) Validate

func (e HostLinkError) Validate() error

Validate reports whether error-specific safe details match its stable code.

type HostLinkErrorCode

type HostLinkErrorCode string

HostLinkErrorCode is a stable HostLink control-plane refusal class. It has no provider error string or runtime payload, so callers can act without parsing opaque internals.

const (
	HostLinkErrorNotAdmitting       HostLinkErrorCode = "not_admitting"
	HostLinkErrorReleasing          HostLinkErrorCode = "releasing"
	HostLinkErrorNoCapacity         HostLinkErrorCode = "no_capacity"
	HostLinkErrorEpochMismatch      HostLinkErrorCode = "epoch_mismatch"
	HostLinkErrorRuntimeUnavailable HostLinkErrorCode = "runtime_unavailable"
	HostLinkErrorRuntimeMismatch    HostLinkErrorCode = "runtime_mismatch"
)

type HostLinkRegistryObservation

type HostLinkRegistryObservation struct {
	Version                WireVersion      `json:"version"`
	TenantID               TenantID         `json:"tenant_id"`
	SessionID              SessionID        `json:"session_id"`
	HostID                 HostID           `json:"host_id"`
	HostGeneration         uint64           `json:"host_generation"`
	AgentID                AgentID          `json:"agent_id"`
	RuntimeCompatibilityID string           `json:"runtime_compatibility_id"`
	Placement              HostPlacement    `json:"placement"`
	InternalEndpoint       InternalEndpoint `json:"internal_endpoint"`
	Residency              SessionResidency `json:"residency"`
	Accepting              bool             `json:"accepting"`
	LeaseEpoch             uint64           `json:"lease_epoch"`
	ObservedAt             time.Time        `json:"observed_at"`
	ExpiresAt              time.Time        `json:"expires_at"`
}

HostLinkRegistryObservation is a current Host-owned session location hint. It is not ownership proof: SessionStore's lease and epoch fence remain authoritative even while the observation is fresh.

func (HostLinkRegistryObservation) MarshalJSON

func (r HostLinkRegistryObservation) MarshalJSON() ([]byte, error)

func (*HostLinkRegistryObservation) UnmarshalJSON

func (r *HostLinkRegistryObservation) UnmarshalJSON(data []byte) error

func (HostLinkRegistryObservation) Validate

func (r HostLinkRegistryObservation) Validate() error

Validate reports whether the per-session route observation has a current lease tuple and an expiry after its observation time.

type HostLinkUnbindRequest

type HostLinkUnbindRequest struct {
	Version        WireVersion `json:"version"`
	TenantID       TenantID    `json:"tenant_id"`
	SessionID      SessionID   `json:"session_id"`
	HostID         HostID      `json:"host_id"`
	HostGeneration uint64      `json:"host_generation"`
	LeaseEpoch     uint64      `json:"lease_epoch"`
	IdempotencyKey string      `json:"idempotency_key"`
}

HostLinkUnbindRequest removes one Factory-local route. Repeating the same tuple/key is safe; it never releases the Host's lease or durable session.

func (HostLinkUnbindRequest) MarshalJSON

func (r HostLinkUnbindRequest) MarshalJSON() ([]byte, error)

func (*HostLinkUnbindRequest) UnmarshalJSON

func (r *HostLinkUnbindRequest) UnmarshalJSON(data []byte) error

func (HostLinkUnbindRequest) Validate

func (r HostLinkUnbindRequest) Validate() error

Validate reports whether the unbind request identifies a bound route.

type HostPlacement

type HostPlacement string

HostPlacement identifies the admission model for one Host advertisement. It is a bounded routing label, not a request to create or attach a session.

const (
	HostPlacementPooled    HostPlacement = "pooled"
	HostPlacementDedicated HostPlacement = "dedicated"
)

type IDValidationCode

type IDValidationCode string

IDValidationCode is a stable machine-readable reason an identity is invalid.

const (
	// IDValidationCodeEmpty reports an empty identity.
	IDValidationCodeEmpty IDValidationCode = "empty"
	// IDValidationCodeTooLong reports an identity larger than MaxIDBytes bytes.
	IDValidationCodeTooLong IDValidationCode = "too_long"
	// IDValidationCodeInvalidUTF8 reports an identity that is not valid UTF-8.
	IDValidationCodeInvalidUTF8 IDValidationCode = "invalid_utf8"
)

type IDValidationError

type IDValidationError struct {
	Code IDValidationCode
}

IDValidationError reports a failed identity validation. Code is stable for callers that need to distinguish validation failures without matching Error.

func (*IDValidationError) Error

func (e *IDValidationError) Error() string

type InputRequest

type InputRequest struct {
	CommandEnvelope
	SessionID SessionID       `json:"session_id"`
	Blocks    json.RawMessage `json:"blocks"`
	Metadata  MessageMetadata `json:"metadata,omitempty"`
	Principal *Principal      `json:"principal,omitempty"`
}

InputRequest admits input blocks for an existing durable session. The blocks retain their canonical JSON form so Core does not import Harness content types. Metadata and Principal are as on CreateRequest.

func (*InputRequest) UnmarshalJSON

func (r *InputRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON strictly decodes an input request.

func (InputRequest) Validate

func (r InputRequest) Validate() error

Validate reports whether the input request contains a client command identity, a target session, and a non-empty JSON block array.

type InternalEndpoint

type InternalEndpoint string

InternalEndpoint is a non-secret WebSocket address for an authenticated HostLink. Credentials, signed query parameters, and fragments are prohibited; service authentication is carried by the HostLink adapter rather than this durable routing observation.

func HostLinkEndpoint added in v0.10.0

func HostLinkEndpoint(base InternalEndpoint, tenant TenantID) (InternalEndpoint, error)

HostLinkEndpoint derives one tenant's HostLink address from a Host's BASE endpoint.

A Host's advertised internal_endpoint is a BASE

A Host serves a separate HostLink per tenant, at HostLinkPathPrefix plus that tenant, and authenticates each connection against the tenant its PATH names before any transport is chosen. The internal_endpoint a Host advertises (in HostLinkCapacityReport and HostLinkRegistryObservation) is therefore a base — a ws or wss scheme and an authority, nothing else — and a Factory reaches tenant T on that Host by dialling HostLinkEndpoint(base, T). One Factory link carries one tenant; a pooled Host serving several tenants has one link per tenant, all derived from the same base.

The result is the base with at most one trailing '/' removed, then HostLinkPathPrefix, then the tenant escaped with url.PathEscape. That is byte-for-byte the path host v0.2.1's router resolves back to the same tenant (pinned by goldens produced by running that router). The escaping is not optional: a '%' in a tenant would otherwise be decoded, so a tenant spelled "%2e" would reach tenant "."; and '?', '#', space and control characters would otherwise end or break the path.

What it refuses

It refuses, with a *HostLinkEndpointError, every input that cannot produce a routable, valid address. It checks in the order listed and reports the first that applies, so the base is judged before the tenant, and an invalid tenant before an unroutable one before an overlong one:

  • a base that fails InternalEndpoint.Validate (invalid_base);
  • a base whose path is "/hostlink" or begins with HostLinkPathPrefix (case-exact), as a working host v0.2.1 per-tenant endpoint's does (base_names_tenant);
  • a base with any other path, or an empty fragment marker (base_not_bare). That includes an endpoint behind an ingress path prefix, such as ".../p/hostlink/t". A path prefix is refused rather than preserved because a Host serves HostLinkPathPrefix at its root; admitting one later is an additive relaxation, while refusing one later would not be;
  • a tenant that fails TenantID.Validate (invalid_tenant);
  • "." and "..", and any tenant containing '/' (unroutable_tenant). A Go http.ServeMux path-cleans "/hostlink/." and "/hostlink/.." and redirects them, and a Host takes exactly one path segment, which '/' — escaped or not, since the server decodes it — can never be. "." and ".." do reach host v0.2.1 when spelled "%2E" and "%2E%2E", but only because Go 1.22's mux cleans the escaped path: under GODEBUG=httpmuxgo121=1 every spelling is redirected, and RFC 3986 treats "%2E" and "." as equivalent (section 6.2.2), so a normalising intermediary may rewrite one to the other and then remove the dot segment. An address that routes only by that accident is refused;
  • a derived address longer than MaxIDBytes (too_long).

It returns an endpoint that passes InternalEndpoint.Validate whenever it returns no error. It is a pure function over its arguments.

Compatibility window

Host v0.2.1 advertises its configured internal endpoint verbatim, checking only InternalEndpoint.Validate, and a Factory predating this rule dials the advertised endpoint verbatim. The per-tenant spelling (base plus "/hostlink/<tenant>") was therefore a deployment convention, not a Host property: it is the only spelling an older Factory could reach a Host by.

  • A Host that advertises a BASE, dialled verbatim by an older Factory, answers 404, because the base's path names no tenant segment.
  • A deriving Factory refuses a conventional v0.2.1 per-tenant endpoint with base_names_tenant, and one behind an ingress path prefix with base_not_bare.
  • A host v0.2.1 RECONFIGURED to advertise a bare base already works with a deriving Factory, with no Host upgrade, because v0.2.1 already serves every tenant at HostLinkPathPrefix plus that tenant. It then no longer works with an older Factory.

So what must move together is the advertised value and the Factory: switch a Host's configured endpoint to a base only once every Factory dialling it derives addresses.

Example
package main

import (
	"fmt"

	sessionwire "github.com/looprig/core/sessionwire/v1"
)

func main() {
	// A pooled Host advertises one BASE; each tenant's link is derived from it.
	base := sessionwire.InternalEndpoint("ws://host-7.pool.internal:7443")
	for _, tenant := range []sessionwire.TenantID{"acme", "a b", "a/b", "."} {
		endpoint, err := sessionwire.HostLinkEndpoint(base, tenant)
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Println(endpoint)
	}
}
Output:
ws://host-7.pool.internal:7443/hostlink/acme
ws://host-7.pool.internal:7443/hostlink/a%20b
sessionwire/v1: cannot derive HostLink endpoint: unroutable_tenant
sessionwire/v1: cannot derive HostLink endpoint: unroutable_tenant

func (InternalEndpoint) Validate

func (endpoint InternalEndpoint) Validate() error

Validate reports whether endpoint is a bounded, credential-free WebSocket endpoint suitable for HostLink discovery. Every failure is reported as a *RequestValidationError naming the internal_endpoint member, so a caller can branch on Code instead of matching error text: an absent endpoint reports RequestValidationCodeMissingField and every malformed or credential-bearing spelling reports RequestValidationCodeInvalidField.

type InterruptRequest

type InterruptRequest struct {
	CommandEnvelope
	SessionID SessionID  `json:"session_id"`
	Principal *Principal `json:"principal,omitempty"`
}

InterruptRequest asks the durable command plane to interrupt the target session. Completion is observed through CommandStatus and public events. Principal is the optional Factory-stamped sender; metadata is never carried.

func (*InterruptRequest) UnmarshalJSON

func (r *InterruptRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON strictly decodes an interrupt request.

func (InterruptRequest) Validate

func (r InterruptRequest) Validate() error

Validate reports whether the interrupt request is structurally admissible.

type JournalEvent

type JournalEvent struct {
	EventID    EventID         `json:"event_id"`
	JournalSeq uint64          `json:"journal_seq"`
	Body       json.RawMessage `json:"body"`
	// contains filtered or unexported fields
}

JournalEvent is a public durable event. Body is already canonical public JSON and remains opaque to Core: it is neither decoded from nor synthesized from a Harness runtime event.

func (JournalEvent) AdditionalFields

func (e JournalEvent) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible public event members.

func (JournalEvent) MarshalJSON

func (e JournalEvent) MarshalJSON() ([]byte, error)

func (*JournalEvent) UnmarshalJSON

func (e *JournalEvent) UnmarshalJSON(data []byte) error

func (JournalEvent) Validate

func (e JournalEvent) Validate() error

Validate reports whether the event has a public identity, a durable sequence, and a non-null JSON body.

type JournalPage

type JournalPage struct {
	Events         []JournalEvent `json:"events"`
	CapturedTip    uint64         `json:"journal_tip"`
	CoveredThrough uint64         `json:"covered_through"`
	NextCursor     Cursor         `json:"next_cursor,omitempty"`
	PreviousCursor Cursor         `json:"previous_cursor,omitempty"`
	// contains filtered or unexported fields
}

JournalPage is a bounded public journal response captured at CapturedTip. CoveredThrough can advance across private ledger records, but never reveals their kind or bytes; clients may only use this authenticated watermark to close a sequence gap.

func (JournalPage) AdditionalFields

func (p JournalPage) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields.

func (JournalPage) MarshalJSON

func (p JournalPage) MarshalJSON() ([]byte, error)

func (*JournalPage) UnmarshalJSON

func (p *JournalPage) UnmarshalJSON(data []byte) error

func (JournalPage) Validate

func (p JournalPage) Validate() error

Validate reports whether page sequences are increasing and all public events are covered by an authenticated watermark within the captured journal tip.

type JournalTip

type JournalTip struct {
	TenantID  TenantID  `json:"tenant_id"`
	SessionID SessionID `json:"session_id"`
	Tip       uint64    `json:"journal_tip"`
	// contains filtered or unexported fields
}

JournalTip is a repeatable repair hint. It is not an acknowledgement or a browser cursor: a client compares Tip with its own covered sequence and reads any missing public pages through the durable query plane.

func (JournalTip) AdditionalFields

func (h JournalTip) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible repair-hint members.

func (JournalTip) MarshalJSON

func (h JournalTip) MarshalJSON() ([]byte, error)

func (JournalTip) RecordType

func (h JournalTip) RecordType() SessionRecordType

RecordType reports the stable session-channel record name this type is published under.

func (*JournalTip) UnmarshalJSON

func (h *JournalTip) UnmarshalJSON(data []byte) error

func (JournalTip) Validate

func (h JournalTip) Validate() error

Validate reports whether h has the tenant/session scope required for a durable repair hint. A zero Tip is valid for a session with no journal yet.

type MessageMetadata added in v0.12.0

type MessageMetadata map[string]string

MessageMetadata is a client's app-defined string fields on create or input. It is not identity; the verified sender is Principal.

func (MessageMetadata) Validate added in v0.12.0

func (m MessageMetadata) Validate() error

Validate enforces field, key, value and canonical object byte limits. Iterating sorted keys gives deterministic errors.

type MetadataValidationCode added in v0.12.0

type MetadataValidationCode string

MetadataValidationCode identifies the rule a metadata object violates.

const (
	MetadataValidationCodeTooManyFields MetadataValidationCode = "too_many_fields"
	MetadataValidationCodeInvalidKey    MetadataValidationCode = "invalid_key"
	MetadataValidationCodeReservedKey   MetadataValidationCode = "reserved_key"
	MetadataValidationCodeValueTooLarge MetadataValidationCode = "value_too_large"
	MetadataValidationCodeInvalidValue  MetadataValidationCode = "invalid_value"
	MetadataValidationCodeTooLarge      MetadataValidationCode = "too_large"
)

type MetadataValidationError added in v0.12.0

type MetadataValidationError struct {
	Code MetadataValidationCode
	Key  string
}

MetadataValidationError reports invalid metadata. Key is present only when it satisfies the grammar; Error never echoes caller-controlled key or value. Unwrap exposes a request-level malformed-field error to consumers.

func (*MetadataValidationError) Error added in v0.12.0

func (e *MetadataValidationError) Error() string

func (*MetadataValidationError) Unwrap added in v0.12.0

func (e *MetadataValidationError) Unwrap() error

type ObjectMetadata

type ObjectMetadata struct {
	Reference ObjectReference `json:"reference"`
	SizeBytes uint64          `json:"size_bytes"`
	MediaType string          `json:"media_type,omitempty"`
	Digest    string          `json:"digest,omitempty"`
	CreatedAt time.Time       `json:"created_at,omitzero"`
}

ObjectMetadata describes an immutable logical object without exposing a provider URL, credential, backend path, or object bytes.

func (*ObjectMetadata) UnmarshalJSON

func (m *ObjectMetadata) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes object metadata through the same member decoder as every other record — gaining duplicate-member rejection and the package's stable typed errors — while keeping the deliberate unknown-member drop. Decoding the members explicitly also keeps the promise that a validation error names fields and never values: encoding/json's own type error embeds the offending literal.

func (ObjectMetadata) Validate

func (m ObjectMetadata) Validate() error

Validate reports whether metadata has a safe logical reference.

type ObjectReference

type ObjectReference struct {
	ObjectID string `json:"object_id"`
}

ObjectReference is an opaque logical object identity. It is not a bucket key, signed URL, credential, or a byte payload.

Unlike forward-compatible public projection records, object records are a redaction boundary: unknown JSON members are intentionally not retained or re-emitted, so a provider URL, credential, or object bytes cannot be proxied.

func (*ObjectReference) UnmarshalJSON

func (r *ObjectReference) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes an object reference through the same member decoder as every other record, so a duplicate member is rejected here too rather than leaving two parsers free to disagree about which copy wins on a redaction boundary. The deliberate redaction drop is unchanged: a member this version does not declare is discarded rather than retained and re-emitted, so a provider URL, credential, or object byte payload cannot be proxied.

func (ObjectReference) Validate

func (r ObjectReference) Validate() error

Validate reports whether the logical reference is bounded opaque UTF-8.

type Principal added in v0.12.0

type Principal struct {
	Tenant  TenantID      `json:"tenant"`
	Subject SubjectID     `json:"subject"`
	Kind    PrincipalKind `json:"kind"`
}

Principal is the verified sender of a command as Factory established it. Factory stamps it after credential verification; clients never supply it. Core validates its shape but does not prove who the sender is.

func (Principal) MarshalJSON added in v0.12.0

func (p Principal) MarshalJSON() ([]byte, error)

MarshalJSON validates p and preserves tenant, subject, kind member order.

func (*Principal) UnmarshalJSON added in v0.12.0

func (p *Principal) UnmarshalJSON(data []byte) error

UnmarshalJSON strictly decodes a principal and refuses unknown members.

func (Principal) Validate added in v0.12.0

func (p Principal) Validate() error

Validate checks each identity and the known principal kind.

type PrincipalKind added in v0.12.0

type PrincipalKind string

PrincipalKind classifies the verified sender of a command.

const (
	PrincipalKindActor   PrincipalKind = "actor"
	PrincipalKindService PrincipalKind = "service"
)

type RequestValidationCode

type RequestValidationCode string

RequestValidationCode is a stable, transport-neutral reason a request record could not be decoded or validated. It intentionally names fields, never values, so request payloads such as gate answers do not leak through an error string.

const (
	RequestValidationCodeInvalidJSON        RequestValidationCode = "invalid_json"
	RequestValidationCodeDuplicateField     RequestValidationCode = "duplicate_field"
	RequestValidationCodeUnknownField       RequestValidationCode = "unknown_field"
	RequestValidationCodeMissingField       RequestValidationCode = "missing_required_field"
	RequestValidationCodeInvalidField       RequestValidationCode = "invalid_field"
	RequestValidationCodeUnsupportedVersion RequestValidationCode = "unsupported_version"
)

type RequestValidationError

type RequestValidationError struct {
	Code  RequestValidationCode
	Field string
}

RequestValidationError reports a structural V1 request failure. Field is a stable field name rather than a caller-provided value.

func (*RequestValidationError) Error

func (e *RequestValidationError) Error() string

type RestoreRequest

type RestoreRequest struct {
	CommandEnvelope
	SessionID SessionID  `json:"session_id"`
	Principal *Principal `json:"principal,omitempty"`
}

RestoreRequest is the explicit wire-compatibility request. New clients rely on normal durable command admission and placement; this record keeps the staged route explicit without making a read trigger restoration. Principal is the optional Factory-stamped sender; metadata is never carried.

func (*RestoreRequest) UnmarshalJSON

func (r *RestoreRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON strictly decodes a compatibility restore request.

func (RestoreRequest) Validate

func (r RestoreRequest) Validate() error

Validate reports whether the compatibility restore request is structurally admissible.

type SessionID

type SessionID string

SessionID identifies one session within a tenant.

func (SessionID) Validate

func (id SessionID) Validate() error

Validate reports whether id is a permitted opaque session identity.

type SessionPage

type SessionPage struct {
	Sessions       []SessionSummary `json:"sessions"`
	NextCursor     Cursor           `json:"next_cursor,omitempty"`
	PreviousCursor Cursor           `json:"previous_cursor,omitempty"`
	// contains filtered or unexported fields
}

SessionPage is a bounded recent-first page. It is a durable read and never restores a cold session as a side effect.

func (SessionPage) AdditionalFields

func (p SessionPage) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields.

func (SessionPage) MarshalJSON

func (p SessionPage) MarshalJSON() ([]byte, error)

func (*SessionPage) UnmarshalJSON

func (p *SessionPage) UnmarshalJSON(data []byte) error

func (SessionPage) Validate

func (p SessionPage) Validate() error

Validate reports whether the page is in non-increasing LastActiveAt order. Equal timestamps are left in the provider's stable cursor order.

type SessionRecordType

type SessionRecordType string

SessionRecordType is the stable wire name of a record carried on the tenant-scoped session channel `session:{tid}:{sid}` (specification 8.1). One subscriber receives every publication, repair hint, and repair control on that single channel, so each record names its own kind in the `type` envelope member rather than leaving a consumer to infer a kind from which optional members happen to be present.

The two spec-named values are used verbatim: specification 17.3 and 21 name the `session.reset` repair control and the repeatable `journal_tip` hint. A dotted name follows the control/RPC namespace the specification already uses for `session.input`, `session.interrupt`, and `gate.respond`; the two publication records are data rather than controls and use the snake_case record names their schemas and fixtures are already published under.

The discriminator belongs to the envelope. It never enters the opaque relayed event body, so the canonical byte-identical relay contract is unchanged.

const (
	SessionRecordTypeEnduringPublication  SessionRecordType = "enduring_publication"
	SessionRecordTypeEphemeralPublication SessionRecordType = "ephemeral_publication"
	SessionRecordTypeJournalTip           SessionRecordType = "journal_tip"
	SessionRecordTypeSessionReset         SessionRecordType = "session.reset"
)

func SessionRecordTypeOf

func SessionRecordTypeOf(data []byte) (SessionRecordType, error)

SessionRecordTypeOf reports the record kind of one encoded session-channel record so a subscriber can dispatch before decoding. It fails closed on a missing, non-string, or unrecognized discriminator rather than guessing.

type SessionReset

type SessionReset struct {
	TenantID       TenantID  `json:"tenant_id"`
	SessionID      SessionID `json:"session_id"`
	LastContiguous uint64    `json:"last_contiguous"`
	JournalTip     uint64    `json:"journal_tip"`
	// contains filtered or unexported fields
}

SessionReset is a repeatable repair control sent after a bounded delivery path loses an enduring frame. LastContiguous is the greatest sequence the sender knows it forwarded in order; JournalTip is one durable tip captured for the receiver's subsequent bounded repair reads.

func (SessionReset) AdditionalFields

func (r SessionReset) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible reset-control members.

func (SessionReset) MarshalJSON

func (r SessionReset) MarshalJSON() ([]byte, error)

func (SessionReset) RecordType

func (r SessionReset) RecordType() SessionRecordType

RecordType reports the stable session-channel record name this type is published under.

func (*SessionReset) UnmarshalJSON

func (r *SessionReset) UnmarshalJSON(data []byte) error

func (SessionReset) Validate

func (r SessionReset) Validate() error

Validate reports whether reset coverage is internally coherent. A zero sequence is valid for a consumer that has not yet received a durable event.

type SessionResidency

type SessionResidency string

SessionResidency is independent from SessionState: an idle session can remain resident and a cold session remains readable.

const (
	SessionResidencyCold      SessionResidency = "cold"
	SessionResidencyAttaching SessionResidency = "attaching"
	SessionResidencyResident  SessionResidency = "resident"
	SessionResidencyReleasing SessionResidency = "releasing"
)

type SessionState

type SessionState string

SessionState is the durable execution-state projection. Values may grow in a future wire version; a consumer must treat an unfamiliar non-empty value as a state it cannot actively control rather than as a missing session.

const (
	SessionStateRunning       SessionState = "running"
	SessionStateWaitingOnGate SessionState = "waiting_on_gate"
	SessionStateSuspended     SessionState = "suspended"
	SessionStateRestoring     SessionState = "restoring"
	SessionStateIdle          SessionState = "idle"
	SessionStateFailed        SessionState = "failed"
	SessionStateInterrupted   SessionState = "interrupted"
	SessionStateStopped       SessionState = "stopped"
)

type SessionStatus

type SessionStatus struct {
	SessionID     SessionID        `json:"session_id"`
	AgentID       AgentID          `json:"agent_id"`
	State         SessionState     `json:"state"`
	Residency     SessionResidency `json:"residency"`
	JournalTip    uint64           `json:"journal_tip"`
	WaitingGateID GateID           `json:"waiting_gate_id,omitempty"`
	UpdatedAt     time.Time        `json:"updated_at,omitzero"`
	// contains filtered or unexported fields
}

SessionStatus is the replay-free durable state for one session. It reports only projections that remain available while every Host is stopped.

func (SessionStatus) AdditionalFields

func (s SessionStatus) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields.

func (SessionStatus) MarshalJSON

func (s SessionStatus) MarshalJSON() ([]byte, error)

func (*SessionStatus) UnmarshalJSON

func (s *SessionStatus) UnmarshalJSON(data []byte) error

func (SessionStatus) Validate

func (s SessionStatus) Validate() error

Validate reports whether the status names a session and both independent execution and residency dimensions.

type SessionSummary

type SessionSummary struct {
	SessionID    SessionID    `json:"session_id"`
	AgentID      AgentID      `json:"agent_id"`
	State        SessionState `json:"state"`
	Title        string       `json:"title,omitempty"`
	CreatedAt    time.Time    `json:"created_at,omitzero"`
	LastActiveAt time.Time    `json:"last_active_at"`
	// contains filtered or unexported fields
}

SessionSummary is the bounded, picker-facing durable projection used in a recent-first SessionPage. It deliberately contains no live Host route.

func (SessionSummary) AdditionalFields

func (s SessionSummary) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of unknown additive response fields.

func (SessionSummary) MarshalJSON

func (s SessionSummary) MarshalJSON() ([]byte, error)

func (*SessionSummary) UnmarshalJSON

func (s *SessionSummary) UnmarshalJSON(data []byte) error

func (SessionSummary) Validate

func (s SessionSummary) Validate() error

Validate reports whether the summary has its required durable identity and recency projection. Any non-empty state is allowed for forward compatibility.

type SubjectID added in v0.12.0

type SubjectID string

SubjectID identifies the verified user or service account a Factory established for a command. It is opaque and never a display name.

func (SubjectID) Validate added in v0.12.0

func (id SubjectID) Validate() error

Validate reports whether id is a permitted opaque subject identity.

type TenantID

type TenantID string

TenantID identifies the tenant that owns a session.

func (TenantID) Validate

func (id TenantID) Validate() error

Validate reports whether id is a permitted opaque tenant identity.

type VersionNegotiationRequest

type VersionNegotiationRequest struct {
	SupportedVersions []WireVersion `json:"supported_versions"`
}

VersionNegotiationRequest advertises the sessionwire versions a peer can speak before any versioned HostLink control records are accepted.

func DecodeHostLinkConnectRequest added in v0.9.0

func DecodeHostLinkConnectRequest(data []byte) (VersionNegotiationRequest, error)

DecodeHostLinkConnectRequest reads a HostLink connect's Data as the bare VersionNegotiationRequest. It is strict: a wrapped or otherwise unknown member is refused with RequestValidationCodeUnknownField.

func (VersionNegotiationRequest) MarshalJSON

func (r VersionNegotiationRequest) MarshalJSON() ([]byte, error)

func (*VersionNegotiationRequest) UnmarshalJSON

func (r *VersionNegotiationRequest) UnmarshalJSON(data []byte) error

func (VersionNegotiationRequest) Validate

func (r VersionNegotiationRequest) Validate() error

Validate reports whether the peer offered a non-empty, duplicate-free set.

type VersionNegotiationResponse

type VersionNegotiationResponse struct {
	Version WireVersion `json:"version"`
	// contains filtered or unexported fields
}

VersionNegotiationResponse selects the one current sessionwire version both ends use. Unknown additive response fields are retained for public ClientLink negotiation proxies; HostLink control requests remain strict.

It also carries the optional hostlink_methods member: the set of reserved HostLink methods the replying Host serves, by name (the HostLinkMethod* constants), PLUS any capability tokens it advertises (see "Capability tokens" in hostlink_framing.go, e.g. HostLinkCapabilityGateResponse). A token is never dispatched as an RPC — it is read only with Supports or HostLinkMethods to answer "can this peer do X" — while a method name is both dispatched and readable the same way. It is the capability signal a Factory gates on. A Host that predates it answers without the member, which decodes as an empty set, and a Factory must then not send a method or admit a command it cannot see advertised — a v0.1.0 Host routes hostlink.attach as a channel and answers runtime_unavailable, indistinguishable from a genuine refusal. Read it with Supports or HostLinkMethods; a Host sets it with WithHostLinkMethods after NegotiateVersion.

The member is omitted when empty, so a reply that advertises nothing is byte-identical to the v0.8.0 shape. On the wire it is validated as a SET of non-empty identifiers — a duplicate or an empty name is invalid_field — but a name this version has no constant for is TOLERATED AND RETAINED, not refused: refusing would make the very first record a newer Host sends fail every older Factory's connect, and a method added later by Host must not break a Factory built against this version. Retaining (rather than dropping) the name keeps a proxy from silently stripping a capability on re-encode and makes Supports the truthful "the peer advertised this".

The set is held behind a pointer rather than as a slice field so the struct stays comparable, as it was at v0.8.0. Comparable is not value equality: on a reply carrying methods or extensions, == compares the pointers and so reports identity, not contents — two decodes of the same bytes are !=. Only replies carrying neither compare by value, so two replies that advertise nothing are equal. Compare what a reply advertises with Supports or HostLinkMethods, and its extensions with AdditionalFields.

func DecodeHostLinkConnectReply added in v0.9.0

func DecodeHostLinkConnectReply(data []byte) (VersionNegotiationResponse, error)

DecodeHostLinkConnectReply reads a HostLink connect reply's Data as the bare VersionNegotiationResponse. A reply without the version member — including one wrapped as {"version_negotiation":{...}} — is refused with RequestValidationCodeMissingField rather than read as version 0.

func NegotiateVersion

func NegotiateVersion(request VersionNegotiationRequest) (VersionNegotiationResponse, error)

NegotiateVersion selects CurrentWireVersion when the peer advertises it. Callers can serialize the returned response or return the stable unsupported version validation error without depending on a transport library.

func (VersionNegotiationResponse) AdditionalFields

func (r VersionNegotiationResponse) AdditionalFields() map[string]json.RawMessage

AdditionalFields returns copies of forward-compatible response members.

func (VersionNegotiationResponse) HostLinkMethods added in v0.9.0

func (r VersionNegotiationResponse) HostLinkMethods() []string

HostLinkMethods returns a copy of the advertised method names in wire order, or nil when the reply advertised none.

func (VersionNegotiationResponse) MarshalJSON

func (r VersionNegotiationResponse) MarshalJSON() ([]byte, error)

func (VersionNegotiationResponse) Supports added in v0.9.0

func (r VersionNegotiationResponse) Supports(method string) bool

Supports reports whether the replying peer advertised method in its hostlink_methods. It is an exact string match against the reserved method names (HostLinkMethod*). It reports false for every method on a reply that carried no hostlink_methods member, which is what a v0.8.0-era Host sends.

func (*VersionNegotiationResponse) UnmarshalJSON

func (r *VersionNegotiationResponse) UnmarshalJSON(data []byte) error

func (VersionNegotiationResponse) Validate

func (r VersionNegotiationResponse) Validate() error

Validate reports whether r selects this Core implementation's supported V1 and advertises a well-formed method set.

func (VersionNegotiationResponse) WithHostLinkMethods added in v0.9.0

func (r VersionNegotiationResponse) WithHostLinkMethods(methods ...string) VersionNegotiationResponse

WithHostLinkMethods returns a copy of r whose advertised set is exactly methods; no methods clears it. The set is validated by Validate and MarshalJSON, not here, so a Host builds the reply the same way it sets Version.

type WireVersion

type WireVersion uint8

WireVersion identifies a Looprig session wire-contract version.

const CurrentWireVersion WireVersion = 1

CurrentWireVersion is version 1 of the Looprig session wire contract. It is intentionally independent of any transport or broker client-protocol version.

Jump to

Keyboard shortcuts

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