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.
HostLink addressing ¶
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 ¶
- Constants
- func CanonicalDeadlineUnixMillis(deadline time.Time) string
- func EncodeHostLinkConnectReply(response VersionNegotiationResponse) ([]byte, error)
- func EncodeHostLinkConnectRequest(request VersionNegotiationRequest) ([]byte, error)
- func HostLinkChannel(tenantID TenantID, sessionID SessionID) string
- type AgentCapabilitySummary
- type AgentID
- type CommandEnvelope
- type CommandID
- type CommandIDFramingCode
- type CommandIDFramingError
- type CommandState
- type CommandStatus
- type CreateRequest
- type Cursor
- type DepartmentCapabilitySummary
- type EnduringPublication
- func (p EnduringPublication) AdditionalFields() map[string]json.RawMessage
- func (p EnduringPublication) MarshalJSON() ([]byte, error)
- func (p EnduringPublication) RecordType() SessionRecordType
- func (p *EnduringPublication) UnmarshalJSON(data []byte) error
- func (p EnduringPublication) Validate() error
- type EphemeralPublication
- func (p EphemeralPublication) AdditionalFields() map[string]json.RawMessage
- func (p EphemeralPublication) MarshalJSON() ([]byte, error)
- func (p EphemeralPublication) RecordType() SessionRecordType
- func (p *EphemeralPublication) UnmarshalJSON(data []byte) error
- func (p EphemeralPublication) Validate() error
- type ErrorCode
- type ErrorDetail
- type ErrorEnvelope
- type EventID
- type GateAnswerability
- type GateControl
- type GateFieldKind
- type GateID
- type GatePage
- type GateProjection
- type GatePrompt
- type GatePromptField
- type GatePromptOption
- type GatePromptSchema
- type GateResponseRequest
- type HostID
- type HostIsolationClass
- type HostLinkAttachMode
- type HostLinkAttachRequest
- type HostLinkBindRequest
- type HostLinkCapacityReport
- type HostLinkCommandDelivery
- type HostLinkDrainObservation
- type HostLinkDrainRequest
- type HostLinkDrainState
- type HostLinkEndpointCode
- type HostLinkEndpointError
- type HostLinkError
- type HostLinkErrorCode
- type HostLinkRegistryObservation
- type HostLinkUnbindRequest
- type HostPlacement
- type IDValidationCode
- type IDValidationError
- type InputRequest
- type InternalEndpoint
- type InterruptRequest
- type JournalEvent
- type JournalPage
- type JournalTip
- type MessageMetadata
- type MetadataValidationCode
- type MetadataValidationError
- type ObjectMetadata
- type ObjectReference
- type Principal
- type PrincipalKind
- type RequestValidationCode
- type RequestValidationError
- type RestoreRequest
- type SessionID
- type SessionPage
- type SessionRecordType
- type SessionReset
- type SessionResidency
- type SessionState
- type SessionStatus
- type SessionSummary
- type SubjectID
- type TenantID
- type VersionNegotiationRequest
- type VersionNegotiationResponse
- func (r VersionNegotiationResponse) AdditionalFields() map[string]json.RawMessage
- func (r VersionNegotiationResponse) HostLinkMethods() []string
- func (r VersionNegotiationResponse) MarshalJSON() ([]byte, error)
- func (r VersionNegotiationResponse) Supports(method string) bool
- func (r *VersionNegotiationResponse) UnmarshalJSON(data []byte) error
- func (r VersionNegotiationResponse) Validate() error
- func (r VersionNegotiationResponse) WithHostLinkMethods(methods ...string) VersionNegotiationResponse
- type WireVersion
Examples ¶
Constants ¶
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" )
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.
const ( MaxMetadataFields = 16 MaxMetadataKeyBytes = 64 MaxMetadataValueBytes = 1024 MaxMetadataBytes = 4096 )
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.
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.
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.
const HostLinkChannelPrefix = "hostlink.v1."
HostLinkChannelPrefix is the leading segment of every HostLink session channel.
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.
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 ¶
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
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 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 ¶
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 ¶
UnmarshalJSON rejects malformed JSON string encodings before they can be normalized into a different command identity by encoding/json.
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" )
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 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" 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 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 (*GatePage) UnmarshalJSON ¶
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 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" 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
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
MarshalJSON validates p and preserves tenant, subject, kind member order.
func (*Principal) UnmarshalJSON ¶ added in v0.12.0
UnmarshalJSON strictly decodes a principal and refuses unknown members.
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 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.
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.