Documentation
¶
Index ¶
- Constants
- Variables
- func CanonicalBytes(env Envelope) ([]byte, error)
- func IsEventBusNotProvisioned(err error) bool
- func IsIngestNotProvisioned(err error) bool
- func IsIngestPermanentlyRefused(err error) bool
- func IsIngestTooLarge(err error) bool
- func RedactURLError(err error) error
- type APIError
- type ActionInfo
- type ActionParam
- type AuditBatch
- type AuditEntry
- type AuditEvent
- type BinaryInfo
- type BridgeInfo
- type BridgeSnapshot
- type CapabilityManifestRequest
- type Clock
- type Config
- type ConnectFunc
- type ControlPlane
- func (c *ControlPlane) ConnectSSE(ctx context.Context, nodeID, lastEventID string) (*http.Response, error)
- func (c *ControlPlane) DeleteStateReport(ctx context.Context, nodeID, key string) error
- func (c *ControlPlane) ExecutionCallback(ctx context.Context, nodeID, executionID string, req ExecutionCallbackRequest) (*ExecutionCallbackResponse, error)
- func (c *ControlPlane) FetchSecret(ctx context.Context, nodeID, name string, version int) (*SecretEnvelope, error)
- func (c *ControlPlane) FetchState(ctx context.Context, nodeID string) (*NodeStateSnapshot, error)
- func (c *ControlPlane) GetJSON(ctx context.Context, path string, result any) error
- func (c *ControlPlane) Heartbeat(ctx context.Context, nodeID string, req HeartbeatRequest) (*HeartbeatResponse, error)
- func (c *ControlPlane) PostJSON(ctx context.Context, path string, body any, result any) error
- func (c *ControlPlane) PutStateReport(ctx context.Context, nodeID, key string, req NodeStateReportRequest) (*NodeStateReportResponse, error)
- func (c *ControlPlane) Register(ctx context.Context, req RegisterRequest) (*RegisterResponse, error)
- func (c *ControlPlane) ReportAudit(ctx context.Context, nodeID string, events []AuditEvent) (*IngestReceipt, error)
- func (c *ControlPlane) ReportEndpoint(ctx context.Context, nodeID string, req EndpointRequest) (*EndpointResponse, error)
- func (c *ControlPlane) ReportIntegrityViolations(ctx context.Context, nodeID string, req IntegrityViolationsRequest) error
- func (c *ControlPlane) ReportLogs(ctx context.Context, nodeID string, lines []LogLine) (*IngestReceipt, error)
- func (c *ControlPlane) ReportMetrics(ctx context.Context, nodeID string, samples []MetricSample) (*IngestReceipt, error)
- func (c *ControlPlane) ReportSessionActivity(ctx context.Context, nodeID, sessionID string, req SessionActivityRequest) error
- func (c *ControlPlane) RotateKeys(ctx context.Context, req KeyRotateRequest) (*KeyRotateResponse, error)
- func (c *ControlPlane) SetAuthToken(token string)
- func (c *ControlPlane) UpdateCapabilities(ctx context.Context, nodeID string, caps CapabilityManifestRequest) error
- func (c *ControlPlane) UploadExecutionOutput(ctx context.Context, uploadURL string, output []byte) error
- type DataEntry
- type DeclaredHook
- type DeliveryMode
- type Ed25519Verifier
- type EndpointRequest
- type EndpointResponse
- type Envelope
- type EventDispatcher
- type EventHandler
- type EventVerifier
- type ExecutionCallbackRequest
- type ExecutionCallbackResponse
- type ExecutionOutput
- type FailureAction
- type HeartbeatRequest
- type HeartbeatResponse
- type HookInfo
- type IngestReceipt
- type IngressConfig
- type IngressInfo
- type IngressRule
- type IntegrityDetector
- type IntegrityViolationKind
- type IntegrityViolationReport
- type IntegrityViolationsRequest
- type IntegrityViolationsResponse
- type K8sActivity
- type KeyRotateRequest
- type KeyRotateResponse
- type LocalEndpointConfig
- type LogBatch
- type LogEntry
- type LogLine
- type MetricBatch
- type MetricPoint
- type MetricSample
- type NoOpVerifier
- type NodeStateBlock
- type NodeStateExecution
- type NodeStateReportRequest
- type NodeStateReportResponse
- type NodeStateSession
- type NodeStateSnapshot
- type Peer
- type PolicyRule
- type PolicySnapshot
- type PollFunc
- type PortRange
- type ReachabilitySnapshot
- type ReconcileTrigger
- type ReconnectEngine
- func (r *ReconnectEngine) Mode() DeliveryMode
- func (r *ReconnectEngine) Run(ctx context.Context, connectFn ConnectFunc, pollFn PollFunc) error
- func (r *ReconnectEngine) SetBaseInterval(d time.Duration)
- func (r *ReconnectEngine) SetClock(c Clock)
- func (r *ReconnectEngine) SetIntervals(base, max time.Duration)
- func (r *ReconnectEngine) SetOnAuthFailure(fn func())
- func (r *ReconnectEngine) SetOnModeChange(fn func(DeliveryMode))
- func (r *ReconnectEngine) SetPollInterval(d time.Duration)
- func (r *ReconnectEngine) SetPollingFallbackConfig(fallbackAfter, pollInterval time.Duration)
- func (r *ReconnectEngine) SetReprobeInterval(d time.Duration)
- type RegisterPeer
- type RegisterRequest
- type RegisterResponse
- type RelayConfig
- type RelaySessionAssignment
- type RetryCallback
- type SSEEvent
- type SSEManager
- func (m *SSEManager) Mode() DeliveryMode
- func (m *SSEManager) RegisterHandler(eventType string, handler EventHandler)
- func (m *SSEManager) SetIdleTimeout(d time.Duration)
- func (m *SSEManager) SetOnModeChange(fn func(DeliveryMode))
- func (m *SSEManager) SetPollFunc(fn PollFunc)
- func (m *SSEManager) SetPollingFallback(fallbackAfter, pollInterval time.Duration)
- func (m *SSEManager) SetReconcileTrigger(t ReconcileTrigger)
- func (m *SSEManager) SetReconnectIntervals(base, max time.Duration)
- func (m *SSEManager) SetReprobeInterval(d time.Duration)
- func (m *SSEManager) Shutdown()
- func (m *SSEManager) Start(ctx context.Context, nodeID string) error
- type SSEParser
- type SSEStream
- type SSHActivity
- type SecretEnvelope
- type SecretRef
- type SessionActivityRequest
- type SessionTarget
- type SessionTargetK8s
- type SessionTargetSSH
- type SessionTargetTCP
- type SigningKeyRotation
- type SiteToSiteConfig
- type SiteToSiteInfo
- type SiteToSiteTunnel
- type SnapshotPeer
- type StateEntry
- type TCPActivity
- type UserAccessConfig
- type UserAccessInfo
- type UserAccessPeer
Constants ¶
const ( // Contract types are emitted by the control plane per the OpenAPI events // document. Their payloads are treated as opaque: the reconciler pull is // authoritative, so each of these events triggers a reconcile. EventNodeStateUpdated = "node_state_updated" EventPolicyUpdated = "policy_updated" EventBridgeConfigUpdated = "bridge_config_updated" // EventActionRequest exists as a push-latency optimisation: action // dispatches are delivered in the executions block of the state pull, so the // event's payload is opaque and it triggers a reconcile like the rest of the // tier — the resulting pull carries the dispatch. EventActionRequest = "action_request" // EventSessionSetup is the same push-latency optimisation for mediated // access: sessions are delivered in the sessions block of the state pull, so // the event's payload is opaque and it triggers a reconcile like the rest of // the tier — the resulting pull carries the session. EventSessionSetup = "session_setup" // Documented-coming family: types that land with the platform's 14-type // taxonomy. They are already named so the agent can subscribe to them once // the control plane starts emitting them; every one triggers a reconcile. EventPeerRegistered = "peer_registered" EventPeerPSKAssigned = "peer_psk_assigned" EventPeerDeregistered = "peer_deregistered" EventPeerEndpointChanged = "peer_endpoint_changed" EventRotateKeys = "rotate_keys" EventPeerKeyRotated = "peer_key_rotated" EventSigningKeyRotated = "signing_key_rotated" // EventSessionRevoked carries no teardown of its own: a session leaving the // pull's sessions block is what closes it, so this event only pulls the // observing reconcile forward. EventSessionRevoked = "session_revoked" )
const ( // ReachabilityHealthy marks a node whose heartbeats arrive on schedule. ReachabilityHealthy = "healthy" // ReachabilityStale marks a node whose last admitted heartbeat is older than // the control plane's freshness window. ReachabilityStale = "stale" // ReachabilityUnreachable marks a node the control plane has given up on. ReachabilityUnreachable = "unreachable" // ReachabilityNeverReported marks a node whose first heartbeat was never // admitted. It is the verdict a freshly enrolled node is born with. ReachabilityNeverReported = "never_reported" )
Reachability verdicts the platform contract documents today. The set is open: these are the values plexd expects to see, not the values it accepts.
const ( // ActionKindBuiltin dispatches an action built into plexd. ActionKindBuiltin = "builtin" // ActionKindHook dispatches a hook registered by the node. ActionKindHook = "hook" )
The two legal values of a NodeStateExecution.Type.
const ( // ExecutionStatusAck acknowledges receipt of the action request. ExecutionStatusAck = "ack" // ExecutionStatusStarted reports that the action has begun running. ExecutionStatusStarted = "started" // ExecutionStatusSucceeded is the terminal callback for a successful run. ExecutionStatusSucceeded = "succeeded" // ExecutionStatusFailed is the terminal callback for a failed run. ExecutionStatusFailed = "failed" // ExecutionStatusCancelled is the terminal callback for a cancelled run. ExecutionStatusCancelled = "cancelled" )
Execution callback statuses a node reports on the v1 execution callback (POST /v1/nodes/{node_id}/executions/{execution_id}).
const ( // CodeNSKNodeMismatch (403) means the callback's node id does not match the // node identified by the NSK bearer credential. CodeNSKNodeMismatch = "nsk_node_mismatch" // CodeInvalidStateTransition (409) means the callback would advance the // invocation along an illegal edge of the execution state machine. CodeInvalidStateTransition = "invalid_state_transition" // CodeExecutionAlreadyTerminal (409) means the invocation has already // settled and accepts no further callbacks. CodeExecutionAlreadyTerminal = "execution_already_terminal" )
RFC 9457 problem codes with which the control plane refuses an execution callback. A refusal carrying one of these is deliberate and permanent: the node must stop driving the execution instead of retrying or double-reporting.
const ( // SessionKindSSH mediates an SSH session; Target.SSH is set. SessionKindSSH = "ssh" // SessionKindK8s mediates a Kubernetes API session; Target.K8s is set. SessionKindK8s = "k8s" // SessionKindTCP mediates a plain TCP forward; Target.TCP is set. SessionKindTCP = "tcp" )
The three legal values of a NodeStateSession.Kind.
const ( MetricGroupNodeResources = "node_resources" MetricGroupTunnelHealth = "tunnel_health" MetricGroupPeerLatency = "peer_latency" MetricGroupAgentStats = "agent_stats" )
Wire metric groups (closed set; out-of-set → 400 ingest_batch_malformed).
const ( // TCPPhaseSessionStarted marks the opening of a TCP session. TCPPhaseSessionStarted = "session_started" // TCPPhaseSessionEnded marks the close of a TCP session. TCPPhaseSessionEnded = "session_ended" )
TCP session lifecycle phases reported on a TCPActivity.
const ( // TerminatedByTTLExpired means the session reached its time-to-live. TerminatedByTTLExpired = "ttl_expired" // TerminatedByIdleTimeout means the session was idle past its timeout. TerminatedByIdleTimeout = "idle_timeout" // TerminatedByPlexdClose means plexd closed the session locally. TerminatedByPlexdClose = "plexd_close" // TerminatedByOperatorRevoke means the operator's access was revoked. TerminatedByOperatorRevoke = "operator_revoke" )
Reasons a TCP session was terminated, reported on a session_ended TCPActivity.
const DefaultConnectTimeout = 10 * time.Second
DefaultConnectTimeout is the default TCP connect timeout.
const DefaultRequestTimeout = 30 * time.Second
DefaultRequestTimeout is the default HTTP request timeout.
const DefaultSSEIdleTimeout = 90 * time.Second
DefaultSSEIdleTimeout is the default SSE idle timeout.
const DefaultSSEReprobeInterval = 10 * time.Minute
DefaultSSEReprobeInterval is how often pull-only delivery mode re-probes the descoped SSE endpoint to detect that it has come back.
const DefaultStalenessWindow = 5 * time.Minute
DefaultStalenessWindow is the maximum age of an event before it is considered stale.
const ( // ExecutionStatusPending marks an execution the control plane has dispatched // and the node has not yet acknowledged. It appears in the executions block // of the node state snapshot and is never reported by a node on the // execution callback. ExecutionStatusPending = "pending" )
Execution status observed only on the pull block.
const MaxIntegrityViolationsPerBatch = 128
MaxIntegrityViolationsPerBatch is the contract's per-request ceiling on the violations array. A larger batch is refused whole with 400 integrity_violations_too_many, and an empty one with integrity_violations_empty.
Variables ¶
var ( ErrBadRequest = &APIError{StatusCode: 400, Message: "bad request"} ErrForbidden = &APIError{StatusCode: 403, Message: "forbidden"} ErrNotFound = &APIError{StatusCode: 404, Message: "not found"} ErrConflict = &APIError{StatusCode: 409, Message: "conflict"} ErrPayloadTooLarge = &APIError{StatusCode: 413, Message: "payload too large"} ErrUnprocessable = &APIError{StatusCode: 422, Message: "unprocessable entity"} ErrRateLimit = &APIError{StatusCode: 429, Message: "rate limit exceeded"} ErrServer = &APIError{StatusCode: 500, Message: "server error"} )
Sentinel errors for common HTTP error status codes.
var ( ErrIntegrityViolationsEmpty = errors.New("integrity violation batch is empty") ErrIntegrityViolationsTooMany = fmt.Errorf("integrity violation batch exceeds %d entries", MaxIntegrityViolationsPerBatch) )
ErrIntegrityViolationsEmpty and ErrIntegrityViolationsTooMany are returned by ReportIntegrityViolations before any HTTP request when the batch is outside the contract's 1..MaxIntegrityViolationsPerBatch bounds.
var ErrSSEIdleTimeout = errors.New("api: SSE idle timeout")
ErrSSEIdleTimeout is returned when the SSE stream receives no data within the configured idle timeout period.
var ErrSecretNameInvalid = fmt.Errorf("secret name is outside the grammar %s", secretNamePattern)
ErrSecretNameInvalid is returned by FetchSecret before any HTTP request when the requested secret name is outside the contract's name grammar.
Functions ¶
func CanonicalBytes ¶ added in v0.2.0
CanonicalBytes returns the canonical signing bytes for an envelope: a JSON object with exactly the fields id, type, scope, key_id, issued_at, payload in that order and no signature member. A nil Payload serializes as "payload":null. It is the single canonical-form seam shared between the agent and the mock control plane; the control plane's own helper is unpublished, so this shape is an author-approved assumption until the platform documents it.
func IsEventBusNotProvisioned ¶ added in v0.2.0
IsEventBusNotProvisioned reports whether err is the control plane's long-term descope of the signed event stream for the node: a 501 carrying the signed_event_bus_not_provisioned problem code. Unlike a transient 5xx, this is a durable verdict — the endpoint stays unavailable until the node is re-provisioned — so it is answered by switching to pull-only delivery rather than by retrying against a channel that is not there.
func IsIngestNotProvisioned ¶ added in v0.2.0
IsIngestNotProvisioned reports whether err is the control plane's refusal to accept observability ingest because it is not provisioned for the node: a 501 carrying the observability_ingest_not_provisioned problem code.
func IsIngestPermanentlyRefused ¶ added in v0.2.0
IsIngestPermanentlyRefused reports whether err is a refusal of an observability ingest batch that no retry can fix: a 400 carrying the ingest_batch_malformed problem code, which is a verdict on the batch bytes themselves. Re-sending them would draw the identical status forever, so a PlatformReporter drops such a batch rather than returning it for re-buffering.
The sibling refusals are deliberately not permanent. A 400 ingest_sent_at_invalid faults the X-Plexsphere-Sent-At header, which is re-stamped from the wall clock on every attempt, so it clears once the node's clock converges. A 415 faults the Content-Encoding, a transport property of the deployment rather than of the batch, so it clears once the gateway that rejects gzip is fixed. Both are returned to the caller for re-buffering. A 413 is classified by IsIngestTooLarge and answered by splitting the batch, and a 501 not-provisioned refusal by IsIngestNotProvisioned.
func IsIngestTooLarge ¶ added in v0.2.0
IsIngestTooLarge reports whether err is the control plane's refusal of an observability ingest batch for exceeding its size limit: a 413. The batch content is acceptable, only its size is not, so the caller answers by splitting the batch and re-sending the halves rather than dropping it.
func RedactURLError ¶ added in v0.2.0
RedactURLError strips the request URL from a *url.Error, keeping only the operation and the underlying cause. url.Error.Error() renders the full URL including its query string, and a presigned upload URL carries its credential there — logging such an error verbatim would publish a live write capability against the object it points at. Errors of any other type pass through.
Types ¶
type APIError ¶
type APIError struct {
StatusCode int
Message string
// Code carries the machine-readable code member of an RFC 9457
// problem+json response. It is empty when the response carries no
// code (or is not a problem+json body).
Code string
// CorrelationID carries the correlation_id member of an RFC 9457
// problem+json response, falling back to the X-Correlation-Id header
// of that same response. Both sources are read only from a response
// that presents itself as a problem document, and that is the whole
// of what the gate buys: a response an intermediary substituted for
// the control plane's contributes no id. It does not authenticate the
// header, so a tracing proxy that passes a genuine problem document
// through and stamps its own X-Correlation-Id on it is read by the
// fallback. It is empty when neither source carries a usable id.
CorrelationID string
RetryAfter time.Duration // only set for 429
}
APIError is the base error type for HTTP API errors. It supports errors.Is matching by status code and errors.As extraction.
type ActionInfo ¶
type ActionInfo struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters []ActionParam `json:"parameters"`
}
type ActionParam ¶
type AuditBatch ¶
type AuditBatch = []AuditEntry
AuditBatch is the internal pipeline and local-endpoint payload for a batch of audit entries. The control-plane leg of POST /v1/nodes/{node_id}/audit sends AuditEvent instead.
type AuditEntry ¶
type AuditEntry struct {
Timestamp time.Time `json:"timestamp"`
Source string `json:"source"`
EventType string `json:"event_type"`
Subject json.RawMessage `json:"subject"`
Object json.RawMessage `json:"object"`
Action string `json:"action"`
Result string `json:"result"`
Hostname string `json:"hostname"`
Raw string `json:"raw"`
}
AuditEntry is the internal pipeline and local-endpoint format for a single audit record. The control-plane leg sends AuditEvent instead.
type AuditEvent ¶ added in v0.2.0
type AuditEvent struct {
Source string `json:"source"`
Action string `json:"action"`
Outcome string `json:"outcome"`
Timestamp time.Time `json:"timestamp"`
}
AuditEvent is the control-plane wire format for a single audit record in the body of POST /v1/nodes/{node_id}/audit.
type BinaryInfo ¶
BinaryInfo describes the running agent binary. It is reported through the node API's local surface; the control plane receives the same two values as the flat binary_version / binary_checksum fields of a capability manifest.
type BridgeInfo ¶
type BridgeInfo struct {
Enabled bool `json:"enabled"`
AccessInterface string `json:"access_interface"`
ActiveRoutes int `json:"active_routes"`
RelayEnabled bool `json:"relay_enabled"`
ActiveRelaySessions int `json:"active_relay_sessions"`
IngressEnabled bool `json:"ingress_enabled"`
ActiveIngressRules int `json:"active_ingress_rules"`
SiteToSiteEnabled bool `json:"site_to_site_enabled"`
ActiveSiteToSiteTunnels int `json:"active_site_to_site_tunnels"`
}
BridgeInfo is the bridge status reported by the node in heartbeats.
type BridgeSnapshot ¶ added in v0.2.0
type BridgeSnapshot struct {
Relay *RelayConfig `json:"relay"`
UserAccess *UserAccessConfig `json:"user_access"`
Ingress *IngressConfig `json:"ingress"`
SiteToSite *SiteToSiteConfig `json:"site_to_site"`
}
BridgeSnapshot carries the four bridge subtrees. Each child is present-but-nullable; there are no base fields on the wire.
type CapabilityManifestRequest ¶ added in v0.4.0
type CapabilityManifestRequest struct {
// BinaryVersion is the agent version string, non-empty after trimming.
BinaryVersion string `json:"binary_version"`
// BinaryChecksum is the running binary's SHA-256 as 32 raw bytes in
// standard-padded base64 (see integrity.WireChecksum). Anything that does
// not decode to exactly 32 bytes is refused with 400
// binary_checksum_invalid.
BinaryChecksum string `json:"binary_checksum"`
// SSHHostKeyFingerprint is the optional OpenSSH host-key fingerprint in the
// canonical `SHA256:<base64>` form. Omitted when the agent has none.
SSHHostKeyFingerprint string `json:"ssh_host_key_fingerprint,omitempty"`
// DeclaredHooks are the hooks the agent advertises, at most 128, with
// unique names. Omitted when empty rather than sent as null.
DeclaredHooks []DeclaredHook `json:"declared_hooks,omitempty"`
}
CapabilityManifestRequest is the body of PUT /v1/nodes/{node_id}/capabilities: the agent's binary version and digest, plus the hooks it advertises.
The handler decodes it with DisallowUnknownFields, so this struct carries the contract's fields and nothing else. In particular there is no field for the agent's builtin action list — the control plane has no column for it, and an earlier payload that sent one under `builtin_actions` (inside a nested `binary` object that the contract also does not have) was refused whole. The action list stays available locally through the node API's /v1/actions.
type Config ¶
type Config struct {
// BaseURL is the control plane API base URL (required).
// Example: "https://api.plexsphere.com"
BaseURL string `yaml:"base_url"`
// TLSInsecureSkipVerify disables TLS certificate verification.
// WARNING: Only use for development/testing.
TLSInsecureSkipVerify bool `yaml:"tls_insecure_skip_verify"`
// ConnectTimeout is the maximum time to wait for a TCP connection.
// Default: 10s
ConnectTimeout time.Duration `yaml:"connect_timeout"`
// RequestTimeout is the maximum time for a complete HTTP request/response cycle.
// Default: 30s
RequestTimeout time.Duration `yaml:"request_timeout"`
// SSEIdleTimeout is the maximum time to wait for any data on the SSE stream
// before considering the connection stale and reconnecting.
// Default: 90s
SSEIdleTimeout time.Duration `yaml:"sse_idle_timeout"`
// SSEReprobeInterval is how often pull-only delivery re-probes the SSE
// endpoint after the control plane descoped it.
// Default: 10m
SSEReprobeInterval time.Duration `yaml:"sse_reprobe_interval"`
}
Config holds the configuration for the ControlPlane client. Config is passed as a constructor argument — no file I/O in this package.
func (*Config) ApplyDefaults ¶
func (c *Config) ApplyDefaults()
ApplyDefaults sets default values for zero-valued fields.
type ConnectFunc ¶
ConnectFunc is called to establish an SSE connection. It should block while the connection is active and return when it drops.
type ControlPlane ¶
type ControlPlane struct {
// contains filtered or unexported fields
}
ControlPlane is the client for the Plexsphere control plane API.
func NewControlPlane ¶
NewControlPlane creates a new ControlPlane client with the given configuration.
func (*ControlPlane) ConnectSSE ¶
func (c *ControlPlane) ConnectSSE(ctx context.Context, nodeID, lastEventID string) (*http.Response, error)
ConnectSSE opens an SSE connection to the node event stream. The caller is responsible for closing the response body. GET /v1/nodes/{node_id}/events
func (*ControlPlane) DeleteStateReport ¶ added in v0.2.0
func (c *ControlPlane) DeleteStateReport(ctx context.Context, nodeID, key string) error
DeleteStateReport removes a single per-key node state report. A 204 No Content returns nil; a 404 report_not_found (and any other non-2xx status) surfaces as an *APIError through errorFromResponse. DELETE /v1/nodes/{node_id}/state/reports/{key}
func (*ControlPlane) ExecutionCallback ¶ added in v0.2.0
func (c *ControlPlane) ExecutionCallback(ctx context.Context, nodeID, executionID string, req ExecutionCallbackRequest) (*ExecutionCallbackResponse, error)
ExecutionCallback posts a single execution lifecycle callback and returns the server's new invocation status, plus a presigned output upload URL when the callback declares an over-ceiling output. POST /v1/nodes/{node_id}/executions/{execution_id}
func (*ControlPlane) FetchSecret ¶
func (c *ControlPlane) FetchSecret(ctx context.Context, nodeID, name string, version int) (*SecretEnvelope, error)
FetchSecret retrieves a specific secret for the node as the raw AES-256-GCM envelope served by the control plane: the octet-stream body carries <12-byte nonce> || <ciphertext + 16-byte GCM tag>, and the version and KID ride in the X-Plexsphere-Secret-Version and X-Plexsphere-Secret-KID headers. A version > 0 selects an older version via ?version=N; version == 0 is the current version. GET /v1/nodes/{node_id}/secrets/{name}
func (*ControlPlane) FetchState ¶
func (c *ControlPlane) FetchState(ctx context.Context, nodeID string) (*NodeStateSnapshot, error)
FetchState retrieves the desired-state snapshot for a node. GET /v1/nodes/{node_id}/state
func (*ControlPlane) Heartbeat ¶
func (c *ControlPlane) Heartbeat(ctx context.Context, nodeID string, req HeartbeatRequest) (*HeartbeatResponse, error)
Heartbeat sends a heartbeat to the control plane. POST /v1/nodes/{node_id}/heartbeat
func (*ControlPlane) PostJSON ¶
PostJSON sends a POST request with a JSON body and decodes the JSON response.
func (*ControlPlane) PutStateReport ¶ added in v0.2.0
func (c *ControlPlane) PutStateReport(ctx context.Context, nodeID, key string, req NodeStateReportRequest) (*NodeStateReportResponse, error)
PutStateReport publishes a single per-key node state report and returns the server's acknowledgement. PUT /v1/nodes/{node_id}/state/reports/{key}
func (*ControlPlane) Register ¶
func (c *ControlPlane) Register(ctx context.Context, req RegisterRequest) (*RegisterResponse, error)
Register sends a registration request to the control plane. POST /v1/register is security: [] — the bootstrap token travels in the body, so the request never carries the shared bearer token even if one is set.
func (*ControlPlane) ReportAudit ¶
func (c *ControlPlane) ReportAudit(ctx context.Context, nodeID string, events []AuditEvent) (*IngestReceipt, error)
ReportAudit posts a batch of audit events to the platform ingest endpoint as NDJSON (one JSON object per line) and returns the 202 ingest receipt. POST /v1/nodes/{node_id}/audit
func (*ControlPlane) ReportEndpoint ¶
func (c *ControlPlane) ReportEndpoint(ctx context.Context, nodeID string, req EndpointRequest) (*EndpointResponse, error)
ReportEndpoint reports the node's NAT endpoint information. PUT /v1/nodes/{node_id}/endpoint
func (*ControlPlane) ReportIntegrityViolations ¶ added in v0.4.0
func (c *ControlPlane) ReportIntegrityViolations(ctx context.Context, nodeID string, req IntegrityViolationsRequest) error
ReportIntegrityViolations reports a batch of integrity violations to the control plane. The 200 response carries a commit timestamp and an echo of the batch size; the agent keeps no replay queue to reconcile them against, so the body is not decoded and a non-2xx surfaces as an *APIError.
The batch bounds are checked here rather than left to the server: the two codes it answers with (integrity_violations_empty, integrity_violations_too_many) describe a caller mistake, and a refused request would take every violation in it down. POST /v1/nodes/{node_id}/integrity-violations
func (*ControlPlane) ReportLogs ¶
func (c *ControlPlane) ReportLogs(ctx context.Context, nodeID string, lines []LogLine) (*IngestReceipt, error)
ReportLogs posts a batch of log lines to the platform ingest endpoint as NDJSON (one JSON object per line) and returns the 202 ingest receipt. POST /v1/nodes/{node_id}/logs
func (*ControlPlane) ReportMetrics ¶
func (c *ControlPlane) ReportMetrics(ctx context.Context, nodeID string, samples []MetricSample) (*IngestReceipt, error)
ReportMetrics posts a batch of metric samples to the platform ingest endpoint as a JSON array and returns the 202 ingest receipt. POST /v1/nodes/{node_id}/metrics
func (*ControlPlane) ReportSessionActivity ¶ added in v0.2.0
func (c *ControlPlane) ReportSessionActivity(ctx context.Context, nodeID, sessionID string, req SessionActivityRequest) error
ReportSessionActivity posts a one-of session activity record (ssh, k8s, or tcp). Success is 204 No Content. POST /v1/nodes/{node_id}/sessions/{session_id}
func (*ControlPlane) RotateKeys ¶
func (c *ControlPlane) RotateKeys(ctx context.Context, req KeyRotateRequest) (*KeyRotateResponse, error)
RotateKeys completes a pending mesh-key rotation; the server identifies the node from the NSK bearer credential. POST /v1/keys/rotate
func (*ControlPlane) SetAuthToken ¶
func (c *ControlPlane) SetAuthToken(token string)
SetAuthToken sets the bearer token used for API authentication.
func (*ControlPlane) UpdateCapabilities ¶
func (c *ControlPlane) UpdateCapabilities(ctx context.Context, nodeID string, caps CapabilityManifestRequest) error
UpdateCapabilities publishes the node's capability manifest. PUT /v1/nodes/{node_id}/capabilities
func (*ControlPlane) UploadExecutionOutput ¶ added in v0.2.0
func (c *ControlPlane) UploadExecutionOutput(ctx context.Context, uploadURL string, output []byte) error
UploadExecutionOutput PUTs an over-ceiling execution output to a presigned URL. Presigned URLs carry their own authentication, so this request sends neither the bearer token nor gzip encoding; it uploads the raw bytes with a Content-Type of application/octet-stream.
The upload URL comes from the control plane, and captured action output routinely contains configuration and credentials, so the transport is pinned twice over: the URL may not be less secure than the configured control-plane base URL (an https control plane can never downgrade an upload to http), and redirects are not followed — a 3xx is surfaced as an error rather than re-sending the body to whatever host the redirect names.
type DeclaredHook ¶ added in v0.4.0
type DeclaredHook struct {
Name string `json:"name"`
// Checksum is the hook payload's SHA-256 as 32 raw bytes in
// standard-padded base64, same encoding as BinaryChecksum.
Checksum string `json:"checksum"`
}
DeclaredHook pairs a hook name with the digest of its payload, so the integrity correlator can see a hook change without re-fetching it.
type DeliveryMode ¶ added in v0.2.0
type DeliveryMode string
DeliveryMode identifies which channel currently delivers control-plane state.
const ( // DeliveryModeStreaming means the SSE event stream is the live channel. DeliveryModeStreaming DeliveryMode = "streaming" // DeliveryModePullOnly means the event stream is descoped and the // reconciler's own loop is the only delivery channel; SSE is re-probed at // a low cadence to detect its return. DeliveryModePullOnly DeliveryMode = "pull_only" // DeliveryModeDegradedPolling means SSE has been failing transiently long // enough that the legacy polling fallback is driving state. DeliveryModeDegradedPolling DeliveryMode = "degraded_polling" )
type Ed25519Verifier ¶
type Ed25519Verifier struct {
// contains filtered or unexported fields
}
Ed25519Verifier verifies Envelope signatures using Ed25519 keys selected by key id. It supports rotation by holding a current key and an optional previous key that stays accepted until a transition deadline.
func NewEd25519Verifier ¶
func NewEd25519Verifier(keyID string, key ed25519.PublicKey) *Ed25519Verifier
NewEd25519Verifier returns a new verifier that accepts envelopes signed with the given key and carrying the given key id.
func (*Ed25519Verifier) Rotate ¶ added in v0.2.0
func (v *Ed25519Verifier) Rotate(rot SigningKeyRotation) error
Rotate installs a new current signing key from a signing_key_rotated event. The previous key is retained as a grace entry only when the rotation names a previous key id the verifier currently holds and a non-zero transition deadline; otherwise no grace entry is kept. On any error the installed keys are left unchanged.
type EndpointRequest ¶ added in v0.2.0
type EndpointResponse ¶
type Envelope ¶ added in v0.2.0
type Envelope struct {
ID string `json:"id"`
Type string `json:"type"`
Scope string `json:"scope"`
KeyID string `json:"key_id"`
IssuedAt time.Time `json:"issued_at"`
Payload json.RawMessage `json:"payload"`
Signature string `json:"signature"`
}
Envelope is the wire format for signed events the control plane streams over SSE. It matches the control plane's OpenAPI v1 events contract.
func ParseEnvelope ¶
ParseEnvelope unmarshals data into an Envelope and validates required fields.
type EventDispatcher ¶
type EventDispatcher struct {
// contains filtered or unexported fields
}
EventDispatcher routes verified events to registered handlers by event type.
func NewEventDispatcher ¶
func NewEventDispatcher(logger *slog.Logger) *EventDispatcher
NewEventDispatcher creates a new EventDispatcher.
func (*EventDispatcher) Dispatch ¶
func (d *EventDispatcher) Dispatch(ctx context.Context, envelope Envelope)
Dispatch invokes all handlers registered for the event's type. Handler errors are logged but do not stop processing of subsequent handlers. Events with no registered handler are logged at debug level and discarded.
func (*EventDispatcher) Register ¶
func (d *EventDispatcher) Register(eventType string, handler EventHandler)
Register adds a handler for the given event type. Multiple handlers can be registered for the same event type.
type EventHandler ¶
EventHandler is a function that handles a verified SSE event.
type EventVerifier ¶
EventVerifier verifies the signature of an Envelope.
type ExecutionCallbackRequest ¶ added in v0.2.0
type ExecutionCallbackRequest struct {
Status string `json:"status"`
ExitCode *int `json:"exit_code,omitempty"`
Error string `json:"error,omitempty"`
DeclaredOutputBytes int64 `json:"declared_output_bytes,omitempty"`
Output *ExecutionOutput `json:"output,omitempty"`
}
ExecutionCallbackRequest is the single callback a node posts to POST /v1/nodes/{node_id}/executions/{execution_id} to advance an execution through its lifecycle. Status is one of the ExecutionStatus* values. ExitCode is a pointer so a terminal callback can report an explicit zero. Error is set on failed terminals. DeclaredOutputBytes is the byte length of the captured output; a declaration over the 16 KiB inline ceiling drives the presign mint.
type ExecutionCallbackResponse ¶ added in v0.2.0
type ExecutionCallbackResponse struct {
Status string `json:"status"`
OutputUploadURL string `json:"output_upload_url,omitempty"`
}
ExecutionCallbackResponse is the 200 response to an execution callback. Status is the new invocation status. OutputUploadURL is a presigned PUT URL present only on the first callback that declares an over-ceiling output; the node derives the object key from the URL path and uploads the bytes there.
type ExecutionOutput ¶ added in v0.2.0
type ExecutionOutput struct {
Inline string `json:"inline,omitempty"`
ObjectKey string `json:"object_key,omitempty"`
SHA256 string `json:"sha256,omitempty"`
}
ExecutionOutput carries an execution's captured output on a terminal callback. Inline is the base64-encoded output body and is used only when it is at most 16 KiB. ObjectKey and SHA256 describe an already-uploaded over-ceiling output: ObjectKey is the object-store key and SHA256 is the lowercase-hex SHA-256 of the uploaded bytes.
type FailureAction ¶
type FailureAction int
FailureAction indicates how the reconnect engine should handle a failure.
const ( // RetryTransient means use exponential backoff (network errors, 5xx). RetryTransient FailureAction = iota // RetryAuth means invoke OnAuthFailure callback and pause (401). RetryAuth // RespectServer means use the server-provided Retry-After delay (429). RespectServer // PermanentFailure means stop reconnection entirely (403, 404). PermanentFailure // RetryDescoped means the endpoint is descoped long-term (a 501 // signed_event_bus_not_provisioned) — switch to pull-only delivery // immediately, with no backoff, rather than retrying a channel that is // not there. RetryDescoped )
func ClassifyError ¶
func ClassifyError(err error) FailureAction
ClassifyError determines the appropriate reconnection action for an error.
type HeartbeatRequest ¶
type HeartbeatResponse ¶
type IngestReceipt ¶ added in v0.2.0
type IngestReceipt struct {
AcceptedAt time.Time `json:"accepted_at"`
Records int `json:"records"`
}
IngestReceipt is the 202 body of the three ingest operations (POST /v1/nodes/{node_id}/metrics|logs|audit). Records is the number of records the control plane accepted from the batch.
type IngressConfig ¶
type IngressConfig struct {
Enabled bool `json:"enabled"`
Rules []IngressRule `json:"rules"`
}
IngressConfig is the ingress configuration pushed from the control plane.
type IngressInfo ¶
type IngressInfo struct {
Enabled bool `json:"enabled"`
RuleCount int `json:"rule_count"`
ConnectionCount int `json:"connection_count"`
ACMEEnabled bool `json:"acme_enabled"`
}
IngressInfo is the ingress status reported by the node in heartbeats.
type IngressRule ¶
type IngressRule struct {
RuleID string `json:"rule_id"`
ListenPort int `json:"listen_port"`
TargetAddr string `json:"target_addr"`
// Mode is the TLS handling mode: "tcp" (passthrough), "terminate" (static cert),
// or "acme" (automatic certificate via ACME).
Mode string `json:"mode"`
CertPEM string `json:"cert_pem,omitempty"`
KeyPEM string `json:"key_pem,omitempty"`
Hostname string `json:"hostname,omitempty"`
}
IngressRule represents a single public ingress rule.
type IntegrityDetector ¶ added in v0.4.0
type IntegrityDetector string
IntegrityDetector is the on-node detector that surfaced a violation. Like IntegrityViolationKind the set is closed; a value outside it is refused with 400 integrity_violation_detected_by_invalid.
const ( // IntegrityDetectorStartupScan is the agent's own sweep of the artifacts it // holds baselines for. The contract has no value for a periodic re-scan, so // the interval-driven sweep reports this one too. IntegrityDetectorStartupScan IntegrityDetector = "startup_scan" // IntegrityDetectorInotify is the hooks-directory watcher reacting to a // filesystem event. IntegrityDetectorInotify IntegrityDetector = "inotify" // IntegrityDetectorPreDispatch is the check that runs immediately before a // hook executes. IntegrityDetectorPreDispatch IntegrityDetector = "pre_dispatch" )
type IntegrityViolationKind ¶ added in v0.4.0
type IntegrityViolationKind string
IntegrityViolationKind is the artifact class a violation applies to. The control plane canonicalises every entry through a value-object constructor that refuses anything outside this set with 400 integrity_violation_kind_invalid, so the type exists to keep an unchecked string from reaching the wire in the first place.
const ( // IntegrityKindBinaryChecksum reports the agent's own binary. IntegrityKindBinaryChecksum IntegrityViolationKind = "binary_checksum" // IntegrityKindHookChecksum reports a hook script. IntegrityKindHookChecksum IntegrityViolationKind = "hook_checksum" // IntegrityKindSSHHostKey reports the mesh SSH host key. IntegrityKindSSHHostKey IntegrityViolationKind = "ssh_host_key" )
type IntegrityViolationReport ¶
type IntegrityViolationReport struct {
Kind IntegrityViolationKind `json:"kind"`
DetectedBy IntegrityDetector `json:"detected_by"`
// ArtifactID identifies the affected artifact — a hook path, the binary
// path, or the host-key file. Non-empty after trimming, at most 4096 bytes.
ArtifactID string `json:"artifact_id"`
// ObservedChecksum is the SHA-256 the agent computed, as 32 raw bytes in
// standard-padded base64 (see integrity.WireChecksum). Required for the
// checksum kinds; anything that does not decode to exactly 32 bytes is
// refused with 400 integrity_violation_checksum_invalid.
ObservedChecksum string `json:"observed_checksum,omitempty"`
// ExpectedChecksum is the baseline digest, same encoding as
// ObservedChecksum.
ExpectedChecksum string `json:"expected_checksum,omitempty"`
// ObservedFingerprint is the OpenSSH host-key fingerprint the agent
// observed, in the canonical `SHA256:<base64>` form. Required for the
// ssh_host_key kind.
ObservedFingerprint string `json:"observed_fingerprint,omitempty"`
// ExpectedFingerprint is the baseline fingerprint, same form as
// ObservedFingerprint.
ExpectedFingerprint string `json:"expected_fingerprint,omitempty"`
}
IntegrityViolationReport is one entry of an IntegrityViolationsRequest.
The handler decodes the envelope with DisallowUnknownFields, so this struct carries the contract's fields and nothing else — in particular there is no timestamp (the control plane stamps its own) and no free-text detail field.
Kind decides which digest pair is legal. A binary_checksum or hook_checksum entry carries the checksums and no fingerprint; an ssh_host_key entry carries the fingerprints and no checksum. Crossing them is refused with 400 integrity_violation_kind_mismatch, which is why the four digest fields are omitempty: an unset one must be absent from the JSON, not present and empty.
type IntegrityViolationsRequest ¶ added in v0.4.0
type IntegrityViolationsRequest struct {
// Violations carries between 1 and MaxIntegrityViolationsPerBatch entries.
Violations []IntegrityViolationReport `json:"violations"`
}
IntegrityViolationsRequest is the body of POST /v1/nodes/{node_id}/integrity-violations: a batch of the tamper-evidence divergences the agent detected locally. The endpoint takes a batch even when the agent has a single violation to report, so a lone finding travels as a one-entry array rather than as a bare object.
type IntegrityViolationsResponse ¶ added in v0.4.0
type IntegrityViolationsResponse struct {
// AcceptedAt is the server-side commit timestamp.
AcceptedAt time.Time `json:"accepted_at"`
// ViolationCount echoes the number of rows persisted, matching the length
// of the violations array on input.
ViolationCount int `json:"violation_count"`
}
IntegrityViolationsResponse is the 200 body of POST /v1/nodes/{node_id}/integrity-violations. The work is synchronous — every row and the integrity_alert outbox event are committed before the response is written — so the status is 200 rather than 202.
type K8sActivity ¶ added in v0.2.0
type K8sActivity struct {
Verb string `json:"verb"`
ResourceKind string `json:"resource_kind,omitempty"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
StatusCode int `json:"status_code,omitempty"`
DurationMS int64 `json:"duration_ms,omitempty"`
}
K8sActivity records a single Kubernetes API action proxied through the session. Verb is the API verb; the remaining fields describe the target object and the outcome.
type KeyRotateRequest ¶
type KeyRotateRequest struct {
NewPublicKey string `json:"new_public_key"`
}
type KeyRotateResponse ¶
type LocalEndpointConfig ¶
type LocalEndpointConfig struct {
// URL is the HTTPS endpoint URL. Must use the https:// scheme when set.
URL string `yaml:"url"`
// SecretKey is the authentication credential for the local endpoint.
// Required when URL is non-empty.
SecretKey string `yaml:"secret_key"`
// TLSInsecureSkipVerify disables TLS certificate verification.
TLSInsecureSkipVerify bool `yaml:"tls_insecure_skip_verify"`
}
LocalEndpointConfig holds the configuration for a local data-plane endpoint that a pipeline can send data to in addition to the platform. A zero-valued LocalEndpointConfig means "not configured" and passes validation.
func (*LocalEndpointConfig) Validate ¶
func (c *LocalEndpointConfig) Validate(prefix string) error
Validate checks that the local endpoint configuration is well-formed. The prefix is prepended to error messages for context (e.g. "metrics").
type LogBatch ¶
type LogBatch = []LogEntry
LogBatch is the internal pipeline and local-endpoint payload for a batch of log entries. The control-plane leg of POST /v1/nodes/{node_id}/logs sends LogLine instead.
type LogEntry ¶
type LogEntry struct {
Timestamp time.Time `json:"timestamp"`
Source string `json:"source"`
Unit string `json:"unit"`
Message string `json:"message"`
Severity string `json:"severity"`
Hostname string `json:"hostname"`
}
LogEntry is the internal pipeline and local-endpoint format for a single log record. The control-plane leg sends LogLine instead.
type LogLine ¶ added in v0.2.0
type LogLine struct {
Severity string `json:"severity"`
Unit string `json:"unit,omitempty"`
Hostname string `json:"hostname,omitempty"`
Message string `json:"message"`
Timestamp time.Time `json:"timestamp"`
}
LogLine is the control-plane wire format for a single log record in the body of POST /v1/nodes/{node_id}/logs. Unit and Hostname are absent when unknown.
type MetricBatch ¶
type MetricBatch = []MetricPoint
MetricBatch is the internal pipeline and local-endpoint payload for a batch of metric points. The control-plane leg of POST /v1/nodes/{node_id}/metrics sends MetricSample instead.
type MetricPoint ¶
type MetricPoint struct {
Timestamp time.Time `json:"timestamp"`
Group string `json:"group"`
PeerID string `json:"peer_id,omitempty"`
Data json.RawMessage `json:"data"`
}
MetricPoint is the internal pipeline and local-endpoint format for a single metric. The control-plane leg sends MetricSample instead.
type MetricSample ¶ added in v0.2.0
type MetricSample struct {
Group string `json:"group"`
Name string `json:"name"`
Value float64 `json:"value"`
Labels map[string]string `json:"labels,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
MetricSample is the control-plane wire format for a single metric in the body of POST /v1/nodes/{node_id}/metrics. Group is one of the MetricGroup* values; Labels is absent when the sample carries no dimensions.
type NoOpVerifier ¶
type NoOpVerifier struct{}
NoOpVerifier is an EventVerifier that accepts all envelopes without verification.
type NodeStateBlock ¶ added in v0.2.0
type NodeStateBlock struct {
Metadata []StateEntry `json:"metadata"`
Data []StateEntry `json:"data"`
Reports []StateEntry `json:"reports"`
}
NodeStateBlock is a three-bucket state block. Each bucket is a required array (never null when the block is populated), with entries ordered by key ascending.
type NodeStateExecution ¶ added in v0.3.0
type NodeStateExecution struct {
ExecutionID string `json:"execution_id"`
Action string `json:"action"`
Type string `json:"type"`
Parameters map[string]json.RawMessage `json:"parameters"`
Status string `json:"status"`
RequestedAt time.Time `json:"requested_at"`
ExpiresAt time.Time `json:"expires_at"`
}
NodeStateExecution is one pending action dispatch in the executions block of GET /v1/nodes/{node_id}/state. That block is the delivery channel for action dispatches: it is always present ([] when empty, never null) and ordered by RequestedAt, then ExecutionID. An entry keeps reappearing on every pull until its execution reaches a terminal status through the execution callback, so a consumer must tolerate re-observing the same ExecutionID.
Type is one of the ActionKind* values; Status is ExecutionStatusPending, ExecutionStatusAck, or ExecutionStatusStarted. ExpiresAt is an absolute UTC deadline, not a relative timeout. Parameters is nullable: a JSON null decodes to a nil map. The entry carries no callback URL and no hook checksum.
A parameter value is held as raw JSON, not decoded into any: the state response is decoded with a plain decoder, so a JSON number would land in an any as a float64 and every integer beyond 2^53 would reach the action rewritten. The raw form hands the value to the action exactly as the control plane sent it.
type NodeStateReportRequest ¶ added in v0.2.0
type NodeStateReportRequest struct {
Value string `json:"value"`
WorkloadTag string `json:"workload_tag,omitempty"`
}
NodeStateReportRequest is the control-plane wire format for the body of PUT /v1/nodes/{id}/state/reports/{key}. Value is the opaque report payload; WorkloadTag attributes the report to a workload and is absent when the report is unattributed.
type NodeStateReportResponse ¶ added in v0.2.0
type NodeStateReportResponse struct {
AcceptedAt time.Time `json:"accepted_at"`
Key string `json:"key"`
}
NodeStateReportResponse is the control-plane wire format for the 200 body of PUT /v1/nodes/{id}/state/reports/{key} (and DELETE /v1/nodes/{id}/state/ reports/{key}). Key echoes the report key the operation addressed.
type NodeStateSession ¶ added in v0.3.0
type NodeStateSession struct {
SessionID string `json:"session_id"`
JTI string `json:"jti"`
Kind string `json:"kind"`
Target SessionTarget `json:"target"`
ExpiresAt time.Time `json:"expires_at"`
IdleTimeoutSeconds int `json:"idle_timeout_seconds,omitempty"`
}
NodeStateSession is one live mediated-access session in the sessions block of GET /v1/nodes/{node_id}/state. That block is desired state, not a queue: it is always present on the wire ([] when empty, never null). An entry appears when the control plane issues the session and disappears on revocation or hard expiry — the disappearance is the teardown signal, there is no separate teardown event. A response that violates the contract by omitting or nulling the block decodes to a nil NodeStateSnapshot.Sessions and is not read as an empty block.
Kind is one of the SessionKind* values and selects which member of Target is set. JTI equals the session id: it is carried as an opaque value and never evaluated. ExpiresAt is an absolute UTC timestamp, not a relative timeout. IdleTimeoutSeconds 0 or absent means the session has no idle window.
type NodeStateSnapshot ¶ added in v0.2.0
type NodeStateSnapshot struct {
Peers []SnapshotPeer `json:"peers"`
Reachability json.RawMessage `json:"reachability"`
Policy *PolicySnapshot `json:"policy"`
Bridge *BridgeSnapshot `json:"bridge"`
State *NodeStateBlock `json:"state"`
Reports *NodeStateBlock `json:"reports"`
Executions []NodeStateExecution `json:"executions"`
// Sessions is a pointer for the same reason the block fields above are: the
// sessions block is desired state whose emptiness is destructive — an empty
// block tears every live session down — so "the control plane says you have
// no sessions" ([]) has to stay distinguishable from "the control plane did
// not populate the block" (null, or a key a build predating the block never
// wrote). A nil pointer is the second case and is not a teardown signal.
Sessions *[]NodeStateSession `json:"sessions"`
}
NodeStateSnapshot is the desired-state envelope returned by GET /v1/nodes/{node_id}/state. Every block key is always present on the wire: a null value means "block not populated", never "field absent". The differ therefore distinguishes a nil pointer from a populated block, so none of the eight fields carry omitempty.
type Peer ¶
type Peer struct {
ID string `json:"id"`
PublicKey string `json:"public_key"`
MeshIP string `json:"mesh_ip"`
Endpoint string `json:"endpoint"`
AllowedIPs []string `json:"allowed_ips"`
PSK string `json:"psk"`
}
Peer is the internal WireGuard programming shape built by peerFromSnapshot.
type PolicyRule ¶
type PolicyRule struct {
Action string `json:"action"`
Protocol string `json:"protocol"`
SourceCIDR string `json:"source_cidr"`
DestinationCIDR string `json:"destination_cidr"`
Ports *PortRange `json:"ports,omitempty"`
}
PolicyRule is a five-tuple firewall rule. Ports is present iff Protocol is tcp or udp and is absent for icmp/any.
type PolicySnapshot ¶ added in v0.2.0
type PolicySnapshot struct {
RevisionID string `json:"revision_id"`
Fingerprint string `json:"fingerprint"`
Rules []PolicyRule `json:"rules"`
}
PolicySnapshot is the single merged policy block. Fingerprint is a 44-char base64 SHA-256 over the server's canonical rule byte stream; plexd treats it as an opaque comparison key and never re-derives it from Rules.
type PortRange ¶ added in v0.2.0
PortRange is a single inclusive destination port range (from <= to).
type ReachabilitySnapshot ¶ added in v0.7.0
type ReachabilitySnapshot struct {
State string `json:"state"`
LastHeartbeatAt *time.Time `json:"last_heartbeat_at,omitempty"`
ChangedAt time.Time `json:"changed_at"`
}
ReachabilitySnapshot is the interior of the snapshot's reachability block: the control plane's own verdict about this node, derived from the heartbeats it has admitted. It is a diagnostic projection, never desired state.
State is a plain string and is never validated against the constants below. The verdict vocabulary belongs to the control plane and grows there — never_reported was added after plexd shipped — so a value this build does not know must keep flowing to the log rather than be rejected. LastHeartbeatAt is absent until the first heartbeat is accepted; ChangedAt is always present.
type ReconcileTrigger ¶ added in v0.2.0
type ReconcileTrigger interface {
TriggerReconcile()
}
ReconcileTrigger requests a full state reconcile. *reconcile.Reconciler satisfies it.
type ReconnectEngine ¶
type ReconnectEngine struct {
// contains filtered or unexported fields
}
ReconnectEngine manages SSE reconnection with backoff and polling fallback.
func NewReconnectEngine ¶
func NewReconnectEngine(logger *slog.Logger) *ReconnectEngine
NewReconnectEngine creates a new ReconnectEngine with default settings.
func (*ReconnectEngine) Mode ¶ added in v0.2.0
func (r *ReconnectEngine) Mode() DeliveryMode
Mode returns the delivery mode currently reported by the engine.
func (*ReconnectEngine) Run ¶
func (r *ReconnectEngine) Run(ctx context.Context, connectFn ConnectFunc, pollFn PollFunc) error
Run is the main state machine loop that manages SSE reconnection.
States: Connecting -> Connected | Backoff
Backoff -> Connecting | Polling Polling -> Connecting (periodic SSE retry)
Context cancellation exits from any state.
func (*ReconnectEngine) SetBaseInterval ¶
func (r *ReconnectEngine) SetBaseInterval(d time.Duration)
SetBaseInterval updates the base backoff interval. This is called when the SSE retry: field is received from the server.
func (*ReconnectEngine) SetClock ¶
func (r *ReconnectEngine) SetClock(c Clock)
SetClock sets a custom clock implementation for testing.
func (*ReconnectEngine) SetIntervals ¶
func (r *ReconnectEngine) SetIntervals(base, max time.Duration)
SetIntervals configures the base and max backoff intervals and resets the current interval to the new base. Useful for testing with fast intervals.
func (*ReconnectEngine) SetOnAuthFailure ¶
func (r *ReconnectEngine) SetOnAuthFailure(fn func())
SetOnAuthFailure sets the callback invoked on authentication failures.
func (*ReconnectEngine) SetOnModeChange ¶ added in v0.2.0
func (r *ReconnectEngine) SetOnModeChange(fn func(DeliveryMode))
SetOnModeChange sets the callback invoked on every delivery-mode transition. The callback is fired outside the engine's lock and may be nil.
func (*ReconnectEngine) SetPollInterval ¶
func (r *ReconnectEngine) SetPollInterval(d time.Duration)
SetPollInterval sets how often to poll during polling fallback mode.
func (*ReconnectEngine) SetPollingFallbackConfig ¶
func (r *ReconnectEngine) SetPollingFallbackConfig(fallbackAfter, pollInterval time.Duration)
SetPollingFallbackConfig configures when to enter polling mode and how often to poll.
func (*ReconnectEngine) SetReprobeInterval ¶ added in v0.2.0
func (r *ReconnectEngine) SetReprobeInterval(d time.Duration)
SetReprobeInterval sets how often pull-only mode re-probes the SSE endpoint. A non-positive duration is ignored.
type RegisterPeer ¶ added in v0.2.0
type RegisterPeer struct {
NodeID string `json:"node_id"`
MeshIP string `json:"mesh_ip"`
PublicKey string `json:"public_key"`
FallbackEndpoint string `json:"fallback_endpoint,omitempty"`
}
RegisterPeer is the initial peer snapshot entry returned by POST /v1/register. It is deliberately narrow: it carries NO psk, allowed_ips, or endpoint. The reconciliation peer shape is SnapshotPeer.
type RegisterRequest ¶
type RegisterResponse ¶
type RegisterResponse struct {
NodeID string `json:"node_id"`
MeshIP string `json:"mesh_ip"`
SigningPublicKey string `json:"signing_public_key"`
SigningKeyID string `json:"signing_key_id"`
NSK string `json:"nsk"`
PeerSnapshot []RegisterPeer `json:"peer_snapshot"`
DomainMeshCIDR string `json:"domain_mesh_cidr"`
}
type RelayConfig ¶
type RelayConfig struct {
Sessions []RelaySessionAssignment `json:"sessions"`
}
RelayConfig is the relay configuration pushed from the control plane. It contains the list of relay session assignments for this bridge node.
type RelaySessionAssignment ¶
type RelaySessionAssignment struct {
SessionID string `json:"session_id"`
PeerAID string `json:"peer_a_id"`
PeerAEndpoint string `json:"peer_a_endpoint"`
PeerBID string `json:"peer_b_id"`
PeerBEndpoint string `json:"peer_b_endpoint"`
ExpiresAt time.Time `json:"expires_at"`
}
RelaySessionAssignment represents a relay session assigned by the control plane.
type RetryCallback ¶
RetryCallback is called when the SSE server sends a retry: field.
type SSEEvent ¶
type SSEEvent struct {
Type string // from "event:" field, defaults to "message"
Data string // concatenated data fields
ID string // from "id:" field
}
SSEEvent represents a single parsed SSE event.
type SSEManager ¶
type SSEManager struct {
// contains filtered or unexported fields
}
SSEManager is the top-level orchestrator that wires SSEStream, ReconnectEngine, EventVerifier, and EventDispatcher together.
func NewSSEManager ¶
func NewSSEManager(client *ControlPlane, verifier EventVerifier, logger *slog.Logger) *SSEManager
NewSSEManager creates a new SSEManager. If verifier is nil, NoOpVerifier is used.
func (*SSEManager) Mode ¶ added in v0.2.0
func (m *SSEManager) Mode() DeliveryMode
Mode returns the delivery mode currently reported by the reconnect engine.
func (*SSEManager) RegisterHandler ¶
func (m *SSEManager) RegisterHandler(eventType string, handler EventHandler)
RegisterHandler adds a handler for the given event type. Must be called before Start.
func (*SSEManager) SetIdleTimeout ¶ added in v0.2.0
func (m *SSEManager) SetIdleTimeout(d time.Duration)
SetIdleTimeout sets the SSE idle timeout used for connections opened by Start. A zero duration falls back to DefaultSSEIdleTimeout.
func (*SSEManager) SetOnModeChange ¶ added in v0.2.0
func (m *SSEManager) SetOnModeChange(fn func(DeliveryMode))
SetOnModeChange registers a callback invoked on every delivery-mode transition. The callback may be nil.
func (*SSEManager) SetPollFunc ¶
func (m *SSEManager) SetPollFunc(fn PollFunc)
SetPollFunc sets the function called during polling fallback to fetch full state.
func (*SSEManager) SetPollingFallback ¶
func (m *SSEManager) SetPollingFallback(fallbackAfter, pollInterval time.Duration)
SetPollingFallback configures when to enter polling mode and how often to poll.
func (*SSEManager) SetReconcileTrigger ¶ added in v0.2.0
func (m *SSEManager) SetReconcileTrigger(t ReconcileTrigger)
SetReconcileTrigger sets the trigger fired once after every successful SSE connect so the client covers replay gaps with a full reconcile pull. A nil trigger disables the pull.
func (*SSEManager) SetReconnectIntervals ¶
func (m *SSEManager) SetReconnectIntervals(base, max time.Duration)
SetReconnectIntervals configures the base and max backoff intervals. Useful for testing with fast intervals.
func (*SSEManager) SetReprobeInterval ¶ added in v0.2.0
func (m *SSEManager) SetReprobeInterval(d time.Duration)
SetReprobeInterval sets how often pull-only mode re-probes the SSE endpoint. It delegates to the reconnect engine.
func (*SSEManager) Shutdown ¶
func (m *SSEManager) Shutdown()
Shutdown gracefully stops the manager by cancelling its context.
type SSEParser ¶
type SSEParser struct {
// contains filtered or unexported fields
}
SSEParser reads from an io.Reader and emits parsed SSE events.
func NewSSEParser ¶
NewSSEParser creates a parser reading from the given reader.
func (*SSEParser) LastEventID ¶
LastEventID returns the most recently received event ID.
func (*SSEParser) Next ¶
Next reads lines until a complete event is found. Returns the event and true, or a zero event and false when the reader is exhausted.
func (*SSEParser) SetRetryCallback ¶
func (p *SSEParser) SetRetryCallback(cb RetryCallback)
SetRetryCallback sets the function called when a retry: field is received.
type SSEStream ¶
type SSEStream struct {
// contains filtered or unexported fields
}
SSEStream connects to the SSE endpoint, parses events, verifies envelopes, and dispatches them to registered handlers.
func NewSSEStream ¶
func NewSSEStream(client *ControlPlane, verifier EventVerifier, dispatcher *EventDispatcher, idleTimeout time.Duration, logger *slog.Logger) *SSEStream
NewSSEStream creates a new SSEStream.
func (*SSEStream) Connect ¶
Connect establishes the SSE connection and processes events until the connection drops or context is cancelled. Returns nil when the connection closes cleanly, or an error.
func (*SSEStream) LastEventID ¶
LastEventID returns the last received event ID (for reconnection).
func (*SSEStream) SetOnConnected ¶ added in v0.2.0
func (s *SSEStream) SetOnConnected(fn func())
SetOnConnected registers a hook invoked once after every ConnectSSE that returns HTTP 200, before the parse loop begins.
type SSHActivity ¶ added in v0.2.0
type SSHActivity struct {
Command string `json:"command"`
ExitCode *int `json:"exit_code,omitempty"`
StartedAt *time.Time `json:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
}
SSHActivity records a completed SSH session command. Command is the executed command line and is capped at 1 KiB. StartedAt and CompletedAt are RFC 3339 timestamps.
type SecretEnvelope ¶ added in v0.2.0
SecretEnvelope is the raw AES-256-GCM envelope served by GET /v1/nodes/{id}/secrets/{name}: Data is <12-byte nonce> || <ciphertext + 16-byte GCM tag>, Version and KID come from the X-Plexsphere-Secret-Version and X-Plexsphere-Secret-KID headers. It is parsed from headers and body, never from JSON.
type SessionActivityRequest ¶ added in v0.2.0
type SessionActivityRequest struct {
SSH *SSHActivity `json:"ssh,omitempty"`
K8s *K8sActivity `json:"k8s,omitempty"`
TCP *TCPActivity `json:"tcp,omitempty"`
}
SessionActivityRequest is the one-of activity record a node posts to POST /v1/nodes/{node_id}/sessions/{session_id}. Exactly one member is set, selecting the session kind: SSH, K8s, or TCP.
type SessionTarget ¶ added in v0.3.0
type SessionTarget struct {
SSH *SessionTargetSSH `json:"ssh,omitempty"`
K8s *SessionTargetK8s `json:"k8s,omitempty"`
TCP *SessionTargetTCP `json:"tcp,omitempty"`
}
SessionTarget is the target of a NodeStateSession. Exactly one member is set, selecting the session kind: SSH, K8s, or TCP.
type SessionTargetK8s ¶ added in v0.3.0
type SessionTargetK8s struct {
User string `json:"user"`
ImpersonateGroups []string `json:"impersonate_groups,omitempty"`
}
SessionTargetK8s is the target of a k8s session. User is the impersonated Kubernetes user; ImpersonateGroups are the groups impersonated alongside it.
type SessionTargetSSH ¶ added in v0.3.0
type SessionTargetSSH struct {
User string `json:"user"`
AllowedCommands []string `json:"allowed_commands,omitempty"`
}
SessionTargetSSH is the target of an ssh session. User is the local account the session logs in as; AllowedCommands, when set, is the closed set of command lines the session may run.
type SessionTargetTCP ¶ added in v0.3.0
SessionTargetTCP is the target of a tcp session: the host and port the node forwards the session's connections to.
type SigningKeyRotation ¶ added in v0.2.0
type SigningKeyRotation struct {
KeyID string `json:"key_id"`
PublicKey string `json:"public_key"`
PreviousKeyID string `json:"previous_key_id"`
TransitionExpires time.Time `json:"transition_expires"`
}
SigningKeyRotation is the payload of the signing_key_rotated event: the new current signing key and, optionally, the previous key id kept valid until a transition deadline. Its shape is an author-approved assumption until the platform taxonomy documents it.
type SiteToSiteConfig ¶
type SiteToSiteConfig struct {
Enabled bool `json:"enabled"`
Tunnels []SiteToSiteTunnel `json:"tunnels"`
}
SiteToSiteConfig is the site-to-site VPN configuration pushed from the control plane.
type SiteToSiteInfo ¶
type SiteToSiteInfo struct {
Enabled bool `json:"enabled"`
TunnelCount int `json:"tunnel_count"`
TunnelProviderNames []string `json:"tunnel_provider_names,omitempty"`
}
SiteToSiteInfo is the site-to-site VPN status reported by the node in heartbeats.
type SiteToSiteTunnel ¶
type SiteToSiteTunnel struct {
TunnelID string `json:"tunnel_id"`
RemoteEndpoint string `json:"remote_endpoint"`
RemotePublicKey string `json:"remote_public_key"`
LocalSubnets []string `json:"local_subnets"`
RemoteSubnets []string `json:"remote_subnets"`
PSK string `json:"psk,omitempty"`
InterfaceName string `json:"interface_name"`
ListenPort int `json:"listen_port"`
// ProviderType specifies which tunnel provider to use for this tunnel.
// Empty or "wireguard" means the default WireGuard-based approach.
// Other values (e.g. "ipsec", "openvpn") delegate to the corresponding TunnelProvider.
ProviderType string `json:"provider_type,omitempty"`
}
SiteToSiteTunnel represents a single site-to-site VPN tunnel definition.
type SnapshotPeer ¶ added in v0.2.0
type SnapshotPeer struct {
NodeID string `json:"node_id"`
MeshIP string `json:"mesh_ip"`
PublicKey string `json:"public_key"`
FallbackEndpoint string `json:"fallback_endpoint,omitempty"`
}
SnapshotPeer is a reconciliation peer entry in NodeStateSnapshot. It is a separate type from RegisterPeer (one Go type per contract schema) and carries NO psk, allowed_ips, or endpoint: AllowedIPs are derived locally as mesh_ip/32, fallback_endpoint is the relay target programmed as the WireGuard endpoint, and no preshared key is fabricated.
type StateEntry ¶ added in v0.2.0
type StateEntry struct {
Key string `json:"key"`
Value string `json:"value"`
WorkloadTag string `json:"workload_tag,omitempty"`
}
StateEntry is a single state entry. Value is an opaque string; WorkloadTag is absent/empty when the entry is unattributed.
type TCPActivity ¶ added in v0.2.0
type TCPActivity struct {
Phase string `json:"phase"`
TargetHost string `json:"target_host,omitempty"`
TargetPort int `json:"target_port,omitempty"`
ListenerEndpoint string `json:"listener_endpoint,omitempty"`
BytesIn *int64 `json:"bytes_in,omitempty"`
BytesOut *int64 `json:"bytes_out,omitempty"`
TerminatedBy string `json:"terminated_by,omitempty"`
}
TCPActivity records a TCP session lifecycle event. Phase is one of the TCPPhase* values. ListenerEndpoint is the node's bound listener address and is set only on session_started rows. BytesIn (operator to target) and BytesOut (target to operator) are pointers so a session_ended row carries explicit zeros while a session_started row omits the byte counters. TerminatedBy, when set, is one of the TerminatedBy* values.
type UserAccessConfig ¶
type UserAccessConfig struct {
Enabled bool `json:"enabled"`
InterfaceName string `json:"interface_name"`
ListenPort int `json:"listen_port"`
Peers []UserAccessPeer `json:"peers"`
}
UserAccessConfig is the user access configuration pushed from the control plane.
type UserAccessInfo ¶
type UserAccessInfo struct {
Enabled bool `json:"enabled"`
InterfaceName string `json:"interface_name"`
PeerCount int `json:"peer_count"`
ListenPort int `json:"listen_port"`
ProviderName string `json:"provider_name,omitempty"`
ProviderStatus string `json:"provider_status,omitempty"`
}
UserAccessInfo is the user access status reported by the node in heartbeats.