store

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LineChainOperationSet    = "set"
	LineChainOperationRemove = "remove"

	LineChainStatusPlanned           = "planned"
	LineChainStatusApplying          = "applying"
	LineChainStatusAppliedUnobserved = "applied_unobserved"
	LineChainStatusConverged         = "converged"
	LineChainStatusDrifted           = "drifted"
	LineChainStatusFailed            = "failed"
)
View Source
const (
	MaxVpnUserRecords            = 100_000
	MaxVpnUserCredentials        = 16
	MaxLineSecretRecordBytes     = 16 << 10
	MaxLineSecretCollectionBytes = 256 << 20
)
View Source
const (
	NodeStatusEventRetention = 30 * 24 * time.Hour

	// NodeStatusServerID keys the control plane's own rows. Node ids come from
	// id.New and never start with an underscore; ReservedNodeID holds the line
	// for ids a caller supplies.
	NodeStatusServerID = "_server"

	NodeStatusOnline  = "online"
	NodeStatusOffline = "offline"

	NodeStatusCauseBeat          = "beat"
	NodeStatusCauseLivenessSweep = "liveness_sweep"
	NodeStatusCauseServerStart   = "server_start"
	NodeStatusCauseServerStop    = "server_stop"
)

Node status history: one record per transition of a node's Online flag, keyed "<id>/<instant>" in node_status_events. The bucket takes the usage-day placement: with the hot store on it lives only in bolt and is read with a prefix seek, so the JSON state is never rewritten with it.

Exactly two hooks write. The beat that turns Online false to true (UpdateMetrics, and the hello path through AppendNodeStatusEvent) and the liveness sweep that turns it true to false (MarkStaleNodesOffline). Nothing samples: a node that never flaps has no rows, and its state over a window is read off the node record by the history endpoint.

The control plane records its own runs under NodeStatusServerID: an offline row at the last instant the previous process is known to have been alive and an online row at start. A stopped control plane observes nothing, so the endpoint renders that gap as unknown rather than as every node going down; the sweep at start then flips whatever really died.

Bounds: rows older than NodeStatusEventRetention go on the sweep tick, and each id keeps at most maxNodeStatusEvents rows (the maxMonitorResults pattern). Thirty-three nodes at the observed flap rate write about thirty rows a day, well under both.

View Source
const (
	// NotifyWebhookAccepted means the request authenticated and produced at least
	// one planned delivery.
	NotifyWebhookAccepted = "accepted"
	// NotifyWebhookNoRoute means the request authenticated but no enabled rule and
	// channel pair matched the event. This is the state production is in today
	// (zero channels, zero rules) and it is worth showing plainly rather than
	// reporting a success that reached nobody.
	NotifyWebhookNoRoute = "no_route"
	// NotifyWebhookRejected means the request never became an event: bad secret,
	// disabled webhook, oversized or malformed payload, or rate limited.
	NotifyWebhookRejected = "rejected"
	// NotifyWebhookFailed means deliveries were planned but every channel send
	// returned an error.
	NotifyWebhookFailed = "failed"
	// NotifyWebhookPartial means some channel sends succeeded and some failed.
	NotifyWebhookPartial = "partial"
)

Delivery outcomes. A webhook fire is accepted or rejected synchronously, then fanned out asynchronously, so the record carries both halves.

View Source
const (
	// CapabilityEnrolled allows the capability to act on this node.
	CapabilityEnrolled = "enrolled"
	// CapabilityExcluded refuses it, and records why. Distinct from having no
	// record at all: a NAT box with no exposed port is not "not got to yet",
	// it is "deliberately out, until port forwarding exists". Losing that
	// distinction is what makes an exclusion list decay into a backlog.
	CapabilityExcluded = "excluded"
)

Node capability enrolment: which capabilities an operator has allowed to act on one node. Deliberately separate from role/tags/groups, which say what a node IS, and from agent_runtime, which says what the agent can do right now. Those are three different questions, and dispatch needs all three: a node can be perfectly capable and still be one an operator has decided to leave alone.

View Source
const (
	DurableProtocolNetGuardV1  = "netguard-v1"
	DurableProtocolLineChainV2 = "linechain-e3-v2"
)
View Source
const (
	AgentUpdatePlugin       = "agentupdate"
	AgentUpdateActionPrefix = "update-agent:"
)

Agent update approvals are recognised here because the re-lease decision is taken under the store lock, and a downgrade must be stopped at that moment rather than reported afterwards. The constants mirror the server's agentupdate plugin; the server references these so the two cannot drift.

View Source
const MaxPluginSecretsPerBucket = 256

MaxPluginSecretsPerBucket bounds one plugin's vault. KV is unbounded, which is tolerable for plaintext scratch data; it is not tolerable here, because every write re-encrypts and rewrites the entire state file, so an unbounded vault is both a disk amplifier and a way for one plugin to bloat every other plugin's persistence path.

View Source
const MaxTaskLeaseAttempts = 3

MaxTaskLeaseAttempts is how many times a target may be handed a task whose lease keeps dying without a result. Three is enough to absorb one restart and one lost poll; past that the loop is the script, not the network.

View Source
const MaxVpnCredentialPasswordBytes = 256
View Source
const (
	// MaxWebAuthnCredentialsPerUser caps how many passkeys one operator may
	// register. It bounds both storage and the allow/exclude lists sent to the
	// browser; a generous ceiling that still refuses runaway growth. Exported so
	// the server can fail a registration fast (before starting a ceremony) with
	// the same limit the store enforces on write.
	MaxWebAuthnCredentialsPerUser = 10
)
View Source
const TaskExpired = "expired"

TaskExpired marks a task the control plane has stopped waiting for: a target never leased it before the queue deadline, so delivery was withdrawn.

Deliberately NOT model.TaskFailed. A node that was switched off is not a script that went wrong, and folding the two together would put offline machines in the failed count - the same conflation the task copy already avoids by leaving an unanswered target unreported rather than done.

It lives here rather than beside model.TaskQueued because model is the SDK, pinned by sdk.ref and consumed by plugins; adding a status value there is a coordinated two-repo release and a change to a contract others read. The string is what goes over the wire either way. Move it when the SDK next ships.

View Source
const TaskStalled = "stalled"

TaskStalled marks a leased task that has stopped making progress: at least one target still owes a result and no resultless target holds a live lease, so nothing is running and nothing has answered. Saying "leased" there reads as "Running" on the console, which is a lie the operator waits on for days. Like TaskExpired it is a view-level status (derived, never persisted) and lives here rather than in the SDK for the same release-coordination reason.

View Source
const TaskStalledAgentLostReason = "agent lost during run three times"

TaskStalledAgentLostReason is the stall reason for a target whose agent disappeared mid-run MaxTaskLeaseAttempts times. The wording names the mechanism the operator has to look for: the script kills the agent.

View Source
const (
	UsageDayRetentionDays = 400
)

Daily usage rollups. Two record families, each keyed "<id>/<yyyymmdd>":

usage_day_node  <node_id>/<day>  that node's lines for the day
usage_day_user  <user_id>/<day>  one identity's counted traffic for the day

Monotonic deltas are added into the current UTC day at ingestion, so a row is a sum of deltas rather than a copy of a counter, and a core restart on the node cannot zero a day. Retention is UsageDayRetentionDays, pruned when the ingestion path sees the UTC day roll over.

JSON keys are deliberately short. The documented bound is 33 nodes with 200 lines each kept for 400 days; a node-day record in which every line was active and carried one named user marshals to roughly 30 KiB with these keys, which keeps the whole fleet under 512 MiB on disk in that worst case. TestUsageDaySizeBound holds the number.

Variables

View Source
var (
	ErrLineChainRevisionConflict = errors.New("line chain graph revision conflict")
	ErrLineChainCycle            = errors.New("line chain cycle")
	ErrLineChainSourceBusy       = errors.New("line chain source already has an active attempt")
	ErrLineChainAttemptNotFound  = errors.New("line chain attempt not found")
)
View Source
var (
	ErrTaskNotFound           = errors.New("task not found")
	ErrTaskNotCancelable      = errors.New("only queued tasks can be cancelled")
	ErrTaskDurableProtected   = errors.New("durable protocol task cannot be mutated through generic task management")
	ErrTaskLeaseMismatch      = errors.New("task lease mismatch")
	ErrTaskTransitionConflict = errors.New("task approval transition conflict")
)

Task management sentinel errors so handlers can map store outcomes to HTTP status codes without string matching.

View Source
var ErrGuardBindingManaged = errors.New("guard binding is managed")

ErrGuardBindingManaged is returned when a delete would remove a binding that is still managed. The table that binding applied stays on the node, so dropping the record would leave a live guard table with no owner.

View Source
var ErrGuardRealityDurabilityDegraded = errors.New("guard reality committed with degraded durability")

ErrGuardRealityDurabilityDegraded means the atomic rename committed the snapshot, but syncing the parent directory failed. Callers must treat the snapshot as accepted while surfacing the durability warning operationally.

View Source
var ErrGuardRealityNodeChanged = errors.New("guard reality node identity changed")

ErrGuardRealityNodeChanged is returned when the node authenticated by the handler no longer exists as the same immutable identity generation.

View Source
var ErrGuardRealityStale = errors.New("guard reality snapshot is stale")

ErrGuardRealityStale is returned when a write would replace a newer snapshot or conflict with a different snapshot collected at the same instant.

View Source
var ErrGuardVersionConflict = errors.New("guard record version conflict")

ErrGuardVersionConflict is returned when an optimistic-concurrency upsert carries a stale Version. Security groups and node guard bindings require the caller to echo the current version so two operators cannot silently clobber each other's firewall edits (design-13, closing the NFTInputs upsert gap).

View Source
var ErrLineChainDeleteConflict = errors.New("node deletion conflicts with an issued line chain lease")
View Source
var ErrNetGuardCompileNodeNotFound = errors.New("netguard compile node not found")
View Source
var ErrWebAuthnCredentialLimit = errors.New("passkey limit reached for this account")

ErrWebAuthnCredentialLimit is returned when a user is already at the passkey cap. The server surfaces it as a clear client error.

Functions

func CompareAgentVersions added in v0.2.3

func CompareAgentVersions(a, b string) (int, bool)

CompareAgentVersions orders two agent release strings: negative when a is older than b, positive when newer, zero when equal. The second return is false when either side is not a release the fleet can be compared on (a custom build tag, say), in which case no ordering claim is made.

func ExportBoltToJSON added in v0.2.0

func ExportBoltToJSON(boltPath, jsonPath string, cph secret.Cipher, opts MigrationOptions) error

func MigrateJSONToBolt added in v0.2.0

func MigrateJSONToBolt(jsonPath, boltPath string, cph secret.Cipher, opts MigrationOptions) error

func ParseUsageDay added in v0.2.3

func ParseUsageDay(day string) (time.Time, error)

ParseUsageDay is the inverse of UsageDay.

func ReservedNodeID added in v0.2.3

func ReservedNodeID(id string) bool

ReservedNodeID reports whether an id is in the underscore namespace the control plane keeps for its own rows. Enrolment refuses such an id so a node can never write over the server's start and stop marks.

func SortedVpnUserRecordIDs added in v0.2.3

func SortedVpnUserRecordIDs(records map[string]VpnUserPublicRecord) []string

func TaskPastQueueDeadline added in v0.2.3

func TaskPastQueueDeadline(t model.Task, now time.Time, deadline time.Duration) bool

TaskPastQueueDeadline exposes the same judgement to the API layer, so the status the console shows and the delivery the agent gets are decided by one rule rather than two that can drift.

func UsageDay added in v0.2.3

func UsageDay(t time.Time) string

UsageDay formats a time as the UTC day key.

func UsageDayKey added in v0.2.3

func UsageDayKey(id, day string) string

UsageDayKey is the record key for one id on one day.

func ValidateVpnUserCredentialSecret added in v0.2.3

func ValidateVpnUserCredentialSecret(credential VpnUserCredentialSecret) error

ValidateVpnUserCredentialSecret is the shared canonical protocol-shape contract used by typed persistence and server request normalization.

func WouldCreateLineChainCycle added in v0.2.3

func WouldCreateLineChainCycle(snapshot LineChainSnapshot, sourceLineUUID, targetLineUUID string) bool

WouldCreateLineChainCycle evaluates a candidate against every committed edge and every already-reserved applying candidate from the supplied immutable snapshot. Planned attempts do not reserve graph membership.

func WriteJSONState added in v0.2.0

func WriteJSONState(path string, st State, cph secret.Cipher, opts MigrationOptions) error

Types

type ApprovalRejection added in v0.2.3

type ApprovalRejection struct {
	ApprovalID string    `json:"approval_id"`
	ActorID    string    `json:"actor_id"`
	At         time.Time `json:"at"`
}

ApprovalRejection records who rejected an approval and when.

The approval row itself cannot say: its model lives in the SDK and carries ApprovedBy but no rejecting actor, and an approval that a node's task failed is also marked rejected, with the approver still in ApprovedBy. So a console reading "rejected" had to guess whether a person said no or a task died, and it guessed from ApprovedBy being empty. This is the signal that guess stood in for: written only when a principal rejects, absent when the node did.

type AuditWALVerification added in v0.2.3

type AuditWALVerification struct {
	// Enabled is false for a store with no WAL at all, which is not a failure:
	// an in-memory store has nothing to verify.
	Enabled  bool
	At       time.Time
	OK       bool
	Err      string
	Count    int
	Head     string
	Anchored bool
}

AuditWALVerification is the outcome of one chain walk, kept so a readiness probe can report what the last walk found instead of paying for its own.

type BoltStateStore added in v0.2.0

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

BoltStateStore is the first bbolt-backed persistence boundary. It stores each State collection in its own bucket so the future Store migration can move from whole-state rewrites to record-level writes without changing handlers.

This type is intentionally not wired into server startup yet: it is the tested import/export foundation for the Phase C migration.

EXPERIMENTAL — do not enable as the runtime backend until the Phase C entry gates are met (security-audit iter-016 D12/D3): (1) a backup/restore command + drill exist; (2) record-level pruning no longer decrypts every record to read non-secret timestamps (D3); (3) this store is round-trip/fuzz-validated for semantic parity against the JSON store. Until then its method set can silently drift from the JSON store, so changes here MUST be mirrored and tested against internal/store/store.go.

func OpenBoltState added in v0.2.0

func OpenBoltState(path string, cph secret.Cipher) (*BoltStateStore, error)

func (*BoltStateStore) AddMonitorResult added in v0.2.0

func (bs *BoltStateStore) AddMonitorResult(r model.MonitorResult) error

func (*BoltStateStore) AddTaskResult added in v0.2.0

func (bs *BoltStateStore) AddTaskResult(r model.TaskResult) error

func (*BoltStateStore) AllNFTInputs added in v0.2.0

func (bs *BoltStateStore) AllNFTInputs() ([]model.NFTInputs, error)

func (*BoltStateStore) AppendAudit added in v0.2.0

func (bs *BoltStateStore) AppendAudit(ev model.AuditEvent) error

func (*BoltStateStore) AppendNodeStatusEvents added in v0.2.3

func (bs *BoltStateStore) AppendNodeStatusEvents(events []nodeStatusAppend) error

AppendNodeStatusEvents is the bolt half of appendNodeStatusEventsLocked: the rows and the per-id trim land in one transaction.

func (*BoltStateStore) ApplyProxyUsage added in v0.2.3

func (bs *BoltStateStore) ApplyProxyUsage(update ProxyUsageUpdate) error

ApplyProxyUsage is the bolt half of Store.ApplyProxyUsage: the snapshot, the user projections, the profile and the day rows land in one transaction.

func (*BoltStateStore) ApplyProxyUsageUpdate added in v0.2.0

func (bs *BoltStateStore) ApplyProxyUsageUpdate(users []model.ProxyUser, profile *model.ProxyNodeProfile, snapshot *model.ProxyUsageSnapshot) error

ApplyProxyUsageUpdate keeps the pre-rollup signature for callers that carry no day rows.

func (*BoltStateStore) Approval added in v0.2.0

func (bs *BoltStateStore) Approval(id string) (model.Approval, bool, error)

func (*BoltStateStore) Approvals added in v0.2.0

func (bs *BoltStateStore) Approvals() ([]model.Approval, error)

func (*BoltStateStore) AuditEventByID added in v0.2.3

func (bs *BoltStateStore) AuditEventByID(id string) (model.AuditEvent, bool, error)

AuditEventByID returns the newest durable event with this id. The audit bucket is keyed by append sequence, not by id, so this is a scan; it exits at the first hit and holds one event at a time. Its callers are the line-chain evidence paths, which run on operator transitions, not on every request.

func (*BoltStateStore) AuditEventKeysPresent added in v0.2.3

func (bs *BoltStateStore) AuditEventKeysPresent(want map[string]struct{}) (map[string]struct{}, error)

AuditEventKeysPresent reports which of want already exist in the audit log. It reads the log once and holds only the answer, so the caller can dedupe a handful of candidates against a million durable events without materialising them. It stops early once every wanted key has been found.

func (*BoltStateStore) AuditEvents added in v0.2.0

func (bs *BoltStateStore) AuditEvents() ([]model.AuditEvent, error)

AuditEvents materialises the entire audit log, sorted newest-first.

This is the expensive read and it has no bound: it is kept for the JSON-only store and for tests, whose logs are small. Production read paths use ScanAuditEventsDesc.

func (*BoltStateStore) Close added in v0.2.0

func (bs *BoltStateStore) Close() error

func (*BoltStateStore) ConsumeOIDCAuthState added in v0.2.0

func (bs *BoltStateStore) ConsumeOIDCAuthState(state string) (auth.OIDCAuthState, bool, error)

func (*BoltStateStore) ConsumeRecoveryCode added in v0.2.0

func (bs *BoltStateStore) ConsumeRecoveryCode(userID, code string) (bool, error)

func (*BoltStateStore) ConsumeTOTPChallenge added in v0.2.0

func (bs *BoltStateStore) ConsumeTOTPChallenge(id string) error

func (*BoltStateStore) CreateTask added in v0.2.0

func (bs *BoltStateStore) CreateTask(t model.Task) error

func (*BoltStateStore) DDNSProfile added in v0.2.0

func (bs *BoltStateStore) DDNSProfile(id string) (model.DDNSProfile, bool, error)

func (*BoltStateStore) DDNSProfiles added in v0.2.0

func (bs *BoltStateStore) DDNSProfiles() ([]model.DDNSProfile, error)

func (*BoltStateStore) DDNSProfilesForNode added in v0.2.0

func (bs *BoltStateStore) DDNSProfilesForNode(nodeID string) ([]model.DDNSProfile, error)

func (*BoltStateStore) DNSDeployment added in v0.2.0

func (bs *BoltStateStore) DNSDeployment(id string) (model.DNSDeployment, bool, error)

func (*BoltStateStore) DNSDeployments added in v0.2.0

func (bs *BoltStateStore) DNSDeployments() ([]model.DNSDeployment, error)

func (*BoltStateStore) DNSDeploymentsForNode added in v0.2.0

func (bs *BoltStateStore) DNSDeploymentsForNode(nodeID string) ([]model.DNSDeployment, error)

func (*BoltStateStore) DeleteDDNSProfile added in v0.2.0

func (bs *BoltStateStore) DeleteDDNSProfile(id string) error

func (*BoltStateStore) DeleteDNSDeployment added in v0.2.0

func (bs *BoltStateStore) DeleteDNSDeployment(id string) error

func (*BoltStateStore) DeleteGroup added in v0.2.0

func (bs *BoltStateStore) DeleteGroup(id string) error

func (*BoltStateStore) DeleteGroupPolicy added in v0.2.0

func (bs *BoltStateStore) DeleteGroupPolicy(id string) error

func (*BoltStateStore) DeleteKV added in v0.2.3

func (bs *BoltStateStore) DeleteKV(bucket, key string) error

func (*BoltStateStore) DeleteMachineProfile added in v0.2.0

func (bs *BoltStateStore) DeleteMachineProfile(id string) error

func (*BoltStateStore) DeleteMonitor added in v0.2.0

func (bs *BoltStateStore) DeleteMonitor(id string) error

func (*BoltStateStore) DeleteNFTInputs added in v0.2.0

func (bs *BoltStateStore) DeleteNFTInputs(nodeID string) error

func (*BoltStateStore) DeleteNetPolicy added in v0.2.0

func (bs *BoltStateStore) DeleteNetPolicy(nodeID string) error

func (*BoltStateStore) DeleteNotifyChannel added in v0.2.0

func (bs *BoltStateStore) DeleteNotifyChannel(id string) error

func (*BoltStateStore) DeleteNotifyRule added in v0.2.0

func (bs *BoltStateStore) DeleteNotifyRule(id string) error

func (*BoltStateStore) DeleteOIDCProvider added in v0.2.0

func (bs *BoltStateStore) DeleteOIDCProvider(id string) error

func (*BoltStateStore) DeleteProxyInbound added in v0.2.0

func (bs *BoltStateStore) DeleteProxyInbound(id string) error

func (*BoltStateStore) DeleteProxyNodeProfile added in v0.2.0

func (bs *BoltStateStore) DeleteProxyNodeProfile(nodeID string) error

func (*BoltStateStore) DeleteProxyUsageSnapshot added in v0.2.0

func (bs *BoltStateStore) DeleteProxyUsageSnapshot(nodeID string) error

func (*BoltStateStore) DeleteProxyUser added in v0.2.0

func (bs *BoltStateStore) DeleteProxyUser(id string) error

func (*BoltStateStore) DeleteSession added in v0.2.0

func (bs *BoltStateStore) DeleteSession(id string) error

func (*BoltStateStore) DeleteStatic added in v0.2.3

func (bs *BoltStateStore) DeleteStatic(bucket, objectPath string) error

DeleteStatic removes one object. Static had no delete at all until files began storing generator scripts here: without one, replacing a file's script would leave the old bytes behind forever.

func (*BoltStateStore) DeleteSubscriptionShare added in v0.2.3

func (bs *BoltStateStore) DeleteSubscriptionShare(id string) error

func (*BoltStateStore) DeleteSubscriptionSnapshot added in v0.2.3

func (bs *BoltStateStore) DeleteSubscriptionSnapshot(key string) error

func (*BoltStateStore) DeleteToken added in v0.2.0

func (bs *BoltStateStore) DeleteToken(id string) (model.Token, bool, error)

func (*BoltStateStore) DeleteTunnel added in v0.2.0

func (bs *BoltStateStore) DeleteTunnel(id string) error

func (*BoltStateStore) EnabledNotifyChannels added in v0.2.0

func (bs *BoltStateStore) EnabledNotifyChannels() ([]model.NotifyChannel, error)

func (*BoltStateStore) EnabledNotifyRules added in v0.2.0

func (bs *BoltStateStore) EnabledNotifyRules() ([]model.NotifyRule, error)

func (*BoltStateStore) EnabledOIDCProviders added in v0.2.0

func (bs *BoltStateStore) EnabledOIDCProviders() ([]model.OIDCProvider, error)

func (*BoltStateStore) ExportState added in v0.2.0

func (bs *BoltStateStore) ExportState() (State, error)

ExportState reads every bbolt bucket and returns a decrypted, initialized State. Values returned by bbolt are decoded inside the transaction.

func (*BoltStateStore) ExportStateWithoutAudit added in v0.2.3

func (bs *BoltStateStore) ExportStateWithoutAudit() (State, error)

ExportStateWithoutAudit is ExportState with the audit log left alone entirely: not held, not even decoded.

The audit log is append-only and unbounded. On this control plane it reached a million events, and every caller that only needed "the rest of the state" was paying gigabytes to hold a log it then threw away. Worse, the readiness probe asked the same question on every call, so a bounded health check was doing an unbounded read while holding the store lock.

Callers that need the events walk them with ScanAuditEventsDesc. The one caller that needs the records checked, the open-time integrity probe, calls ValidateAuditLog explicitly, once.

func (*BoltStateStore) FailTOTPChallenge added in v0.2.0

func (bs *BoltStateStore) FailTOTPChallenge(id string, maxAttempts int) error

func (*BoltStateStore) Group added in v0.2.0

func (bs *BoltStateStore) Group(id string) (model.Group, bool, error)

func (*BoltStateStore) GroupPolicies added in v0.2.0

func (bs *BoltStateStore) GroupPolicies() ([]model.GroupNetPolicy, error)

func (*BoltStateStore) GroupPolicy added in v0.2.0

func (bs *BoltStateStore) GroupPolicy(id string) (model.GroupNetPolicy, bool, error)

func (*BoltStateStore) Groups added in v0.2.0

func (bs *BoltStateStore) Groups() ([]model.Group, error)

func (*BoltStateStore) ImportState added in v0.2.0

func (bs *BoltStateStore) ImportState(st State) error

ImportState replaces the entire bbolt state atomically. Secret-bearing fields are encrypted before they are written; the input State is not mutated.

func (*BoltStateStore) KV added in v0.2.0

func (bs *BoltStateStore) KV(bucket string) ([]model.KVEntry, error)

func (*BoltStateStore) KVStaticMigrated added in v0.2.3

func (bs *BoltStateStore) KVStaticMigrated() (bool, error)

func (*BoltStateStore) LastMonitorResultForNode added in v0.2.0

func (bs *BoltStateStore) LastMonitorResultForNode(monitorID, nodeID string) (model.MonitorResult, bool, error)

func (*BoltStateStore) LeaseTasks added in v0.2.0

func (bs *BoltStateStore) LeaseTasks(nodeID string, limit int) ([]model.Task, error)

func (*BoltStateStore) MachineProfile added in v0.2.0

func (bs *BoltStateStore) MachineProfile(id string) (model.MachineProfile, bool, error)

func (*BoltStateStore) MachineProfileForNode added in v0.2.0

func (bs *BoltStateStore) MachineProfileForNode(nodeID string) (model.MachineProfile, bool, error)

func (*BoltStateStore) MachineProfiles added in v0.2.0

func (bs *BoltStateStore) MachineProfiles() ([]model.MachineProfile, error)

func (*BoltStateStore) MarkKVStaticMigrated added in v0.2.3

func (bs *BoltStateStore) MarkKVStaticMigrated() error

MarkKVStaticMigrated is written once the JSON entries have been copied across.

func (*BoltStateStore) Monitor added in v0.2.0

func (bs *BoltStateStore) Monitor(id string) (model.Monitor, bool, error)

func (*BoltStateStore) MonitorResults added in v0.2.0

func (bs *BoltStateStore) MonitorResults(monitorID string) ([]model.MonitorResult, error)

func (*BoltStateStore) Monitors added in v0.2.0

func (bs *BoltStateStore) Monitors() ([]model.Monitor, error)

func (*BoltStateStore) MonitorsForNode added in v0.2.0

func (bs *BoltStateStore) MonitorsForNode(nodeID string) ([]model.Monitor, error)

func (*BoltStateStore) NFTInputs added in v0.2.0

func (bs *BoltStateStore) NFTInputs(nodeID string) (model.NFTInputs, bool, error)

func (*BoltStateStore) NetPolicies added in v0.2.0

func (bs *BoltStateStore) NetPolicies() ([]model.NetPolicy, error)

func (*BoltStateStore) NetPolicy added in v0.2.0

func (bs *BoltStateStore) NetPolicy(nodeID string) (model.NetPolicy, bool, error)

func (*BoltStateStore) NewestNodeStatusEvent added in v0.2.3

func (bs *BoltStateStore) NewestNodeStatusEvent() (time.Time, error)

NewestNodeStatusEvent is the instant of the newest row across every id. Keys sort by id first, so this is one walk of the bucket, once per start.

func (*BoltStateStore) Node added in v0.2.0

func (bs *BoltStateStore) Node(id string) (model.Node, bool, error)

func (*BoltStateStore) NodeStatusEvents added in v0.2.3

func (bs *BoltStateStore) NodeStatusEvents(id string) ([]NodeStatusEvent, error)

func (*BoltStateStore) Nodes added in v0.2.0

func (bs *BoltStateStore) Nodes() ([]model.Node, error)

func (*BoltStateStore) NotifyChannels added in v0.2.0

func (bs *BoltStateStore) NotifyChannels() ([]model.NotifyChannel, error)

func (*BoltStateStore) NotifyRules added in v0.2.0

func (bs *BoltStateStore) NotifyRules() ([]model.NotifyRule, error)

func (*BoltStateStore) OIDCIdentity added in v0.2.0

func (bs *BoltStateStore) OIDCIdentity(providerID, subject string) (model.OIDCIdentity, bool, error)

func (*BoltStateStore) OIDCProvider added in v0.2.0

func (bs *BoltStateStore) OIDCProvider(id string) (model.OIDCProvider, bool, error)

func (*BoltStateStore) OIDCProviders added in v0.2.0

func (bs *BoltStateStore) OIDCProviders() ([]model.OIDCProvider, error)

func (*BoltStateStore) PluginInstallation added in v0.2.0

func (bs *BoltStateStore) PluginInstallation(id string) (model.PluginInstallation, bool, error)

func (*BoltStateStore) PluginInstallations added in v0.2.0

func (bs *BoltStateStore) PluginInstallations() ([]model.PluginInstallation, error)

func (*BoltStateStore) ProxyInbound added in v0.2.0

func (bs *BoltStateStore) ProxyInbound(id string) (model.ProxyInbound, bool, error)

func (*BoltStateStore) ProxyInbounds added in v0.2.0

func (bs *BoltStateStore) ProxyInbounds() ([]model.ProxyInbound, error)

func (*BoltStateStore) ProxyNodeProfile added in v0.2.0

func (bs *BoltStateStore) ProxyNodeProfile(nodeID string) (model.ProxyNodeProfile, bool, error)

func (*BoltStateStore) ProxyNodeProfiles added in v0.2.0

func (bs *BoltStateStore) ProxyNodeProfiles() ([]model.ProxyNodeProfile, error)

func (*BoltStateStore) ProxyUsageSnapshot added in v0.2.0

func (bs *BoltStateStore) ProxyUsageSnapshot(nodeID string) (model.ProxyUsageSnapshot, bool, error)

func (*BoltStateStore) ProxyUsageSnapshots added in v0.2.0

func (bs *BoltStateStore) ProxyUsageSnapshots() ([]model.ProxyUsageSnapshot, error)

func (*BoltStateStore) ProxyUser added in v0.2.0

func (bs *BoltStateStore) ProxyUser(id string) (model.ProxyUser, bool, error)

func (*BoltStateStore) ProxyUsers added in v0.2.0

func (bs *BoltStateStore) ProxyUsers() ([]model.ProxyUser, error)

func (*BoltStateStore) ProxyUsersForInbound added in v0.2.0

func (bs *BoltStateStore) ProxyUsersForInbound(inboundID string) ([]model.ProxyUser, error)

func (*BoltStateStore) PruneNodeStatusEvents added in v0.2.3

func (bs *BoltStateStore) PruneNodeStatusEvents(before time.Time) (int, error)

PruneNodeStatusEvents deletes every row older than the cutoff. The stale keys are found in a read transaction first: the sweep calls this every tick, and a write transaction with nothing to delete would still fsync.

func (*BoltStateStore) PruneUsageDays added in v0.2.3

func (bs *BoltStateStore) PruneUsageDays(before string) (int, error)

PruneUsageDays deletes every row in both buckets whose day is older than the cutoff. One full walk of two buckets, once per day roll.

func (*BoltStateStore) PutKV added in v0.2.0

func (bs *BoltStateStore) PutKV(entry model.KVEntry) error

func (*BoltStateStore) PutOIDCAuthState added in v0.2.0

func (bs *BoltStateStore) PutOIDCAuthState(st auth.OIDCAuthState) error

func (*BoltStateStore) PutOIDCIdentity added in v0.2.0

func (bs *BoltStateStore) PutOIDCIdentity(idn model.OIDCIdentity) error

func (*BoltStateStore) PutSession added in v0.2.0

func (bs *BoltStateStore) PutSession(sess auth.Session) error

func (*BoltStateStore) PutStatic added in v0.2.0

func (bs *BoltStateStore) PutStatic(obj model.StaticObject) error

func (*BoltStateStore) PutTOTPChallenge added in v0.2.0

func (bs *BoltStateStore) PutTOTPChallenge(c auth.TOTPChallenge) error

func (*BoltStateStore) Results added in v0.2.0

func (bs *BoltStateStore) Results() ([]model.TaskResult, error)

func (*BoltStateStore) ScanAuditEventsDesc added in v0.2.3

func (bs *BoltStateStore) ScanAuditEventsDesc(visit func(model.AuditEvent) bool) error

ScanAuditEventsDesc walks the audit bucket newest-first and hands each event to visit, stopping as soon as visit returns false. It holds one event at a time, so a page of at most a few hundred rows costs a page, not the log.

The order is the bucket's insertion order reversed. That is the order the events were appended and therefore the order the audit chain records them. It is deliberately not a re-sort by the event's own timestamp field: sorting requires holding every event at once, which is the cost this exists to remove. The two orders differ only for an event appended with a timestamp older than the one before it, which is repair, not ordinary recording.

func (*BoltStateStore) SeedNodeStatusEvents added in v0.2.3

func (bs *BoltStateStore) SeedNodeStatusEvents(rows map[string]NodeStatusEvent) error

SeedNodeStatusEvents copies JSON-side rows into bolt when the hot store is first enabled; a row bolt already holds wins, as with SeedUsageDays.

func (*BoltStateStore) SeedUsageDays added in v0.2.3

func (bs *BoltStateStore) SeedUsageDays(nodes map[string]UsageDayNode, users map[string]UsageDayUser) error

SeedUsageDays copies JSON-side rows into bolt when the hot store is first enabled. A row bolt already holds wins: bolt is authoritative from then on and the JSON copy is whatever was last flushed before the switch.

func (*BoltStateStore) Session added in v0.2.0

func (bs *BoltStateStore) Session(id string) (auth.Session, bool, error)

func (*BoltStateStore) SetPluginStatus added in v0.2.0

func (bs *BoltStateStore) SetPluginStatus(id, status string) error

func (*BoltStateStore) Static added in v0.2.0

func (bs *BoltStateStore) Static(bucket string) ([]model.StaticObject, error)

func (*BoltStateStore) TOTPChallenge added in v0.2.0

func (bs *BoltStateStore) TOTPChallenge(id string) (auth.TOTPChallenge, bool, error)

func (*BoltStateStore) Task added in v0.2.0

func (bs *BoltStateStore) Task(id string) (model.Task, bool, error)

func (*BoltStateStore) Tasks added in v0.2.0

func (bs *BoltStateStore) Tasks() ([]model.Task, error)

func (*BoltStateStore) Token added in v0.2.0

func (bs *BoltStateStore) Token(id string) (model.Token, bool, error)

func (*BoltStateStore) Tokens added in v0.2.0

func (bs *BoltStateStore) Tokens() ([]model.Token, error)

func (*BoltStateStore) TouchNodeToken added in v0.2.0

func (bs *BoltStateStore) TouchNodeToken(nodeID string, at time.Time, minInterval time.Duration) (bool, error)

func (*BoltStateStore) Tunnel added in v0.2.0

func (bs *BoltStateStore) Tunnel(id string) (model.TunnelProfile, bool, error)

func (*BoltStateStore) Tunnels added in v0.2.0

func (bs *BoltStateStore) Tunnels() ([]model.TunnelProfile, error)

func (*BoltStateStore) UpdateMetrics added in v0.2.0

func (bs *BoltStateStore) UpdateMetrics(nodeID string, metrics model.Metrics, version, publicIP, publicIPv6, internalIP, internalIPv6, wgIP string, hostFacts model.HostFacts) error

func (*BoltStateStore) UpdateNodeGeo added in v0.2.0

func (bs *BoltStateStore) UpdateNodeGeo(nodeID string, geo *model.NodeGeo) (model.Node, bool, error)

func (*BoltStateStore) UpsertApproval added in v0.2.0

func (bs *BoltStateStore) UpsertApproval(a model.Approval) error

func (*BoltStateStore) UpsertDDNSProfile added in v0.2.0

func (bs *BoltStateStore) UpsertDDNSProfile(p model.DDNSProfile) error

func (*BoltStateStore) UpsertDNSDeployment added in v0.2.0

func (bs *BoltStateStore) UpsertDNSDeployment(dep model.DNSDeployment) error

func (*BoltStateStore) UpsertGroup added in v0.2.0

func (bs *BoltStateStore) UpsertGroup(g model.Group) error

func (*BoltStateStore) UpsertGroupPolicy added in v0.2.0

func (bs *BoltStateStore) UpsertGroupPolicy(p model.GroupNetPolicy) error

func (*BoltStateStore) UpsertMachineProfile added in v0.2.0

func (bs *BoltStateStore) UpsertMachineProfile(p model.MachineProfile) error

func (*BoltStateStore) UpsertMonitor added in v0.2.0

func (bs *BoltStateStore) UpsertMonitor(m model.Monitor) error

func (*BoltStateStore) UpsertNFTInputs added in v0.2.0

func (bs *BoltStateStore) UpsertNFTInputs(inputs model.NFTInputs) error

func (*BoltStateStore) UpsertNetPolicy added in v0.2.0

func (bs *BoltStateStore) UpsertNetPolicy(policy model.NetPolicy) error

func (*BoltStateStore) UpsertNode added in v0.2.0

func (bs *BoltStateStore) UpsertNode(n model.Node) error

func (*BoltStateStore) UpsertNotifyChannel added in v0.2.0

func (bs *BoltStateStore) UpsertNotifyChannel(c model.NotifyChannel) error

func (*BoltStateStore) UpsertNotifyRule added in v0.2.0

func (bs *BoltStateStore) UpsertNotifyRule(rule model.NotifyRule) error

func (*BoltStateStore) UpsertOIDCProvider added in v0.2.0

func (bs *BoltStateStore) UpsertOIDCProvider(p model.OIDCProvider) error

func (*BoltStateStore) UpsertPluginInstallation added in v0.2.0

func (bs *BoltStateStore) UpsertPluginInstallation(p model.PluginInstallation) error

func (*BoltStateStore) UpsertProxyInbound added in v0.2.0

func (bs *BoltStateStore) UpsertProxyInbound(in model.ProxyInbound) error

func (*BoltStateStore) UpsertProxyNodeProfile added in v0.2.0

func (bs *BoltStateStore) UpsertProxyNodeProfile(profile model.ProxyNodeProfile) error

func (*BoltStateStore) UpsertProxyUsageSnapshot added in v0.2.0

func (bs *BoltStateStore) UpsertProxyUsageSnapshot(snapshot model.ProxyUsageSnapshot) error

func (*BoltStateStore) UpsertProxyUser added in v0.2.0

func (bs *BoltStateStore) UpsertProxyUser(u model.ProxyUser) error

func (*BoltStateStore) UpsertSubscriptionShare added in v0.2.3

func (bs *BoltStateStore) UpsertSubscriptionShare(share model.SubscriptionShare) error

UpsertSubscriptionShare writes one share record. The token is sealed by the same cipher pass the proxy-user record uses, so a bolt file lifted on its own carries no usable subscription URL.

func (*BoltStateStore) UpsertSubscriptionSnapshot added in v0.2.3

func (bs *BoltStateStore) UpsertSubscriptionSnapshot(key string, snap model.SubscriptionSnapshot) error

UpsertSubscriptionSnapshot writes one provider payload with Raw sealed. Raw may contain bearer credentials and complete proxy URIs even though the public subscription endpoint later serves it to an authorized client.

func (*BoltStateStore) UpsertSubscriptionSnapshots added in v0.2.3

func (bs *BoltStateStore) UpsertSubscriptionSnapshots(snapshots map[string]model.SubscriptionSnapshot) error

func (*BoltStateStore) UpsertToken added in v0.2.0

func (bs *BoltStateStore) UpsertToken(t model.Token) error

func (*BoltStateStore) UpsertTunnel added in v0.2.0

func (bs *BoltStateStore) UpsertTunnel(t model.TunnelProfile) error

func (*BoltStateStore) UpsertUser added in v0.2.0

func (bs *BoltStateStore) UpsertUser(u model.User) error

func (*BoltStateStore) UsageDayNodeRows added in v0.2.3

func (bs *BoltStateStore) UsageDayNodeRows(nodeID, from, to string) ([]UsageDayNode, error)

func (*BoltStateStore) UsageDayUserRows added in v0.2.3

func (bs *BoltStateStore) UsageDayUserRows(userID, from, to string) ([]UsageDayUser, error)

func (*BoltStateStore) User added in v0.2.0

func (bs *BoltStateStore) User(id string) (model.User, bool, error)

func (*BoltStateStore) UserByUsername added in v0.2.0

func (bs *BoltStateStore) UserByUsername(username string) (model.User, bool, error)

func (*BoltStateStore) ValidateAuditLog added in v0.2.3

func (bs *BoltStateStore) ValidateAuditLog() error

ValidateAuditLog decodes every audit record and keeps none, so a corrupt record is found without the log being materialised. This is an open-time check: the log is append-only, so records already read cannot change under a running process, and repeating the walk on a health probe buys nothing.

type CapabilityPolicy added in v0.2.3

type CapabilityPolicy struct {
	Capability string    `json:"capability"`
	Enforced   bool      `json:"enforced"`
	ActorID    string    `json:"actor_id,omitempty"`
	UpdatedAt  time.Time `json:"updated_at"`
}

CapabilityPolicy is the operator's decision about whether one capability's gate is live on this fleet.

Whether a gate is enforced is policy, not mechanism: the code knows how to resolve scope and what a sensible default is, but only the operator knows when their fleet has been curated enough to switch a capability on without refusing work that should succeed. Baking that into a compile-time constant meant a release per capability, on a product people self-host.

type GuardRealitySnapshot added in v0.2.3

type GuardRealitySnapshot struct {
	Reality    model.GuardNodeReality `json:"reality"`
	ReceivedAt time.Time              `json:"received_at"`
}

GuardRealitySnapshot stores the server-accepted, normalized latest reality report for one node. It deliberately contains operational facts only: raw request bytes, bearer credentials, stderr, key material, and secrets are forbidden from this collection.

type LegacyKVKey added in v0.2.3

type LegacyKVKey struct {
	Bucket string
	Key    string
}

type LineChainAttempt added in v0.2.3

type LineChainAttempt struct {
	ApprovalID              string              `json:"approval_id"`
	Operation               string              `json:"operation"`
	SourceLineUUID          string              `json:"source_line_uuid"`
	SourceNodeID            string              `json:"source_node_id"`
	CandidateTargetLineUUID string              `json:"candidate_target_line_uuid,omitempty"`
	CandidateTargetNodeID   string              `json:"candidate_target_node_id,omitempty"`
	BaseGeneration          uint64              `json:"base_generation"`
	BaseArtifactSHA256      string              `json:"base_artifact_sha256,omitempty"`
	CandidateArtifactSHA256 string              `json:"candidate_artifact_sha256,omitempty"`
	CandidateDefinition     LineChainDefinition `json:"candidate_definition"`
	RequestSHA256           string              `json:"request_sha256"`
	PlanGraphRevision       uint64              `json:"plan_graph_revision"`
	QueuedGraphRevision     uint64              `json:"queued_graph_revision,omitempty"`
	FirstLeaseGraphRevision uint64              `json:"first_lease_graph_revision,omitempty"`
	IssuedTaskID            string              `json:"issued_task_id,omitempty"`
	IssuedLeaseID           string              `json:"issued_lease_id,omitempty"`
	IssuedScriptSHA256      string              `json:"issued_script_sha256,omitempty"`
	IssuedArtifactSHA256    string              `json:"issued_artifact_sha256,omitempty"`
	Status                  string              `json:"status"`
	LastErrorCode           string              `json:"last_error_code,omitempty"`
	LastError               string              `json:"last_error,omitempty"`
	CreatedAt               time.Time           `json:"created_at"`
	UpdatedAt               time.Time           `json:"updated_at"`
}

LineChainAttempt is an in-flight candidate, stored separately so a failed replace/remove cannot overwrite the active committed edge.

type LineChainCompileStateSnapshot added in v0.2.3

type LineChainCompileStateSnapshot struct {
	Nodes               map[string]model.Node
	LineUUIDByHash      map[string]string
	LineUUIDOwnerByHash map[string]string
	VpnUsers            map[string]VpnUserPublicRecord
	VpnUserSecrets      map[string]VpnUserSecretRecord
	ManagedLines        map[string]ManagedLinePublicRecord
	ManagedLineSecrets  map[string]ManagedLineSecretRecord
	Chains              LineChainSnapshot
}

LineChainCompileStateSnapshot is the persistent half of the compiler input. It is copied under one store lock and is safe for server-side projection without further store reads or identity allocation.

type LineChainDefinition added in v0.2.3

type LineChainDefinition struct {
	SourceLineUUID             string    `json:"source_line_uuid"`
	SourceNodeID               string    `json:"source_node_id"`
	SourceLineHashID           string    `json:"source_line_hash_id"`
	SourceInboundTag           string    `json:"source_inbound_tag"`
	TargetLineUUID             string    `json:"target_line_uuid,omitempty"`
	TargetNodeID               string    `json:"target_node_id,omitempty"`
	TargetDefinitionDigest     string    `json:"target_definition_digest,omitempty"`
	TargetPublicMaterialDigest string    `json:"target_public_material_digest,omitempty"`
	TargetCredentialDigest     string    `json:"target_credential_digest,omitempty"`
	OutboundTag                string    `json:"outbound_tag"`
	FragmentPath               string    `json:"fragment_path"`
	FragmentSHA256             string    `json:"fragment_sha256"`
	SidecarPatchSHA256         string    `json:"sidecar_patch_sha256"`
	ArtifactSHA256             string    `json:"artifact_sha256"`
	ApprovalID                 string    `json:"approval_id"`
	TaskID                     string    `json:"task_id,omitempty"`
	ActorID                    string    `json:"actor_id,omitempty"`
	TokenID                    string    `json:"token_id,omitempty"`
	AuditTargetLineUUID        string    `json:"audit_target_line_uuid,omitempty"`
	Status                     string    `json:"status"`
	DriftCode                  string    `json:"drift_code,omitempty"`
	Generation                 uint64    `json:"generation"`
	ObservationRevision        uint64    `json:"observation_revision,omitempty"`
	CreatedAt                  time.Time `json:"created_at"`
	UpdatedAt                  time.Time `json:"updated_at"`
}

LineChainDefinition is the committed host baseline. Target fields are empty only for a committed remove tombstone awaiting scheduled observation.

type LineChainFirstLeaseValidator added in v0.2.3

type LineChainFirstLeaseValidator func(LineChainCompileStateSnapshot, model.Approval, LineChainAttempt, model.Task) error

type LineChainObservation added in v0.2.3

type LineChainObservation struct {
	OutboundTag        string
	DownstreamLineUUID string
}

type LineChainSnapshot added in v0.2.3

type LineChainSnapshot struct {
	Definitions map[string]LineChainDefinition
	Attempts    map[string]LineChainAttempt
	Revision    uint64
}

type LineSecretMigrationBuild added in v0.2.3

type LineSecretMigrationBuild struct {
	VpnUsers           map[string]VpnUserPublicRecord
	VpnUserSecrets     map[string]VpnUserSecretRecord
	ManagedLines       map[string]ManagedLinePublicRecord
	ManagedLineSecrets map[string]ManagedLineSecretRecord
	Legacy             []LegacyKVKey
}

type LineSecretMigrationSource added in v0.2.3

type LineSecretMigrationSource struct {
	VpnUsers           map[string]VpnUserPublicRecord
	VpnUserSecrets     map[string]VpnUserSecretRecord
	ManagedLines       map[string]ManagedLinePublicRecord
	ManagedLineSecrets map[string]ManagedLineSecretRecord
	KV                 []model.KVEntry
	ProxyUsers         []model.ProxyUser
}

type ManagedLinePublicRecord added in v0.2.3

type ManagedLinePublicRecord struct {
	LineUUID         string    `json:"line_uuid"`
	NodeID           string    `json:"node_id"`
	LineHashID       string    `json:"line_hash_id"`
	Tag              string    `json:"tag"`
	Port             int       `json:"port"`
	SNI              string    `json:"sni"`
	HandshakeServer  string    `json:"handshake_server"`
	HandshakePort    int       `json:"handshake_port"`
	RealityPublicKey string    `json:"reality_public_key"`
	ShortID          string    `json:"short_id"`
	UserID           string    `json:"user_id"`
	UserName         string    `json:"user_name"`
	FragmentSHA256   string    `json:"fragment_sha256"`
	Status           string    `json:"status"`
	ApprovalID       string    `json:"approval_id"`
	LastError        string    `json:"last_error,omitempty"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
}

type ManagedLineSecretRecord added in v0.2.3

type ManagedLineSecretRecord struct {
	RealityPrivateKey string `json:"reality_private_key"`
}

type MigrationOptions added in v0.2.0

type MigrationOptions struct {
	Overwrite bool
}

type NetGuardCompileSnapshot added in v0.2.3

type NetGuardCompileSnapshot struct {
	Node    model.Node
	Binding model.NodeGuardBinding
	// HasBinding is false when Binding was synthesised for a node with no
	// stored binding: an empty, observe-only intent that lets review and
	// suggestions run without creating a record.
	HasBinding  bool
	Groups      []model.SecurityGroup
	GuardZones  []model.GuardZone
	NFTInputs   model.NFTInputs
	HasNFTInput bool
	Nodes       map[string]model.Node
}

NetGuardCompileSnapshot is one immutable, revision-consistent view of every store record that can affect compilation for a node.

type NodeCapability added in v0.2.3

type NodeCapability struct {
	NodeID     string `json:"node_id"`
	Capability string `json:"capability"`
	State      string `json:"state"`
	// Reason is required for excluded, so a decision keeps its justification
	// where the decision lives rather than in a chat log.
	Reason    string    `json:"reason,omitempty"`
	ActorID   string    `json:"actor_id,omitempty"`
	UpdatedAt time.Time `json:"updated_at"`
}

type NodeCascadeReport added in v0.2.0

type NodeCascadeReport struct {
	NodeID        string `json:"node_id"`
	TasksStripped int    `json:"tasks_stripped"` // nodeID removed from Targets, task kept
	// TaskNodeRefsCleared counts tasks that still named the node through
	// TargetLeases or RerunOfNodeID after Targets no longer did.
	TaskNodeRefsCleared int `json:"task_node_refs_cleared"`
	TasksDeleted        int `json:"tasks_deleted"` // task deleted (sole target)
	TaskResults         int `json:"task_results"`
	DDNSProfiles        int `json:"ddns_profiles"`
	MachineProfiles     int `json:"machine_profiles"`
	NFTInputs           int `json:"nft_inputs"`
	DNSDeployments      int `json:"dns_deployments"`
	NetPolicies         int `json:"net_policies"`
	// NetPeerRulesStripped / GroupPolicyRulesStripped count node-reference rules
	// removed from OTHER nodes' net policies and from group policies (SHARED:
	// strip the dangling Remote.NodeID rule, keep the owner's policy).
	NetPeerRulesStripped        int `json:"net_peer_rules_stripped"`
	GroupPolicyRulesStripped    int `json:"group_policy_rules_stripped"`
	GeoRoutingStripped          int `json:"geo_routing_stripped"` // SHARED: stripped, kept
	GeoRoutingDeleted           int `json:"geo_routing_deleted"`  // became empty -> deleted
	AgentUpdatePolicies         int `json:"agent_update_policies"`
	ProxyNodeProfiles           int `json:"proxy_node_profiles"`
	ProxyUsageSnapshots         int `json:"proxy_usage_snapshots"`
	MonitorsStripped            int `json:"monitors_stripped"` // SHARED: stripped from Monitor.NodeIDs
	MonitorResults              int `json:"monitor_results"`
	LogSources                  int `json:"log_sources"`
	Groups                      int `json:"groups"`    // Members/LeaderID edited
	Approvals                   int `json:"approvals"` // NO existing primitive
	Tunnels                     int `json:"tunnels"`
	GuardRealitySnapshots       int `json:"guard_reality_snapshots"`
	GuardBindings               int `json:"guard_bindings"`
	ManagedLines                int `json:"managed_lines"`
	LineChainAttemptsReleased   int `json:"line_chain_attempts_released"`
	LineChainDefinitionsDeleted int `json:"line_chain_definitions_deleted"`
	LineChainTargetsDrifted     int `json:"line_chain_targets_drifted"`
	LineChainLeaseConflicts     int `json:"line_chain_lease_conflicts"`
	// RemovedLogSourceIDs lists the log-source IDs whose records this delete
	// removed from the JSON store. The SERVER must call logStore.PurgeSource on
	// each (the log lines live in a separate bbolt db the store cannot reach).
	RemovedLogSourceIDs []string `json:"-"`
}

NodeCascadeReport tallies what a node hard-delete removed (or, in plan mode, would remove) from the JSON store. It is a server-LOCAL plain report: the store cannot import internal/server (that would be an import cycle), so it returns this struct and the server layer maps it onto the wire DTO.

SHARED resources (GeoRouting, Monitors, Groups) are stripped of the gone node rather than deleted, because deleting them would also affect other still-live nodes. Node-owned resources are deleted outright.

type NodeStatusEvent added in v0.2.3

type NodeStatusEvent struct {
	At    time.Time `json:"at"`
	To    string    `json:"to"`
	Cause string    `json:"cause"`
}

NodeStatusEvent is one transition. To is NodeStatusOnline or NodeStatusOffline; Cause names the hook that wrote it.

type NotifyWebhook added in v0.2.3

type NotifyWebhook struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	// EventType is what the existing notification rules match on. It is fixed by
	// the operator at authoring time; a caller cannot choose or override it, so a
	// webhook can only ever raise the one event its author intended.
	EventType string `json:"event_type"`
	// TitleTemplate and BodyTemplate are the operator's message shape. They may
	// interpolate {{data.<field>}} from the caller payload plus the platform
	// variables ({{event_type}}, {{webhook_name}}, {{received_at}}).
	TitleTemplate string `json:"title_template"`
	BodyTemplate  string `json:"body_template"`
	// SecretHash is a PBKDF2 hash, in the same encoding auth.HashSecret produces
	// for storage and node tokens. The plaintext secret is returned exactly once,
	// at creation and at each rotation, and is not recoverable afterwards: a
	// reader of the state file gets no working credential, which is a stronger
	// position than the reversible envelope NotifyChannel.Config carries.
	SecretHash string    `json:"secret_hash,omitempty"`
	Enabled    bool      `json:"enabled"`
	LastUsedAt time.Time `json:"last_used_at,omitzero"`
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
}

NotifyWebhook is an operator-authored inbound entry point that turns an HTTP POST from an outside caller into a notification event.

The type lives here rather than in the SDK model package because nothing outside this server needs it: a webhook is never sent to a node, never crosses the plugin ABI, and never appears in an agent payload. Keeping it server-local also keeps the SDK pin free of a change this slice would otherwise have to coordinate.

The security-relevant shape is that a webhook is *authored*, not *declared by its caller*. EventType and the two templates are set by an operator holding notify:send; the caller of the public endpoint supplies only bounded data for the templates to interpolate. That split is what stops possession of a webhook URL and secret from becoming the ability to send the operator an arbitrary message.

type NotifyWebhookDelivery added in v0.2.3

type NotifyWebhookDelivery struct {
	ID        string `json:"id"`
	WebhookID string `json:"webhook_id"`
	EventType string `json:"event_type"`
	// Outcome is one of the NotifyWebhook* constants above.
	Outcome string `json:"outcome"`
	// Reason explains a rejection or failure in operator-readable terms. It never
	// contains caller-supplied text, only fixed strings chosen by this server, so
	// that a hostile caller cannot write into the console through it.
	Reason string `json:"reason,omitempty"`
	// Title and Body are the rendered message, retained so the operator can see
	// what would have been sent even when nothing was routed.
	Title string `json:"title,omitempty"`
	Body  string `json:"body,omitempty"`
	// SourceIP is the caller address as the server resolved it.
	SourceIP string `json:"source_ip,omitempty"`
	// Fields is the number of caller-supplied data fields accepted.
	Fields int `json:"fields"`
	// Bytes is the size of the caller payload.
	Bytes int `json:"bytes"`
	// Channels is the number of channel sends planned, and Delivered the number
	// that returned without error.
	Channels  int       `json:"channels"`
	Delivered int       `json:"delivered"`
	Test      bool      `json:"test,omitempty"`
	CreatedAt time.Time `json:"created_at"`
}

NotifyWebhookDelivery is one attempt against one webhook, retained in a bounded per-webhook ring so the console can answer "did my webhook work" without scanning the audit stream.

This does not replace the audit trail, it complements it. The audit event is written synchronously and records the security decision: who called, from where, and whether the platform accepted it. That is the evidence record and it is append-only. But the audit event is necessarily written before the channel sends happen, because the sends are asynchronous and can take seconds; it therefore cannot say whether the operator's phone actually rang. This record is updated when the fan-out settles and carries that outcome.

type ProxyUsageUpdate added in v0.2.3

type ProxyUsageUpdate struct {
	Users    []model.ProxyUser
	Profile  *model.ProxyNodeProfile
	Snapshot *model.ProxyUsageSnapshot
	// DayNode and DayUsers are deltas: they are added into the stored rows.
	DayNode  *UsageDayNode
	DayUsers []UsageDayUser
}

ProxyUsageUpdate is one ingestion's write set, committed together so a crash between the snapshot and its day rows cannot leave a delta counted twice on the next report.

type SingBoxLiveness added in v0.2.3

type SingBoxLiveness struct {
	NodeID  string               `json:"node_id"`
	Runtime model.SingBoxRuntime `json:"runtime"`
	// State is running | down | restarting | unknown.
	State      string    `json:"state"`
	StateSince time.Time `json:"state_since"`
	// ProblemSince is set when the state leaves running for down/restarting
	// and cleared only by running again: a probe outage in the middle of an
	// incident must not reset the alert clock.
	ProblemSince time.Time `json:"problem_since,omitempty"`
	// NotifiedDownAt is when the down notification for the current problem
	// episode fired; zero when it has not. Cleared on recovery.
	NotifiedDownAt time.Time `json:"notified_down_at,omitempty"`
	ReceivedAt     time.Time `json:"received_at"`
}

SingBoxLiveness is the durable service-liveness record for one node (design-19): the latest probe the agent reported, the state the server derived from it, and the transition bookkeeping that notification debouncing needs. It is persisted, unlike the inventory mirror, so a multi-day outage survives a server restart and can be reported after the fact.

type State

type State struct {
	Users              map[string]model.User        `json:"users"`
	Tokens             map[string]model.Token       `json:"tokens"`
	Nodes              map[string]model.Node        `json:"nodes"`
	Tasks              map[string]model.Task        `json:"tasks"`
	Results            []model.TaskResult           `json:"results"`
	TaskResultReceipts map[string]TaskResultReceipt `json:"task_result_receipts,omitempty"`
	TaskExecContexts   map[string]TaskExecContext   `json:"task_exec_contexts,omitempty"`
	TaskTargetStates   map[string]TaskTargetState   `json:"task_target_states,omitempty"`
	NodeCapabilities   map[string]NodeCapability    `json:"node_capabilities,omitempty"`
	CapabilityPolicies map[string]CapabilityPolicy  `json:"capability_policies,omitempty"`
	ApprovalRejections map[string]ApprovalRejection `json:"approval_rejections,omitempty"`
	Audit              []model.AuditEvent           `json:"audit"`
	KV                 map[string]model.KVEntry     `json:"kv"`
	// PluginSecrets is the encrypted, namespaced plugin vault (spec §9.4). It is a
	// distinct collection from KV on purpose: KV is plaintext at rest AND readable
	// over GET /api/kv by any principal holding kv:read. A secret must have neither
	// property, so it gets its own map, its own cipher pass, and no HTTP handler.
	PluginSecrets          map[string]model.KVEntry              `json:"plugin_secrets"`
	VpnUsers               map[string]VpnUserPublicRecord        `json:"vpn_users"`
	VpnUserSecrets         map[string]VpnUserSecretRecord        `json:"vpn_user_secrets"`
	ManagedLines           map[string]ManagedLinePublicRecord    `json:"managed_lines"`
	ManagedLineSecrets     map[string]ManagedLineSecretRecord    `json:"managed_line_secrets"`
	LineChainDefinitions   map[string]LineChainDefinition        `json:"line_chain_definitions"`
	LineChainAttempts      map[string]LineChainAttempt           `json:"line_chain_attempts"`
	LineChainAuditEvidence map[string]model.AuditEvent           `json:"line_chain_audit_evidence,omitempty"`
	LineChainGraphRevision uint64                                `json:"line_chain_graph_revision"`
	SubscriptionShares     map[string]model.SubscriptionShare    `json:"subscription_shares"`
	SubscriptionSnapshots  map[string]model.SubscriptionSnapshot `json:"subscription_snapshots"`
	Static                 map[string]model.StaticObject         `json:"static"`
	StorageBuckets         map[string]model.StorageBucket        `json:"storage_buckets"`
	StorageBindings        map[string]model.StorageBinding       `json:"storage_bindings"`
	StorageTokens          map[string]model.StorageAccessToken   `json:"storage_tokens"`
	Plugins                map[string]model.PluginInstallation   `json:"plugins"`
	Approvals              map[string]model.Approval             `json:"approvals"`
	Sessions               map[string]auth.Session               `json:"sessions"`
	DDNS                   map[string]model.DDNSProfile          `json:"ddns"`
	Monitors               map[string]model.Monitor              `json:"monitors"`
	MonResults             map[string][]model.MonitorResult      `json:"monitor_results"`
	LogSources             map[string]model.LogSource            `json:"log_sources"`
	TraceSessions          map[string]model.TraceSession         `json:"trace_sessions"`
	NotifyChannels         map[string]model.NotifyChannel        `json:"notify_channels"`
	NotifyRules            map[string]model.NotifyRule           `json:"notify_rules"`
	// NotifyWebhooks are operator-authored inbound entry points (notify_webhook.go).
	// They hold a PBKDF2 secret hash, not a reversible secret, so unlike
	// NotifyChannels they need no pass in crypto.go.
	NotifyWebhooks map[string]NotifyWebhook `json:"notify_webhooks"`
	// NotifyWebhookDeliveries is the bounded per-webhook attempt history keyed by
	// webhook id, retained for the console. The durable security record is the
	// audit stream, not this.
	NotifyWebhookDeliveries map[string][]NotifyWebhookDelivery  `json:"notify_webhook_deliveries,omitempty"`
	Tunnels                 map[string]model.TunnelProfile      `json:"tunnels"`
	MachineProfiles         map[string]model.MachineProfile     `json:"machine_profiles"`
	MachineVendors          map[string]model.MachineVendor      `json:"machine_vendors"`
	NFTInputs               map[string]model.NFTInputs          `json:"nft_inputs"`
	SecurityGroups          map[string]model.SecurityGroup      `json:"security_groups"`
	GuardZones              map[string]model.GuardZone          `json:"guard_zones"`
	GuardBindings           map[string]model.NodeGuardBinding   `json:"guard_bindings"`
	GuardRealitySnapshots   map[string]GuardRealitySnapshot     `json:"guard_reality_snapshots"`
	SingBoxLiveness         map[string]SingBoxLiveness          `json:"singbox_liveness"`
	DNSDeployments          map[string]model.DNSDeployment      `json:"dns_deployments"`
	NetPolicies             map[string]model.NetPolicy          `json:"net_policies"`
	Groups                  map[string]model.Group              `json:"groups"`
	GroupPolicies           map[string]model.GroupNetPolicy     `json:"group_policies"`
	GeoRouting              map[string]model.GeoRouting         `json:"geo_routing"`
	AgentUpdates            map[string]model.AgentUpdatePolicy  `json:"agent_updates"`
	ProxyInbounds           map[string]model.ProxyInbound       `json:"proxy_inbounds"`
	ProxyUsers              map[string]model.ProxyUser          `json:"proxy_users"`
	ProxyProfiles           map[string]model.ProxyNodeProfile   `json:"proxy_profiles"`
	ProxyUsage              map[string]model.ProxyUsageSnapshot `json:"proxy_usage"`
	// UsageDayNodes and UsageDayUsers are the daily rollups (usage_days.go),
	// keyed "<id>/<yyyymmdd>". With the bolt hot store enabled they live only
	// in bolt and are read with a prefix seek, never held here.
	UsageDayNodes map[string]UsageDayNode `json:"usage_day_node"`
	UsageDayUsers map[string]UsageDayUser `json:"usage_day_user"`
	// NodeStatusEvents is the node status history (node_status_events.go),
	// keyed "<id>/<instant>" and placed like the day rollups.
	NodeStatusEvents map[string]NodeStatusEvent    `json:"node_status_events"`
	TOTPChallenges   map[string]auth.TOTPChallenge `json:"totp_challenges"`
	OIDCProviders    map[string]model.OIDCProvider `json:"oidc_providers"`
	OIDCIdentities   map[string]model.OIDCIdentity `json:"oidc_identities"`
	OIDCAuthStates   map[string]auth.OIDCAuthState `json:"oidc_auth_states"`
	// WebAuthnCreds holds registered passkeys keyed by store record id. The public
	// keys and credential ids are non-secret, so this map is persisted as-is (no
	// at-rest envelope like Users/Sessions carry).
	WebAuthnCreds map[string]auth.WebAuthnCredential `json:"webauthn_credentials"`
	// WebAuthnChallenges holds pending, short-lived passkey ceremony challenges,
	// mirroring TOTPChallenges.
	WebAuthnChallenges map[string]auth.WebAuthnChallenge `json:"webauthn_challenges"`
}

func LoadJSONState added in v0.2.0

func LoadJSONState(path string, cph secret.Cipher) (State, error)

type Store

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

func Open

func Open(path string) (*Store, error)

Open loads (or initializes) the store at path, resolving the at-rest encryption cipher from the environment or a key file under the data directory (see secret.Resolve). An empty path yields an in-memory store with encryption disabled (nothing is persisted).

func OpenWithCipher added in v0.2.0

func OpenWithCipher(path string, cph secret.Cipher) (*Store, error)

OpenWithCipher is Open with an explicitly supplied at-rest cipher. main uses it after logging the resolved key source; tests use it to inject a known cipher. A nil cipher disables encryption.

func (*Store) ActiveTraceSessions added in v0.2.3

func (s *Store) ActiveTraceSessions(now time.Time) []model.TraceSession

ActiveTraceSessions returns sessions still capturing at now. Expiry is evaluated on read rather than by a sweeper so that a session cannot outlive its TTL just because a background job did not run; the agent enforces the same deadline independently.

func (*Store) AddMonitorResult added in v0.2.0

func (s *Store) AddMonitorResult(r model.MonitorResult) error

AddMonitorResult appends a probe result, keeping only the most recent maxMonitorResults entries per monitor.

func (*Store) AddTaskResult

func (s *Store) AddTaskResult(r model.TaskResult) error

func (*Store) AddTaskResultWithContext added in v0.2.3

func (s *Store) AddTaskResultWithContext(r model.TaskResult, ctx *TaskExecContext) error

AddTaskResultWithContext records a result together with the agent posture it ran under. The two are staged into one snapshot on purpose: a result that persisted without its context would be exactly the ambiguous row this exists to remove, and a context without its result would outlive what it describes. A nil context records the result alone, which is what every caller predating this did and what the tests still exercise.

func (*Store) AdvanceTOTPStep added in v0.2.0

func (s *Store) AdvanceTOTPStep(userID string, step uint64) (bool, error)

AdvanceTOTPStep atomically enforces single-use of a TOTP code: it accepts the matched RFC-6238 step only if it is strictly greater than the highest step previously accepted for the user, then persists the new high-water mark. The compare-and-set runs entirely under the store lock so two concurrent logins presenting the same code cannot both succeed (one wins, the other observes a non-increasing step and is rejected). Returns true when the step was accepted and recorded; false when it was a replay (step <= LastTOTPStep) or the user is unknown.

func (*Store) AgentUpdatePolicies added in v0.2.0

func (s *Store) AgentUpdatePolicies() []model.AgentUpdatePolicy

AgentUpdatePolicies returns all update policies sorted by node id.

func (*Store) AgentUpdatePolicy added in v0.2.0

func (s *Store) AgentUpdatePolicy(nodeID string) (model.AgentUpdatePolicy, bool)

AgentUpdatePolicy returns the update policy for one node.

func (*Store) AllNFTInputs added in v0.2.0

func (s *Store) AllNFTInputs() []model.NFTInputs

AllNFTInputs returns all persisted nft inputs sorted by node id.

func (*Store) AppendAudit

func (s *Store) AppendAudit(ev model.AuditEvent) error

func (*Store) AppendAuditIdempotent added in v0.2.3

func (s *Store) AppendAuditIdempotent(ev model.AuditEvent) (bool, error)

AppendAuditIdempotent records required domain evidence exactly once by ID. External audit sinks deduplicate before the staged JSON authority is persisted, so a retry after any partial failure safely repairs the gap.

func (*Store) AppendNodeStatusEvent added in v0.2.3

func (s *Store) AppendNodeStatusEvent(id string, ev NodeStatusEvent) error

AppendNodeStatusEvent records one transition for a caller outside the two store-owned hooks; the hello path uses it for the beat that also registers the node.

func (*Store) ApplyProxyUsage added in v0.2.3

func (s *Store) ApplyProxyUsage(update ProxyUsageUpdate) error

ApplyProxyUsage commits one ingestion. It supersedes ApplyProxyUsageUpdate, which remains for callers that carry no day rows.

func (*Store) ApplyProxyUsageUpdate added in v0.2.0

func (s *Store) ApplyProxyUsageUpdate(users []model.ProxyUser, profile *model.ProxyNodeProfile, snapshot *model.ProxyUsageSnapshot) error

func (*Store) Approval

func (s *Store) Approval(id string) (model.Approval, bool)

func (*Store) ApprovalRejection added in v0.2.3

func (s *Store) ApprovalRejection(approvalID string) (ApprovalRejection, bool)

ApprovalRejection returns who rejected an approval, if a principal did. No record on a rejected approval means the node's task failed it.

func (*Store) Approvals

func (s *Store) Approvals() []model.Approval

func (*Store) ApproveLineChain added in v0.2.3

func (s *Store) ApproveLineChain(approval model.Approval, task model.Task, audits ...model.AuditEvent) (LineChainAttempt, bool, error)

ApproveLineChain atomically changes the reviewed approval and attempt to applying, reserves the candidate graph edge, queues exactly one bound task, and advances R to R+1 in one persistence transaction.

func (*Store) ApproveNetGuard added in v0.2.3

func (s *Store) ApproveNetGuard(approval model.Approval, task *model.Task) (committed bool, err error)

ApproveNetGuard atomically transitions the reviewed NetGuard approval and, when task is non-nil, queues its one host task while the reviewed binding plan anchor is still current. This closes the approval-check-to-decision window: a group, zone, node-address, or NFT-input mutation either invalidates the binding first and this call fails, or happens after the task exists and the leasing gate withholds the now-stale task.

func (*Store) AuditEventByID added in v0.2.3

func (s *Store) AuditEventByID(id string) (model.AuditEvent, bool)

func (*Store) AuditEvents

func (s *Store) AuditEvents() []model.AuditEvent

AuditEvents materialises the whole audit log, newest-first.

It is unbounded by construction, which is why no production read path uses it any more: the API pages through ScanAuditEventsDesc. It stays for the JSON-only store and for tests, whose logs are small.

func (*Store) AuditWALHead added in v0.2.0

func (s *Store) AuditWALHead() (string, int, bool)

AuditWALHead returns the current chain head hash and record count, and whether a WAL is configured. The head can be shipped off-box to detect end-truncation.

func (*Store) AuditWALVerify added in v0.2.0

func (s *Store) AuditWALVerify() (audit.Result, bool, error)

AuditWALVerify re-reads the append-only audit WAL and validates its hash chain. The second return is false when no WAL is configured (in-memory store). The walk itself runs with the store lock released. Holding it for the length of the walk is what made a readiness probe block every write for six seconds on a log of a million records; the file is append-only, so the length and the anchor captured together under the lock describe a prefix that cannot change underneath the walk.

func (*Store) BumpSecurityEpoch added in v0.2.0

func (s *Store) BumpSecurityEpoch(userID string) (uint64, error)

BumpSecurityEpoch increments the user's SecurityEpoch under the store lock and returns the new value. Sessions carry the epoch at which they were minted, so bumping it invalidates every previously-issued session for the user (used on 2FA disable, password change, and admin revoke). Returns (0, nil) when the user is unknown.

func (*Store) CancelTask added in v0.2.0

func (s *Store) CancelTask(id string) (model.Task, error)

CancelTask marks a queued or leased task as cancelled. For a queued task that withdraws delivery. For a leased task it means "stop waiting": the server stops offering redelivery and the lease gate refuses any late result, while whatever already started on a node runs to completion there; cancelling cannot reach into the machine, and does not pretend to. Without this, a task whose result was lost (or whose target died) was stuck "Running" with no operator way out. Terminal tasks stay refused with ErrTaskNotCancelable. The check and the mutation happen under one lock, so concurrent lease/cancel cannot race.

func (*Store) CapabilityPolicy added in v0.2.3

func (s *Store) CapabilityPolicy(capability string) (CapabilityPolicy, bool)

CapabilityPolicy returns the operator's decision for one capability, if they have made one. No policy means the compiled default applies.

func (*Store) Close added in v0.2.0

func (s *Store) Close() error

Close releases the audit WAL file handle.

func (*Store) CompleteLineChainTaskResult added in v0.2.3

func (s *Store) CompleteLineChainTaskResult(r model.TaskResult, approval model.Approval, terminalStatus, errorCode, terminalError string, audits ...model.AuditEvent) (bool, error)

CompleteLineChainTaskResult durably records the exact issued lease result and promotes (or fails) its candidate in one graph revision transition.

func (*Store) CompleteLineChainTaskResultClassified added in v0.2.3

func (s *Store) CompleteLineChainTaskResultClassified(r model.TaskResult, approval model.Approval, terminalError string,
	classifier func(LineChainCompileStateSnapshot, LineChainAttempt) (string, string, func(), error),
	auditFor func(string, string) model.AuditEvent,
) (bool, error)

CompleteLineChainTaskResultClassified runs success drift classification from one current persistent snapshot while holding the same lock through receipt and definition promotion. auditFor freezes evidence from that exact result.

func (*Store) CompleteNetGuardTaskResult added in v0.2.3

func (s *Store) CompleteNetGuardTaskResult(r model.TaskResult, approval model.Approval, binding model.NodeGuardBinding) (committed bool, err error)

CompleteNetGuardTaskResult commits the terminal task result together with the approval and binding transition it proves. The three records are staged and persisted as one state snapshot so callers never acknowledge a task whose authoritative apply state was dropped.

committed is true once the atomic rename crossed the persistence commit point. A non-nil error with committed=true means the parent-directory sync was not confirmed; live state is still published and ReadyCheck reports the durability degradation.

func (*Store) ConfirmDurability added in v0.2.3

func (s *Store) ConfirmDurability() error

ConfirmDurability retries the parent-directory synchronization after a committed atomic rename whose directory fsync was not confirmed. Callers use it before acknowledging a durable protocol transition (for example a lease delivery or terminal task-result receipt). A successful retry proves that the current state-file directory entry is stable; it does not rewrite state.

func (*Store) ConfirmTaskResultReplay added in v0.2.3

func (s *Store) ConfirmTaskResultReplay(r model.TaskResult) (matches, found bool, err error)

ConfirmTaskResultReplay atomically classifies an agent result replay and, on an exact match, confirms the state-file directory durability before the HTTP layer may acknowledge it. Receipt matching survives display-history pruning; generic task results intentionally remain outside this protocol.

func (*Store) ConsumeOIDCAuthState added in v0.2.0

func (s *Store) ConsumeOIDCAuthState(state string) (auth.OIDCAuthState, bool)

ConsumeOIDCAuthState atomically fetches and deletes the auth state for the given `state` value (single use). It returns false if the state is unknown or expired; an expired entry is still deleted.

func (*Store) ConsumeRecoveryCode added in v0.2.0

func (s *Store) ConsumeRecoveryCode(userID, code string) (bool, error)

ConsumeRecoveryCode atomically verifies and removes a single-use recovery code for a user, returning true only if a code matched. The read-modify-write runs entirely under the store lock so concurrent requests cannot double-spend one code or clobber each other's removal.

func (*Store) ConsumeTOTPChallenge added in v0.2.0

func (s *Store) ConsumeTOTPChallenge(id string) error

ConsumeTOTPChallenge marks a challenge spent by deleting it (single-use).

func (*Store) ConsumeWebAuthnChallenge added in v0.2.0

func (s *Store) ConsumeWebAuthnChallenge(id string) error

ConsumeWebAuthnChallenge marks a challenge spent by deleting it (single-use).

func (*Store) CountWebAuthnCredentialsByUser added in v0.2.0

func (s *Store) CountWebAuthnCredentialsByUser(userID string) int

CountWebAuthnCredentialsByUser reports how many passkeys a user has. Used to enforce the cap before beginning a registration ceremony (fail fast) and to decide whether a delete would remove the operator's last passkey.

func (*Store) CountWildcardAdmins added in v0.2.0

func (s *Store) CountWildcardAdmins(excludeID string) int

CountWildcardAdmins counts users holding the global "*" scope, excluding the given user id. It is the last-admin guard for the user-management API: a delete or de-admin that would drop this to zero must be refused.

func (*Store) CreateTask

func (s *Store) CreateTask(t model.Task) error

func (*Store) DDNSProfile added in v0.2.0

func (s *Store) DDNSProfile(id string) (model.DDNSProfile, bool)

DDNSProfile returns a profile by id.

func (*Store) DDNSProfiles added in v0.2.0

func (s *Store) DDNSProfiles() []model.DDNSProfile

DDNSProfiles returns all profiles sorted by creation time.

func (*Store) DDNSProfilesForNode added in v0.2.0

func (s *Store) DDNSProfilesForNode(nodeID string) []model.DDNSProfile

DDNSProfilesForNode returns the profiles bound to a node.

func (*Store) DNSDeployment added in v0.2.0

func (s *Store) DNSDeployment(id string) (model.DNSDeployment, bool)

DNSDeployment returns a self-hosted DNS deployment by id.

func (*Store) DNSDeployments added in v0.2.0

func (s *Store) DNSDeployments() []model.DNSDeployment

DNSDeployments returns all self-hosted DNS deployments sorted by creation time.

func (*Store) DNSDeploymentsForNode added in v0.2.0

func (s *Store) DNSDeploymentsForNode(nodeID string) []model.DNSDeployment

DNSDeploymentsForNode returns all DNS deployments bound to a node.

func (*Store) DeleteAgentUpdatePolicy added in v0.2.0

func (s *Store) DeleteAgentUpdatePolicy(nodeID string) error

DeleteAgentUpdatePolicy removes the update policy for one node.

func (*Store) DeleteDDNSProfile added in v0.2.0

func (s *Store) DeleteDDNSProfile(id string) error

DeleteDDNSProfile removes a profile.

func (*Store) DeleteDNSDeployment added in v0.2.0

func (s *Store) DeleteDNSDeployment(id string) error

DeleteDNSDeployment removes a self-hosted DNS deployment.

func (*Store) DeleteGeoRouting added in v0.2.0

func (s *Store) DeleteGeoRouting(id string) error

DeleteGeoRouting removes a geo-routing record.

func (*Store) DeleteGroup added in v0.2.0

func (s *Store) DeleteGroup(id string) error

DeleteGroup removes a group. It refuses (returns an error) when the group still has child groups (another group's ParentID points at it) or is the scope of any GroupNetPolicy, so deletion cannot orphan a subtree or silently drop an authored policy. Phase 1 surfaces this as an explicit reparent-to-root flow before delete. A missing id is a no-op (idempotent, matching the other Delete* methods).

func (*Store) DeleteGroupPolicy added in v0.2.0

func (s *Store) DeleteGroupPolicy(id string) error

DeleteGroupPolicy removes a group-scoped network policy. A missing id is a no-op (idempotent, matching the other Delete* methods).

func (*Store) DeleteGuardZone added in v0.2.1

func (s *Store) DeleteGuardZone(id string) error

DeleteGuardZone removes a stored guard zone.

func (*Store) DeleteKV added in v0.2.0

func (s *Store) DeleteKV(bucket, key string) error

func (*Store) DeleteLogSource added in v0.2.0

func (s *Store) DeleteLogSource(id string) error

DeleteLogSource removes a log source definition. The line store is purged separately by the caller via logstore.PurgeSource.

func (*Store) DeleteMachineProfile added in v0.2.0

func (s *Store) DeleteMachineProfile(id string) error

DeleteMachineProfile removes a machine profile.

func (*Store) DeleteMachineVendor added in v0.2.1

func (s *Store) DeleteMachineVendor(id string) error

func (*Store) DeleteMonitor added in v0.2.0

func (s *Store) DeleteMonitor(id string) error

DeleteMonitor removes a monitor and its result history.

func (*Store) DeleteNFTInputs added in v0.2.0

func (s *Store) DeleteNFTInputs(nodeID string) error

DeleteNFTInputs removes a node's stored baseline nft input set.

func (*Store) DeleteNetPolicy added in v0.2.0

func (s *Store) DeleteNetPolicy(nodeID string) error

DeleteNetPolicy removes the network policy for a target node.

func (*Store) DeleteNode added in v0.2.0

func (s *Store) DeleteNode(nodeID string) (NodeCascadeReport, bool, error)

DeleteNode hard-deletes a node and cascades the removal across every node-owned and node-referencing resource in a SINGLE critical section, then performs exactly one whole-snapshot Save. The bool is false (and no Save runs) when the node does not exist, so the operation is idempotent. Audit rows are never touched: deletion would break the append-only hash-chained WAL, and the SERVER records one node.delete audit event afterwards.

CRITICAL: every step is INLINE raw s.state mutation. The *ForNode / Delete* / Upsert* helpers each take s.mu themselves, and sync.Mutex is non-reentrant, so calling any of them here would self-deadlock.

func (*Store) DeleteNodeGuardBinding added in v0.2.1

func (s *Store) DeleteNodeGuardBinding(nodeID string) (model.NodeGuardBinding, bool, error)

DeleteNodeGuardBinding removes a node's guard binding and returns the record it removed, or ok=false when the node had none. It is the undo for an observe-only binding written by mistake. The managed check happens here, under the same lock as the delete, so a concurrent upsert that flips the binding to managed=true cannot slip between a caller's check and the removal: a managed binding is refused with ErrGuardBindingManaged and left in place.

func (*Store) DeleteNotifyChannel added in v0.2.0

func (s *Store) DeleteNotifyChannel(id string) error

DeleteNotifyChannel removes a channel.

func (*Store) DeleteNotifyRule added in v0.2.0

func (s *Store) DeleteNotifyRule(id string) error

DeleteNotifyRule removes a notification routing rule.

func (*Store) DeleteNotifyWebhook added in v0.2.3

func (s *Store) DeleteNotifyWebhook(id string) error

func (*Store) DeleteOIDCIdentitiesByUser added in v0.2.0

func (s *Store) DeleteOIDCIdentitiesByUser(userID string) int

DeleteOIDCIdentitiesByUser removes every durable subject→user link bound to userID. The map is keyed by provider+subject (not user id), so it is scanned. Used when deleting a user so a stale link can never re-resolve to a removed account. Returns the count removed.

func (*Store) DeleteOIDCProvider added in v0.2.0

func (s *Store) DeleteOIDCProvider(id string) error

func (*Store) DeletePluginSecret added in v0.2.3

func (s *Store) DeletePluginSecret(bucket, key string) error

func (*Store) DeleteProxyInbound added in v0.2.0

func (s *Store) DeleteProxyInbound(id string) error

DeleteProxyInbound removes a central proxy inbound template.

func (*Store) DeleteProxyNodeProfile added in v0.2.0

func (s *Store) DeleteProxyNodeProfile(nodeID string) error

DeleteProxyNodeProfile removes a per-node proxy render profile.

func (*Store) DeleteProxyUsageSnapshot added in v0.2.0

func (s *Store) DeleteProxyUsageSnapshot(nodeID string) error

DeleteProxyUsageSnapshot removes a node's last accounting snapshot.

func (*Store) DeleteProxyUser added in v0.2.0

func (s *Store) DeleteProxyUser(id string) error

DeleteProxyUser removes a proxy subscriber identity.

func (*Store) DeleteSecurityGroup added in v0.2.1

func (s *Store) DeleteSecurityGroup(id string) error

DeleteSecurityGroup removes a stored security group.

func (*Store) DeleteSession added in v0.2.0

func (s *Store) DeleteSession(id string) error

DeleteSession removes a session (logout / revocation).

func (*Store) DeleteSessionsByActor added in v0.2.0

func (s *Store) DeleteSessionsByActor(actorID string) int

DeleteSessionsByActor drops all live cookie sessions for actorID. Used on user delete so sessions are killed immediately rather than only failing closed on their next lookup. Returns the count removed.

func (*Store) DeleteStatic added in v0.2.3

func (s *Store) DeleteStatic(bucket, objectPath string) error

DeleteStatic removes one object. Static was write-only until now, which is tolerable for a handful of hand-uploaded assets and is not tolerable once something writes an object per record: without a delete, replacing a file's generator script would leave every previous version behind.

func (*Store) DeleteStorageBinding added in v0.2.0

func (s *Store) DeleteStorageBinding(id string) error

func (*Store) DeleteSubscriptionShare added in v0.2.3

func (s *Store) DeleteSubscriptionShare(id string) error

DeleteSubscriptionShare removes a share, which immediately stops serving its URL.

func (*Store) DeleteSubscriptionSnapshot added in v0.2.3

func (s *Store) DeleteSubscriptionSnapshot(pluginID, subscriptionID string) error

func (*Store) DeleteTask added in v0.2.0

func (s *Store) DeleteTask(id string) error

DeleteTask removes a task and any stored results for it from history. It returns ErrTaskNotFound when no such task exists.

func (*Store) DeleteToken added in v0.2.0

func (s *Store) DeleteToken(id string) (model.Token, bool, error)

DeleteToken removes a revoked API token by id. Active tokens must be revoked first by the caller so cleanup cannot accidentally invalidate live automation.

func (*Store) DeleteTraceSession added in v0.2.3

func (s *Store) DeleteTraceSession(id string) error

DeleteTraceSession removes a session record. Captured lines are purged separately through the trace store.

func (*Store) DeleteTunnel added in v0.2.0

func (s *Store) DeleteTunnel(id string) error

DeleteTunnel removes a tunnel profile.

func (*Store) DeleteUser added in v0.2.0

func (s *Store) DeleteUser(id string) bool

DeleteUser removes a user by id. Returns false if no such user existed.

func (*Store) DeleteVpnUserRecord added in v0.2.3

func (s *Store) DeleteVpnUserRecord(id string) error

func (*Store) DeleteWebAuthnCredential added in v0.2.0

func (s *Store) DeleteWebAuthnCredential(id, userID string) (bool, error)

DeleteWebAuthnCredential removes a passkey the user owns. Returns false if no such credential exists for that user (id unknown or owned by someone else).

func (*Store) EnableRuntimeBoltHotStore added in v0.2.0

func (s *Store) EnableRuntimeBoltHotStore(path string) error

EnableRuntimeBoltHotStore moves high-churn runtime collections to a record-level bbolt sidecar while keeping the Store API and in-memory read model unchanged. It is intentionally opt-in so operators can canary the Phase C runtime cutover without changing the JSON control-plane store.

The sidecar owns audit events, interactive sessions, proxy users, per-node proxy profiles, and proxy usage snapshots. Existing JSON values are imported into bbolt on first enable; existing bbolt values are merged back into memory on every enable so a restart recovers hot-domain writes that intentionally did not rewrite the whole JSON file.

func (*Store) EnabledNotifyChannels added in v0.2.0

func (s *Store) EnabledNotifyChannels() []model.NotifyChannel

EnabledNotifyChannels returns only channels that are enabled.

func (*Store) EnabledNotifyRules added in v0.2.0

func (s *Store) EnabledNotifyRules() []model.NotifyRule

EnabledNotifyRules returns enabled notification rules.

func (*Store) EnabledOIDCProviders added in v0.2.0

func (s *Store) EnabledOIDCProviders() []model.OIDCProvider

func (*Store) ExpireTraceSessions added in v0.2.3

func (s *Store) ExpireTraceSessions(now time.Time) ([]model.TraceSession, error)

ExpireTraceSessions marks running sessions whose deadline has passed as expired and returns them. It is idempotent.

func (*Store) FailTOTPChallenge added in v0.2.0

func (s *Store) FailTOTPChallenge(id string, maxAttempts int) error

FailTOTPChallenge records a failed second-factor attempt against a challenge, burning it once it reaches maxAttempts so a single challenge cannot serve as an unlimited guessing oracle for its whole TTL.

func (*Store) GeoRouting added in v0.2.0

func (s *Store) GeoRouting(id string) (model.GeoRouting, bool)

GeoRouting returns a geo-routing record by id.

func (*Store) GeoRoutings added in v0.2.0

func (s *Store) GeoRoutings() []model.GeoRouting

GeoRoutings returns all geo-routing records sorted by creation time.

func (*Store) GeoRoutingsForNode added in v0.2.0

func (s *Store) GeoRoutingsForNode(nodeID string) []model.GeoRouting

GeoRoutingsForNode returns geo-routing records that reference nodeID as a participating target or an authoritative DNS node (for re-render on change).

func (*Store) Group added in v0.2.0

func (s *Store) Group(id string) (model.Group, bool)

Group returns a group by id (deep-copied).

func (*Store) GroupPolicies added in v0.2.0

func (s *Store) GroupPolicies() []model.GroupNetPolicy

GroupPolicies returns all group policies sorted by id. Expansion (Phase 2) applies the (Priority, id, ruleIndex) precedence; id-sort here only guarantees a deterministic list.

func (*Store) GroupPolicy added in v0.2.0

func (s *Store) GroupPolicy(id string) (model.GroupNetPolicy, bool)

GroupPolicy returns a group policy by id (deep-copied).

func (*Store) Groups added in v0.2.0

func (s *Store) Groups() []model.Group

Groups returns all groups sorted by id. Callers that render the tree order by (ParentID, Order); id-sort here only guarantees a deterministic list.

func (*Store) GuardRealitySnapshot added in v0.2.3

func (s *Store) GuardRealitySnapshot(nodeID string) (GuardRealitySnapshot, bool)

GuardRealitySnapshot returns a deep copy of one node's latest reality report.

func (*Store) GuardRealitySnapshots added in v0.2.3

func (s *Store) GuardRealitySnapshots() []GuardRealitySnapshot

GuardRealitySnapshots returns all snapshots sorted by node id.

func (*Store) GuardZone added in v0.2.1

func (s *Store) GuardZone(id string) (model.GuardZone, bool)

GuardZone returns one stored guard zone by id.

func (*Store) GuardZones added in v0.2.1

func (s *Store) GuardZones() []model.GuardZone

GuardZones returns all stored guard zones sorted by id.

func (*Store) KV

func (s *Store) KV(bucket string) []model.KVEntry

func (*Store) KVEntry added in v0.2.0

func (s *Store) KVEntry(bucket, key string) (model.KVEntry, bool)

func (*Store) LastAuditWALVerification added in v0.2.3

func (s *Store) LastAuditWALVerification() (AuditWALVerification, bool)

LastAuditWALVerification returns the most recent chain walk, and whether one has happened at all. Opening the store counts as one: OpenAnchoredWAL verifies the chain before it will append to it.

func (*Store) LastMonitorResultForNode added in v0.2.0

func (s *Store) LastMonitorResultForNode(monitorID, nodeID string) (model.MonitorResult, bool)

LastMonitorResultForNode returns a node's most recent result for a monitor.

func (*Store) LeaseTaskDeliveriesWithApprovalGate added in v0.2.3

func (s *Store) LeaseTaskDeliveriesWithApprovalGate(nodeID string, limit int, plugin, action string, allowed bool) ([]TaskDelivery, error)

func (*Store) LeaseTaskDeliveriesWithDurableProtocols added in v0.2.3

func (s *Store) LeaseTaskDeliveriesWithDurableProtocols(nodeID string, limit int, netGuardAllowed, lineChainAllowed bool) ([]TaskDelivery, error)

func (*Store) LeaseTaskDeliveriesWithLineChainValidator added in v0.2.3

func (s *Store) LeaseTaskDeliveriesWithLineChainValidator(nodeID string, limit int, netGuardAllowed, lineChainAllowed bool, validate LineChainFirstLeaseValidator) ([]TaskDelivery, error)

func (*Store) LeaseTasks

func (s *Store) LeaseTasks(nodeID string, limit int) ([]model.Task, error)

func (*Store) LeaseTasksWithApprovalGate added in v0.2.3

func (s *Store) LeaseTasksWithApprovalGate(nodeID string, limit int, plugin, action string, allowed bool) ([]model.Task, error)

LeaseTasksWithApprovalGate leases the same task set as LeaseTasks while gating one exact approval plugin/action pair. The approval lookup, current guard-plan-anchor check, and lease mutation share s.mu, so neither a capability downgrade nor dependency invalidation can slip through a server-side check-to-lease race. Empty plugin/action disables the gate.

func (*Store) LineChainCompileStateSnapshot added in v0.2.3

func (s *Store) LineChainCompileStateSnapshot() LineChainCompileStateSnapshot

func (*Store) LineChainSnapshot added in v0.2.3

func (s *Store) LineChainSnapshot() LineChainSnapshot

func (*Store) LineUUIDAuthoritySnapshot added in v0.2.3

func (s *Store) LineUUIDAuthoritySnapshot() (map[string]string, map[string]string)

LineUUIDAuthoritySnapshot returns the UUID and owning-node maps from one store generation. Callers can project an entire inventory without per-line store reads.

func (*Store) LogSource added in v0.2.0

func (s *Store) LogSource(id string) (model.LogSource, bool)

LogSource returns a log source by id.

func (*Store) LogSources added in v0.2.0

func (s *Store) LogSources() []model.LogSource

LogSources returns all log sources sorted by creation time.

func (*Store) LogSourcesForNode added in v0.2.0

func (s *Store) LogSourcesForNode(nodeID string) []model.LogSource

LogSourcesForNode returns the enabled log sources a node should tail.

func (*Store) MachineProfile added in v0.2.0

func (s *Store) MachineProfile(id string) (model.MachineProfile, bool)

MachineProfile returns a profile by id.

func (*Store) MachineProfileForNode added in v0.2.0

func (s *Store) MachineProfileForNode(nodeID string) (model.MachineProfile, bool)

MachineProfileForNode returns the profile bound to a node, enforcing the v1 one-profile-per-node invariant at the API layer.

func (*Store) MachineProfiles added in v0.2.0

func (s *Store) MachineProfiles() []model.MachineProfile

MachineProfiles returns all profiles sorted by creation time.

func (*Store) MachineVendor added in v0.2.1

func (s *Store) MachineVendor(id string) (model.MachineVendor, bool)

func (*Store) MachineVendorByName added in v0.2.1

func (s *Store) MachineVendorByName(name string) (model.MachineVendor, bool)

func (*Store) MachineVendors added in v0.2.1

func (s *Store) MachineVendors() []model.MachineVendor

func (*Store) ManagedLineRecord added in v0.2.3

func (s *Store) ManagedLineRecord(id string) (ManagedLinePublicRecord, ManagedLineSecretRecord, bool)

func (*Store) ManagedLineRecords added in v0.2.3

func (s *Store) ManagedLineRecords() (map[string]ManagedLinePublicRecord, map[string]ManagedLineSecretRecord)

func (*Store) MarkPluginSubscriptionSnapshotsStale added in v0.2.3

func (s *Store) MarkPluginSubscriptionSnapshotsStale(pluginID string, _ time.Time) (bool, error)

MarkPluginSubscriptionSnapshotsStale invalidates every durable snapshot owned by one plugin in one store transaction. It deliberately preserves the last-good payload and provenance; the next access must fetch again before the snapshot can become fresh authority.

func (*Store) MarkStaleNodesOffline added in v0.2.0

func (s *Store) MarkStaleNodesOffline(threshold time.Duration, now time.Time, cause string) ([]model.Node, error)

MarkStaleNodesOffline flips Online -> false for every node that is currently Online but whose last heartbeat (LastSeen) is older than threshold. It is the liveness sweep that corrects the otherwise-sticky Online flag (which was only ever set true on a beat and never reset, so a dead node kept showing online forever). It returns the nodes that transitioned online->offline so the caller can audit/notify, and persists once if anything changed. cause names the sweep in each flipped node's status history.

func (*Store) MigrateLineSecrets added in v0.2.3

func (s *Store) MigrateLineSecrets(build func(LineSecretMigrationSource) (LineSecretMigrationBuild, error)) error

MigrateLineSecrets holds the authoritative store lock while the caller transforms a revision-consistent source snapshot and while the staged result crosses the JSON persistence commit point.

func (*Store) Monitor added in v0.2.0

func (s *Store) Monitor(id string) (model.Monitor, bool)

Monitor returns a monitor by id.

func (*Store) MonitorResults added in v0.2.0

func (s *Store) MonitorResults(monitorID string) []model.MonitorResult

MonitorResults returns the result history for a monitor (oldest first).

func (*Store) Monitors added in v0.2.0

func (s *Store) Monitors() []model.Monitor

Monitors returns all monitors sorted by creation time.

func (*Store) MonitorsForNode added in v0.2.0

func (s *Store) MonitorsForNode(nodeID string) []model.Monitor

MonitorsForNode returns the enabled monitors a node should run.

func (*Store) MutateApproval added in v0.2.3

func (s *Store) MutateApproval(id string, mutate func(a *model.Approval) bool) (model.Approval, bool, error)

MutateApproval applies mutate to the stored row under the store lock and persists it only when mutate returns true. The row mutate sees is the current one, so a caller that decided on a snapshot (a listing, a heartbeat) re-checks the status it is about to leave before writing, rather than overwriting a transition another request committed in between. An UpdatedAt the callback leaves at zero is set to now. The second result is false when the row does not exist or mutate declined.

func (*Store) NFTInputs added in v0.2.0

func (s *Store) NFTInputs(nodeID string) (model.NFTInputs, bool)

NFTInputs returns the persisted inputs for a node.

func (*Store) NetGuardCompileSnapshot added in v0.2.3

func (s *Store) NetGuardCompileSnapshot(nodeID string) (NetGuardCompileSnapshot, error)

NetGuardCompileSnapshot captures all compiler inputs under one store lock so review, approval, and result validation cannot observe a combination of policy revisions that never existed together.

func (*Store) NetPolicies added in v0.2.0

func (s *Store) NetPolicies() []model.NetPolicy

NetPolicies returns all network policies sorted by target node id.

func (*Store) NetPolicy added in v0.2.0

func (s *Store) NetPolicy(nodeID string) (model.NetPolicy, bool)

NetPolicy returns the policy for a target node.

func (*Store) Node

func (s *Store) Node(id string) (model.Node, bool)

func (*Store) NodeCapabilities added in v0.2.3

func (s *Store) NodeCapabilities() []NodeCapability

NodeCapabilities lists every recorded decision, for the console's per-node and per-capability views.

func (*Store) NodeCapability added in v0.2.3

func (s *Store) NodeCapability(nodeID, capability string) (NodeCapability, bool)

NodeCapability returns the recorded decision for one (node, capability), if any. No record is not a decision: the caller applies the capability default.

func (*Store) NodeGuardBinding added in v0.2.1

func (s *Store) NodeGuardBinding(nodeID string) (model.NodeGuardBinding, bool)

NodeGuardBinding returns the guard binding for a node.

func (*Store) NodeGuardBindings added in v0.2.1

func (s *Store) NodeGuardBindings() []model.NodeGuardBinding

NodeGuardBindings returns all guard bindings sorted by node id.

func (*Store) NodeStatusEvents added in v0.2.3

func (s *Store) NodeStatusEvents(id string) ([]NodeStatusEvent, error)

NodeStatusEvents returns one id's rows, oldest first.

func (*Store) Nodes

func (s *Store) Nodes() []model.Node

func (*Store) NotifyChannels added in v0.2.0

func (s *Store) NotifyChannels() []model.NotifyChannel

NotifyChannels returns all channels sorted by creation time.

func (*Store) NotifyRules added in v0.2.0

func (s *Store) NotifyRules() []model.NotifyRule

NotifyRules returns all notification rules sorted by creation time.

func (*Store) NotifyWebhook added in v0.2.3

func (s *Store) NotifyWebhook(id string) (NotifyWebhook, bool)

func (*Store) NotifyWebhookDeliveries added in v0.2.3

func (s *Store) NotifyWebhookDeliveries(webhookID string, limit int) []NotifyWebhookDelivery

NotifyWebhookDeliveries returns the retained attempts for one webhook, newest first, capped at limit.

func (*Store) NotifyWebhooks added in v0.2.3

func (s *Store) NotifyWebhooks() []NotifyWebhook

func (*Store) OIDCIdentity added in v0.2.0

func (s *Store) OIDCIdentity(providerID, subject string) (model.OIDCIdentity, bool)

func (*Store) OIDCProvider added in v0.2.0

func (s *Store) OIDCProvider(id string) (model.OIDCProvider, bool)

func (*Store) OIDCProviders added in v0.2.0

func (s *Store) OIDCProviders() []model.OIDCProvider

func (*Store) PendingLineChainAuditEvidence added in v0.2.3

func (s *Store) PendingLineChainAuditEvidence() ([]model.AuditEvent, error)

PendingLineChainAuditEvidence returns transition-owned evidence that has not yet been copied into the ordinary audit sink. Callers use it to repair a committed transition after a lost response or sink failure.

func (*Store) PlanDeleteNode added in v0.2.0

func (s *Store) PlanDeleteNode(nodeID string) (NodeCascadeReport, bool)

PlanDeleteNode computes the same cascade report DeleteNode would produce without mutating or persisting anything (a dry run). The bool is false when the node does not exist.

func (*Store) PlanLineChain added in v0.2.3

func (s *Store) PlanLineChain(attempt LineChainAttempt) (LineChainAttempt, bool, error)

PlanLineChain persists one planned attempt without reserving graph membership or incrementing the global revision.

func (*Store) PlanLineChainApproval added in v0.2.3

func (s *Store) PlanLineChainApproval(attempt LineChainAttempt, approval model.Approval, audits ...model.AuditEvent) (LineChainAttempt, bool, error)

PlanLineChainApproval persists the typed approval and candidate together.

func (*Store) PluginInstallation added in v0.2.0

func (s *Store) PluginInstallation(id string) (model.PluginInstallation, bool)

func (*Store) PluginInstallations added in v0.2.0

func (s *Store) PluginInstallations() []model.PluginInstallation

func (*Store) PluginSecret added in v0.2.3

func (s *Store) PluginSecret(bucket, key string) (model.KVEntry, bool)

func (*Store) ProxyInbound added in v0.2.0

func (s *Store) ProxyInbound(id string) (model.ProxyInbound, bool)

ProxyInbound returns a proxy inbound template by id.

func (*Store) ProxyInbounds added in v0.2.0

func (s *Store) ProxyInbounds() []model.ProxyInbound

ProxyInbounds returns all proxy inbound templates sorted by creation time.

func (*Store) ProxyNodeProfile added in v0.2.0

func (s *Store) ProxyNodeProfile(nodeID string) (model.ProxyNodeProfile, bool)

ProxyNodeProfile returns a proxy node profile by node id.

func (*Store) ProxyNodeProfiles added in v0.2.0

func (s *Store) ProxyNodeProfiles() []model.ProxyNodeProfile

ProxyNodeProfiles returns all proxy node profiles sorted by node id.

func (*Store) ProxyUsageSnapshot added in v0.2.0

func (s *Store) ProxyUsageSnapshot(nodeID string) (model.ProxyUsageSnapshot, bool)

ProxyUsageSnapshot returns the last accounting snapshot for a node.

func (*Store) ProxyUsageSnapshots added in v0.2.0

func (s *Store) ProxyUsageSnapshots() []model.ProxyUsageSnapshot

ProxyUsageSnapshots returns all proxy accounting snapshots sorted by node id.

func (*Store) ProxyUser added in v0.2.0

func (s *Store) ProxyUser(id string) (model.ProxyUser, bool)

ProxyUser returns a proxy user by id.

func (*Store) ProxyUsers added in v0.2.0

func (s *Store) ProxyUsers() []model.ProxyUser

ProxyUsers returns all proxy users sorted by creation time.

func (*Store) ProxyUsersForInbound added in v0.2.0

func (s *Store) ProxyUsersForInbound(inboundID string) []model.ProxyUser

ProxyUsersForInbound returns users provisioned on an inbound. Empty InboundIDs means the user is eligible for every enabled inbound.

func (*Store) PruneNodeStatusEvents added in v0.2.3

func (s *Store) PruneNodeStatusEvents(before time.Time) (int, error)

PruneNodeStatusEvents deletes every row older than the cutoff and reports how many went. Called on the liveness sweep tick.

func (*Store) PruneUsageDays added in v0.2.3

func (s *Store) PruneUsageDays(before string) (int, error)

PruneUsageDays deletes every row older than the given day and reports how many went. Called at the day roll, not on every ingestion.

func (*Store) PurgePluginSecrets added in v0.2.3

func (s *Store) PurgePluginSecrets(bucket string) error

PurgePluginSecrets removes an entire plugin's vault. Spec §10 makes purging plugin data an explicit, audited operator action; this is the primitive it needs.

func (*Store) PutKV

func (s *Store) PutKV(entry model.KVEntry) error

func (*Store) PutLineUUIDAuthority added in v0.2.3

func (s *Store) PutLineUUIDAuthority(hash, uuid, nodeID string) error

PutLineUUIDAuthority persists hash, UUID, and owning node as one store transition. Empty UUID removes both entries and is used only for rollback.

func (*Store) PutManagedLineRecord added in v0.2.3

func (s *Store) PutManagedLineRecord(public ManagedLinePublicRecord, private ManagedLineSecretRecord) error

func (*Store) PutOIDCAuthState added in v0.2.0

func (s *Store) PutOIDCAuthState(st auth.OIDCAuthState) error

func (*Store) PutOIDCIdentity added in v0.2.0

func (s *Store) PutOIDCIdentity(idn model.OIDCIdentity) error

func (*Store) PutPluginSecret added in v0.2.3

func (s *Store) PutPluginSecret(entry model.KVEntry) error

PutPluginSecret stores an encrypted-at-rest secret. There is deliberately no PluginSecrets(bucket) listing counterpart: a plugin reads back a key it chose to write, and nothing — not a plugin, not an HTTP handler — can enumerate the vault.

func (*Store) PutSession added in v0.2.0

func (s *Store) PutSession(sess auth.Session) error

PutSession persists a session, pruning expired entries and enforcing the session cap on every write so neither memory nor the state file grows unbounded under credential-stuffing or churn.

func (*Store) PutStatic

func (s *Store) PutStatic(obj model.StaticObject) error

func (*Store) PutTOTPChallenge added in v0.2.0

func (s *Store) PutTOTPChallenge(c auth.TOTPChallenge) error

PutTOTPChallenge stores a pending second-factor challenge, sweeping expired or used ones first so the set stays bounded (challenges have a short TTL).

func (*Store) PutVpnUserRecord added in v0.2.3

func (s *Store) PutVpnUserRecord(public VpnUserPublicRecord, private VpnUserSecretRecord) error

func (*Store) PutWebAuthnChallenge added in v0.2.0

func (s *Store) PutWebAuthnChallenge(c auth.WebAuthnChallenge) error

PutWebAuthnChallenge stores a pending passkey ceremony challenge, sweeping expired/used ones first so the set stays bounded (challenges are short-lived), exactly like PutTOTPChallenge.

func (*Store) ReadyCheck added in v0.2.0

func (s *Store) ReadyCheck() error

ReadyCheck verifies that persistence has no unresolved directory-sync failure and that the in-memory state can still be serialized with the configured at-rest cipher. It does not write to disk or return state contents; callers use it for readiness probes.

func (*Store) ReconcileLineChains added in v0.2.3

func (s *Store) ReconcileLineChains(observations map[string]LineChainObservation) (bool, error)

ReconcileLineChains advances committed set/replace definitions and remove tombstones from host-applied state using scheduled inventory evidence.

func (*Store) ReconcileLineChainsWithAudits added in v0.2.3

func (s *Store) ReconcileLineChainsWithAudits(observations map[string]LineChainObservation, auditFor func(LineChainDefinition) (model.AuditEvent, bool)) (bool, error)

ReconcileLineChainsWithAudits freezes evidence in the same authoritative JSON commit as its observed definition status transition.

func (*Store) RecordNotifyWebhookDelivery added in v0.2.3

func (s *Store) RecordNotifyWebhookDelivery(d NotifyWebhookDelivery) error

RecordNotifyWebhookDelivery appends an attempt and evicts the oldest beyond maxNotifyWebhookDeliveries.

func (*Store) RecordServerStart added in v0.2.3

func (s *Store) RecordServerStart(now time.Time) error

RecordServerStart writes the control plane's own transition pair. The previous process left no stop mark, so its last known instant is the newest heartbeat or transition it persisted. LastSeen is persisted at most every five minutes per node, so on a fleet of one that instant can trail the real stop by up to that much; the error only widens the unknown gap.

func (*Store) RejectLineChainApproval added in v0.2.3

func (s *Store) RejectLineChainApproval(approvalID, reason string, audits ...model.AuditEvent) (bool, error)

RejectLineChainApproval atomically retires a manually rejected candidate.

func (*Store) RejectLineChainApprovalStale added in v0.2.3

func (s *Store) RejectLineChainApprovalStale(approvalID, staleCode, reason string, audits ...model.AuditEvent) (bool, error)

RejectLineChainApprovalStale atomically retires a planned candidate whose bound inputs changed during approval-time recompile. Planned candidates have not reserved graph membership, so this transition never increments R.

func (*Store) RenameWebAuthnCredential added in v0.2.0

func (s *Store) RenameWebAuthnCredential(id, userID, name string) (auth.WebAuthnCredential, bool, error)

RenameWebAuthnCredential updates the operator-editable label of a passkey the user owns. Ownership is enforced here so a caller cannot rename someone else's credential by guessing its id. Returns the updated record.

func (*Store) ReplaceLineSecretRecords added in v0.2.3

func (s *Store) ReplaceLineSecretRecords(vpnPublic map[string]VpnUserPublicRecord, vpnPrivate map[string]VpnUserSecretRecord, managedPublic map[string]ManagedLinePublicRecord, managedPrivate map[string]ManagedLineSecretRecord, legacy []LegacyKVKey) error

ReplaceLineSecretRecords is the one authoritative migration transaction for both legacy line-secret domains.

func (*Store) ReplaceVpnUserRecords added in v0.2.3

func (s *Store) ReplaceVpnUserRecords(public map[string]VpnUserPublicRecord, private map[string]VpnUserSecretRecord, legacy []LegacyKVKey) error

ReplaceVpnUserRecords migrates public/private identities and removes legacy secret-bearing KV entries in one staged JSON persistence transaction.

func (*Store) ReserveLineChain added in v0.2.3

func (s *Store) ReserveLineChain(approvalID string, expectedRevision uint64) (LineChainAttempt, error)

ReserveLineChain performs the exact approval-time R -> R+1 graph CAS.

func (*Store) Results

func (s *Store) Results() []model.TaskResult

func (*Store) RevokeStorageAccessToken added in v0.2.0

func (s *Store) RevokeStorageAccessToken(id string) (model.StorageAccessToken, bool, error)

func (*Store) RevokeTokensByActor added in v0.2.0

func (s *Store) RevokeTokensByActor(actorID string) int

RevokeTokensByActor marks every non-revoked API token owned by actorID as revoked. Used when deleting a user: bearer tokens are validated by hash + RevokedAt and ignore the user's SecurityEpoch, so they must be revoked explicitly or they outlive the account. Returns the count revoked.

func (*Store) RotateNodeToken added in v0.2.0

func (s *Store) RotateNodeToken(nodeID, tokenHash string) (bool, error)

func (*Store) Save

func (s *Store) Save() error

func (*Store) ScanAuditEventsDesc added in v0.2.3

func (s *Store) ScanAuditEventsDesc(visit func(model.AuditEvent) bool) error

ScanAuditEventsDesc hands each audit event to visit, newest first, stopping as soon as visit returns false. One event is held at a time, so a caller that wants a page of a few hundred rows out of a million pays for the page.

Order is the order the events were appended, which is the order the audit chain records them; see BoltStateStore.ScanAuditEventsDesc.

func (*Store) SecurityGroup added in v0.2.1

func (s *Store) SecurityGroup(id string) (model.SecurityGroup, bool)

SecurityGroup returns one stored security group by id.

func (*Store) SecurityGroups added in v0.2.1

func (s *Store) SecurityGroups() []model.SecurityGroup

SecurityGroups returns all stored security groups sorted by id.

func (*Store) Session added in v0.2.0

func (s *Store) Session(id string) (auth.Session, bool)

Session returns an active session by id. Expired or revoked sessions report not-found without a write.

func (*Store) SetApprovalRejection added in v0.2.3

func (s *Store) SetApprovalRejection(r ApprovalRejection) error

SetApprovalRejection records a principal's rejection of an approval.

func (*Store) SetCapabilityPolicy added in v0.2.3

func (s *Store) SetCapabilityPolicy(policy CapabilityPolicy) error

SetCapabilityPolicy turns one capability's gate on or off for this fleet.

func (*Store) SetNodeCapability added in v0.2.3

func (s *Store) SetNodeCapability(c NodeCapability) error

SetNodeCapability records an enrolment decision. An empty state clears the record, which returns the node to the capability's default rather than asserting anything about it.

func (*Store) SetNodeDisabled added in v0.2.0

func (s *Store) SetNodeDisabled(nodeID string, disabled bool) (bool, error)

SetNodeDisabled flips a node's revocation flag. A disabled node's token is refused by authentication, so this is an immediate revocation without deleting history or config.

func (*Store) SetPluginStatus added in v0.2.0

func (s *Store) SetPluginStatus(id, status string) error

func (*Store) SetTaskQueueDeadline added in v0.2.3

func (s *Store) SetTaskQueueDeadline(d time.Duration)

SetTaskQueueDeadline sets how long a task may sit undelivered before the control plane withdraws it. Zero disables expiry.

func (*Store) SettleNotifyWebhookDelivery added in v0.2.3

func (s *Store) SettleNotifyWebhookDelivery(webhookID, deliveryID, outcome, reason string, delivered int) error

SettleNotifyWebhookDelivery updates an already-recorded attempt with the outcome of the asynchronous channel fan-out. A delivery that has since been evicted, or whose webhook was deleted mid-flight, is silently dropped: the audit event still holds the security-relevant record.

func (*Store) SingBoxLivenessAll added in v0.2.3

func (s *Store) SingBoxLivenessAll() map[string]SingBoxLiveness

SingBoxLivenessAll returns a copy of every node's liveness record.

func (*Store) SingBoxLivenessRecord added in v0.2.3

func (s *Store) SingBoxLivenessRecord(nodeID string) (SingBoxLiveness, bool)

SingBoxLivenessRecord returns one node's liveness record.

func (*Store) Static

func (s *Store) Static(bucket string) []model.StaticObject

func (*Store) StaticObject added in v0.2.0

func (s *Store) StaticObject(bucket, objectPath string) (model.StaticObject, bool)

func (*Store) StorageAccessToken added in v0.2.0

func (s *Store) StorageAccessToken(id string) (model.StorageAccessToken, bool)

func (*Store) StorageAccessTokens added in v0.2.0

func (s *Store) StorageAccessTokens(kind string) []model.StorageAccessToken

func (*Store) StorageBindingForHost added in v0.2.0

func (s *Store) StorageBindingForHost(kind, hostname string) (model.StorageBinding, bool)

func (*Store) StorageBindings added in v0.2.0

func (s *Store) StorageBindings(kind string) []model.StorageBinding

func (*Store) StorageBucket added in v0.2.0

func (s *Store) StorageBucket(kind, name string) (model.StorageBucket, bool)

func (*Store) StorageBucketInventory added in v0.2.3

func (s *Store) StorageBucketInventory(kind string) map[string]int

StorageBucketInventory counts what each bucket of a kind actually holds. StorageBuckets returns only the bucket records an operator registered, so a bucket a plugin wrote into without registering is invisible: the console listed nothing while the sub-store plugin kept its whole database, tens of kilobytes per subscription script, in plugin:latticenet.sub-store. A store that holds data must never read as empty.

func (*Store) StorageBuckets added in v0.2.0

func (s *Store) StorageBuckets(kind string) []model.StorageBucket

func (*Store) SubscriptionShare added in v0.2.3

func (s *Store) SubscriptionShare(id string) (model.SubscriptionShare, bool)

SubscriptionShare returns a share by id.

func (*Store) SubscriptionShareByToken added in v0.2.3

func (s *Store) SubscriptionShareByToken(token string) (model.SubscriptionShare, bool)

SubscriptionShareByToken resolves a share by its exact token. It is the only lookup the public endpoint performs, and that endpoint is unauthenticated, so this is the one comparison in the product an anonymous caller can drive.

The comparison is whole-string on purpose: a prefix or substring match would turn a partially guessed token into a working one. It is also constant-time, and the scan does not stop at the first hit. Returning early leaks, through timing, both how far a candidate token matched and where the matching share sat in the iteration; neither is information the caller is entitled to. The cost is a full pass over a map that holds one entry on a real deployment.

A duplicate token fails closed. It should be unreachable, since tokens are generated from a CSPRNG, but "unreachable" plus "silently serves whichever share the map happened to yield first" is a bad pair: Go randomises map iteration, so the same token would serve different subscriptions on different requests. Refusing is the only answer that is the same every time.

func (*Store) SubscriptionShares added in v0.2.3

func (s *Store) SubscriptionShares() []model.SubscriptionShare

SubscriptionShares returns every share sorted by creation time, then id, so the order is stable across calls and across processes.

func (*Store) SubscriptionSnapshot added in v0.2.3

func (s *Store) SubscriptionSnapshot(pluginID, subscriptionID string) (model.SubscriptionSnapshot, bool)

func (*Store) SubscriptionSnapshots added in v0.2.3

func (s *Store) SubscriptionSnapshots() []model.SubscriptionSnapshot

func (*Store) TOTPChallenge added in v0.2.0

func (s *Store) TOTPChallenge(id string) (auth.TOTPChallenge, bool)

TOTPChallenge returns an active (unused, unexpired) challenge by id.

func (*Store) Task added in v0.2.0

func (s *Store) Task(id string) (model.Task, bool)

func (*Store) TaskExecContext added in v0.2.3

func (s *Store) TaskExecContext(taskID, nodeID string) (TaskExecContext, bool)

TaskExecContext returns the posture pinned for one result, if one was pinned. Results recorded before this existed have none, and the caller must fall back rather than assume a missing context means an unprivileged agent.

func (*Store) TaskProgress added in v0.2.3

func (s *Store) TaskProgress(id string, now time.Time) (TaskProgress, bool)

TaskProgress reports, for a leased task, what each target is doing and whether the task as a whole has stopped making progress (see TaskStalled).

A lease whose StartedAt is zero counts as not live here: for re-execution safety taskLeaseExpired treats it as never expiring, but as evidence of progress it proves nothing, and this method answers the honesty question, not the redelivery one. The second return is false for a missing task or one that is not leased, for which no progress view exists.

func (*Store) TaskProgressStalled added in v0.2.3

func (s *Store) TaskProgressStalled(id string, now time.Time) bool

TaskProgressStalled reports whether this leased task has stopped making progress (see TaskStalled). A lease whose StartedAt is zero counts as not live here: for re-execution safety taskLeaseExpired treats it as never expiring, but as evidence of progress it proves nothing, and this method answers the honesty question, not the redelivery one.

func (*Store) TaskQueueDeadline added in v0.2.3

func (s *Store) TaskQueueDeadline() time.Duration

TaskQueueDeadline reports the configured deadline, so the API layer can describe a task with the same rule the lease gate enforces.

func (*Store) TaskResult added in v0.2.3

func (s *Store) TaskResult(taskID, nodeID string) (model.TaskResult, bool)

TaskResult returns the latest recorded terminal result for one task target. Durable NetGuard replay uses TaskResultReceipts instead of this bounded display history.

func (*Store) TaskResultReceiptMatches added in v0.2.3

func (s *Store) TaskResultReceiptMatches(r model.TaskResult) (matches, found bool)

TaskResultReceiptMatches reports whether a durable NetGuard replay receipt exists and whether the supplied lease/result exactly matches it.

func (*Store) TaskUsesLineChainProtocol added in v0.2.3

func (s *Store) TaskUsesLineChainProtocol(id string) bool

TaskUsesLineChainProtocol reports whether task mutation belongs to the E3 domain lifecycle rather than the generic task-management API.

func (*Store) Tasks

func (s *Store) Tasks() []model.Task

func (*Store) Token added in v0.2.0

func (s *Store) Token(id string) (model.Token, bool)

func (*Store) Tokens

func (s *Store) Tokens() []model.Token

func (*Store) TouchNodeToken added in v0.2.0

func (s *Store) TouchNodeToken(nodeID string, at time.Time, minInterval time.Duration) (bool, error)

func (*Store) TouchNotifyWebhook added in v0.2.3

func (s *Store) TouchNotifyWebhook(id string, at time.Time) error

TouchNotifyWebhook records that a webhook authenticated successfully. It is separate from UpsertNotifyWebhook so a fire never rewrites operator-authored fields, and so a concurrent edit cannot be clobbered by an inbound request.

func (*Store) TouchStorageAccessToken added in v0.2.0

func (s *Store) TouchStorageAccessToken(id string) error

func (*Store) TouchWebAuthnCredential added in v0.2.0

func (s *Store) TouchWebAuthnCredential(id string, signCount uint32, backupState bool, usedAt time.Time) error

TouchWebAuthnCredential records the results of a successful login against a credential: the refreshed signature counter, the current backup state, and the last-used timestamp. Ownership scoping keeps the write authoritative. The caller has already applied the clone-detection policy; this method only persists the agreed new values.

func (*Store) TraceSession added in v0.2.3

func (s *Store) TraceSession(id string) (model.TraceSession, bool)

TraceSession returns one trace session by id.

func (*Store) TraceSessions added in v0.2.3

func (s *Store) TraceSessions() []model.TraceSession

TraceSessions returns every trace session, newest first.

func (*Store) Tunnel added in v0.2.0

func (s *Store) Tunnel(id string) (model.TunnelProfile, bool)

Tunnel returns a tunnel profile by id.

func (*Store) Tunnels added in v0.2.0

func (s *Store) Tunnels() []model.TunnelProfile

Tunnels returns all tunnel profiles sorted by creation time.

func (*Store) UpdateMetrics

func (s *Store) UpdateMetrics(nodeID string, metrics model.Metrics, version, publicIP, publicIPv6, internalIP, internalIPv6, wgIP string, hostFacts model.HostFacts) (bool, error)

UpdateMetrics applies one heartbeat. It reports whether the beat turned the node from offline to online, so the caller can audit the edge the way the liveness sweep audits the other one.

func (*Store) UpdateNodeGeo added in v0.2.0

func (s *Store) UpdateNodeGeo(nodeID string, geo *model.NodeGeo) (model.Node, bool, error)

func (*Store) UpdateNodeMeta added in v0.2.0

func (s *Store) UpdateNodeMeta(nodeID, name, role, comment string, tags []string, agentSourceAllowlist *[]string, inventory **model.NodeInventory) (model.Node, bool, error)

UpdateNodeMeta sets the operator-owned node identity fields and optional agent source allowlist in one locked read-modify-write so it cannot clobber concurrently-reported metrics/last-seen. Tags are trimmed, de-duplicated, and empties dropped. A nil agentSourceAllowlist leaves that policy unchanged; non-nil replaces it, including an empty slice to clear it. inventory uses the same nil-means-unchanged convention one level deeper: a nil outer pointer leaves the stored inventory untouched, while a non-nil outer replaces it with its (possibly nil, i.e. cleared) inner value. Returns the updated node and whether it existed.

func (*Store) UpsertAgentUpdatePolicy added in v0.2.0

func (s *Store) UpsertAgentUpdatePolicy(policy model.AgentUpdatePolicy) error

UpsertAgentUpdatePolicy creates or updates the server-owned update intent for one node. NodeID is the stable key; policies carry no secrets.

func (*Store) UpsertApproval

func (s *Store) UpsertApproval(a model.Approval) error

func (*Store) UpsertDDNSProfile added in v0.2.0

func (s *Store) UpsertDDNSProfile(p model.DDNSProfile) error

UpsertDDNSProfile creates or updates a DDNS profile.

func (*Store) UpsertDNSDeployment added in v0.2.0

func (s *Store) UpsertDNSDeployment(dep model.DNSDeployment) error

UpsertDNSDeployment stores a self-hosted DNS deployment intent record.

func (*Store) UpsertGeoRouting added in v0.2.0

func (s *Store) UpsertGeoRouting(gr model.GeoRouting) error

UpsertGeoRouting creates or updates a geo-routing record.

func (*Store) UpsertGroup added in v0.2.0

func (s *Store) UpsertGroup(g model.Group) error

UpsertGroup creates or updates a fleet group. The group's own ID is the key; callers mint it as "grp_<id>" (see internal/id). Slices are deep-copied on store so the caller cannot mutate persisted state through a retained header.

Phase 1: group CRUD endpoints add slug/name uniqueness, parent-cycle, and nesting-depth validation here (or in a service layer above this method); this Phase-0 store write intentionally persists intent without those checks.

func (*Store) UpsertGroupPolicy added in v0.2.0

func (s *Store) UpsertGroupPolicy(p model.GroupNetPolicy) error

UpsertGroupPolicy creates or updates a group-scoped network policy. The policy ID is the key; callers mint it as "gnp_<id>" (see internal/id). Rules (and their Ports) are deep-copied on store.

func (*Store) UpsertGuardRealitySnapshot added in v0.2.3

func (s *Store) UpsertGuardRealitySnapshot(nodeIdentityUUID string, snapshot GuardRealitySnapshot) (GuardRealitySnapshot, bool, error)

UpsertGuardRealitySnapshot stores the latest normalized reality snapshot for a node. Same collected_at plus identical content is idempotent and does not rewrite received_at; same collected_at plus different content is a conflict.

func (*Store) UpsertGuardZone added in v0.2.1

func (s *Store) UpsertGuardZone(zone model.GuardZone) error

UpsertGuardZone creates or updates a named guard zone.

func (*Store) UpsertLogSource added in v0.2.0

func (s *Store) UpsertLogSource(ls model.LogSource) error

UpsertLogSource creates or updates a log source definition.

func (*Store) UpsertMachineProfile added in v0.2.0

func (s *Store) UpsertMachineProfile(p model.MachineProfile) error

UpsertMachineProfile creates or updates operator-authored machine metadata.

func (*Store) UpsertMachineVendor added in v0.2.1

func (s *Store) UpsertMachineVendor(v model.MachineVendor) error

UpsertMachineVendor creates or updates operator-authored vendor metadata.

func (*Store) UpsertMonitor added in v0.2.0

func (s *Store) UpsertMonitor(m model.Monitor) error

UpsertMonitor creates or updates a monitor.

func (*Store) UpsertNFTInputs added in v0.2.0

func (s *Store) UpsertNFTInputs(inputs model.NFTInputs) error

UpsertNFTInputs stores the authoritative baseline nft input set for a node. The key is NodeID so DNS/ACL/proxy providers can compose into one per-node lattice_guard render without coordinating a separate id namespace.

func (*Store) UpsertNetPolicy added in v0.2.0

func (s *Store) UpsertNetPolicy(policy model.NetPolicy) error

UpsertNetPolicy stores the operator-authored network policy for a node.

func (*Store) UpsertNode

func (s *Store) UpsertNode(n model.Node) error

func (*Store) UpsertNodeGuardBinding added in v0.2.1

func (s *Store) UpsertNodeGuardBinding(binding model.NodeGuardBinding) (model.NodeGuardBinding, error)

UpsertNodeGuardBinding creates or updates a node's guard binding with the same optimistic-concurrency contract as UpsertSecurityGroup.

func (*Store) UpsertNotifyChannel added in v0.2.0

func (s *Store) UpsertNotifyChannel(c model.NotifyChannel) error

UpsertNotifyChannel creates or updates a notification channel.

func (*Store) UpsertNotifyRule added in v0.2.0

func (s *Store) UpsertNotifyRule(rule model.NotifyRule) error

UpsertNotifyRule creates or updates a notification routing rule.

func (*Store) UpsertNotifyWebhook added in v0.2.3

func (s *Store) UpsertNotifyWebhook(hook NotifyWebhook) error

func (*Store) UpsertOIDCProvider added in v0.2.0

func (s *Store) UpsertOIDCProvider(p model.OIDCProvider) error

func (*Store) UpsertPluginInstallation added in v0.2.0

func (s *Store) UpsertPluginInstallation(p model.PluginInstallation) error

func (*Store) UpsertProxyInbound added in v0.2.0

func (s *Store) UpsertProxyInbound(in model.ProxyInbound) error

UpsertProxyInbound stores a central proxy inbound template.

func (*Store) UpsertProxyNodeProfile added in v0.2.0

func (s *Store) UpsertProxyNodeProfile(profile model.ProxyNodeProfile) error

UpsertProxyNodeProfile stores the per-node proxy render profile.

func (*Store) UpsertProxyUsageSnapshot added in v0.2.0

func (s *Store) UpsertProxyUsageSnapshot(snapshot model.ProxyUsageSnapshot) error

UpsertProxyUsageSnapshot stores the last accounting snapshot for a node.

func (*Store) UpsertProxyUser added in v0.2.0

func (s *Store) UpsertProxyUser(u model.ProxyUser) error

UpsertProxyUser stores a central proxy subscriber identity.

func (*Store) UpsertSecurityGroup added in v0.2.1

func (s *Store) UpsertSecurityGroup(group model.SecurityGroup) (model.SecurityGroup, error)

UpsertSecurityGroup creates or updates a reusable security group. New records must carry Version 0; updates must echo the stored Version. The store bumps the version and returns the persisted record.

func (*Store) UpsertSingBoxLiveness added in v0.2.3

func (s *Store) UpsertSingBoxLiveness(rec SingBoxLiveness) (SingBoxLiveness, bool, error)

UpsertSingBoxLiveness stores one node's liveness record and returns the previous one. The caller (the ingest path) owns state derivation and transition logic; this method owns durability only.

func (*Store) UpsertStorageAccessToken added in v0.2.0

func (s *Store) UpsertStorageAccessToken(t model.StorageAccessToken) error

func (*Store) UpsertStorageBinding added in v0.2.0

func (s *Store) UpsertStorageBinding(b model.StorageBinding) error

func (*Store) UpsertStorageBucket added in v0.2.0

func (s *Store) UpsertStorageBucket(b model.StorageBucket) error

func (*Store) UpsertSubscriptionShare added in v0.2.3

func (s *Store) UpsertSubscriptionShare(share model.SubscriptionShare) error

UpsertSubscriptionShare writes a share. Like proxy users, shares go to the record-level hot store when it is enabled rather than through a full rewrite of the state file.

func (*Store) UpsertSubscriptionSnapshot added in v0.2.3

func (s *Store) UpsertSubscriptionSnapshot(snap model.SubscriptionSnapshot) error

UpsertSubscriptionSnapshot writes the last good content for one subscription. It is durable rather than cached: it is what keeps clients served when a provider is unreachable.

func (*Store) UpsertSubscriptionSnapshotWithCommit added in v0.2.3

func (s *Store) UpsertSubscriptionSnapshotWithCommit(snap model.SubscriptionSnapshot) (bool, error)

UpsertSubscriptionSnapshotWithCommit distinguishes a pre-commit failure from a committed rename whose parent-directory durability confirmation failed. Callers must publish the committed state to their own cache authority even when err reports degraded durability.

func (*Store) UpsertToken

func (s *Store) UpsertToken(t model.Token) error

func (*Store) UpsertTraceSession added in v0.2.3

func (s *Store) UpsertTraceSession(ts model.TraceSession) error

UpsertTraceSession creates or updates a trace session.

func (*Store) UpsertTunnel added in v0.2.0

func (s *Store) UpsertTunnel(t model.TunnelProfile) error

UpsertTunnel creates or updates a tunnel profile.

func (*Store) UpsertUser

func (s *Store) UpsertUser(u model.User) error

func (*Store) UpsertWebAuthnCredential added in v0.2.0

func (s *Store) UpsertWebAuthnCredential(c auth.WebAuthnCredential) error

UpsertWebAuthnCredential stores (or replaces) a passkey record. On insert it enforces the per-user cap so a client cannot register unbounded credentials; updates to an existing record (rename, sign-count refresh) are always allowed.

func (*Store) UsageDayNodeRows added in v0.2.3

func (s *Store) UsageDayNodeRows(nodeID, from, to string) ([]UsageDayNode, error)

UsageDayNodeRows returns a node's rows for the inclusive day range, oldest first. Rows are copies: callers may mutate them freely.

func (*Store) UsageDayUserRows added in v0.2.3

func (s *Store) UsageDayUserRows(userID, from, to string) ([]UsageDayUser, error)

UsageDayUserRows returns one identity's rows for the inclusive day range, oldest first.

func (*Store) User

func (s *Store) User(id string) (model.User, bool)

func (*Store) UserByUsername

func (s *Store) UserByUsername(username string) (model.User, bool)

UserByUsername looks up a user by username, case-insensitively. Usernames are effectively case-insensitive identifiers (and OIDC binds on a lowercased email), so password login and SSO resolve the same account regardless of the case used to provision it.

func (*Store) UserCount added in v0.2.0

func (s *Store) UserCount() int

func (*Store) Users added in v0.2.0

func (s *Store) Users() []model.User

Users returns all operator user records (for the user-management admin API). Callers must project to a secret-free view before serializing.

func (*Store) VpnUserPublicRecord added in v0.2.3

func (s *Store) VpnUserPublicRecord(id string) (VpnUserPublicRecord, bool)

func (*Store) VpnUserPublicRecords added in v0.2.3

func (s *Store) VpnUserPublicRecords() map[string]VpnUserPublicRecord

func (*Store) VpnUserRecord added in v0.2.3

func (s *Store) VpnUserRecord(id string) (VpnUserPublicRecord, VpnUserSecretRecord, bool)

func (*Store) VpnUserRecords added in v0.2.3

func (s *Store) VpnUserRecords() (map[string]VpnUserPublicRecord, map[string]VpnUserSecretRecord)

func (*Store) VpnUserSecretRecord added in v0.2.3

func (s *Store) VpnUserSecretRecord(id string) (VpnUserSecretRecord, bool)

func (*Store) VpnUserSecretRecords added in v0.2.3

func (s *Store) VpnUserSecretRecords() map[string]VpnUserSecretRecord

func (*Store) WebAuthnChallenge added in v0.2.0

func (s *Store) WebAuthnChallenge(id string) (auth.WebAuthnChallenge, bool)

WebAuthnChallenge returns an active (unused, unexpired) challenge by id.

func (*Store) WebAuthnCredential added in v0.2.0

func (s *Store) WebAuthnCredential(id string) (auth.WebAuthnCredential, bool)

WebAuthnCredential returns a passkey record by store id.

func (*Store) WebAuthnCredentialByCredentialID added in v0.2.0

func (s *Store) WebAuthnCredentialByCredentialID(credentialID []byte) (auth.WebAuthnCredential, bool)

WebAuthnCredentialByCredentialID looks a passkey up by its raw WebAuthn credential id (not the store id). Used on login to reject an unknown credential and during registration to reject a duplicate.

func (*Store) WebAuthnCredentialsByUser added in v0.2.0

func (s *Store) WebAuthnCredentialsByUser(userID string) []auth.WebAuthnCredential

WebAuthnCredentialsByUser returns a user's passkeys, oldest first for a stable management list.

type TaskDelivery added in v0.2.3

type TaskDelivery struct {
	Task            model.Task
	DurableResult   bool
	DurableProtocol string
}

TaskDelivery carries protocol metadata decided under the same store lock as the lease. DurableResult is true only for the exact gated NetGuard action.

type TaskExecContext added in v0.2.3

type TaskExecContext struct {
	TaskID string `json:"task_id"`
	NodeID string `json:"node_id"`
	// Sandbox is the agent's reported hardening level, e.g.
	// "linux-rlimit-process-group". Empty when the node had not reported one.
	Sandbox string `json:"sandbox,omitempty"`
	// NonRoot is the fact that matters most: an unprivileged agent reads nothing
	// for any probe that needs root, and a script that hides stderr reports that
	// as a clean exit.
	NonRoot      bool `json:"non_root,omitempty"`
	RootExec     bool `json:"root_exec,omitempty"`
	ExecDisabled bool `json:"exec_disabled,omitempty"`
	// ReportedAt is when the AGENT reported this posture; RecordedAt is when the
	// result arrived. A wide gap means the posture is stale relative to the run.
	ReportedAt time.Time `json:"reported_at,omitempty"`
	RecordedAt time.Time `json:"recorded_at"`
}

TaskExecContext is the agent's execution posture as of the moment a result was recorded, pinned so a later reader is not told about a configuration the run never had.

The live posture lives in Server.agentRuntime, which is an in-memory map: it is empty after a restart until each node reports again, and it moves whenever an agent is restarted with different flags. Neither is a good basis for reading a result from last week. What decides whether a run could see privileged state is what was true when it ran.

type TaskProgress added in v0.2.3

type TaskProgress struct {
	Stalled bool
	Targets map[string]TaskTargetProgress
}

TaskProgress is the per-target progress of a leased task plus the derived whole-task answer to "is anything still running this".

type TaskResultReceipt added in v0.2.3

type TaskResultReceipt struct {
	TaskID  string `json:"task_id"`
	NodeID  string `json:"node_id"`
	LeaseID string `json:"lease_id"`
	Digest  string `json:"digest"`
}

TaskResultReceipt is the compact, durable idempotency record retained for a task target even after bounded result history evicts the display payload.

type TaskTargetProgress added in v0.2.3

type TaskTargetProgress struct {
	Status          string
	Attempts        int
	LeaseAge        time.Duration
	LeaseLive       bool
	StalledReason   string
	Answered        bool
	AnsweredFailure bool
}

TaskTargetProgress is one target's share of a leased task, as the console needs to read it: what attempt this is, how long the current lease has been held, and why the store stopped re-leasing it, if it did.

type TaskTargetState added in v0.2.3

type TaskTargetState struct {
	TaskID string `json:"task_id"`
	NodeID string `json:"node_id"`
	// Attempts counts leases issued to this node for this task, the first
	// one included. A task leased before this record existed has one lease
	// and no record; the re-lease path treats that as one attempt already.
	Attempts int `json:"attempts"`
	// StalledReason is set when the store refuses to lease this target again.
	// Once set it is final: nothing clears it short of deleting the task.
	StalledReason string    `json:"stalled_reason,omitempty"`
	StalledAt     time.Time `json:"stalled_at,omitempty"`
}

TaskTargetState is what the control plane remembers about one target's lease history that the SDK's TaskLease cannot carry: how many times the task was handed to this node, and whether the store has given up on it.

It lives beside the task rather than on model.TaskLease because model is the SDK, pinned by sdk.ref and consumed by plugins; adding a field there is a coordinated two-repo release. Keyed by taskResultReceiptKey(task, node), persisted in the JSON state like TaskResultReceipts, and pruned with the task.

type UsageDayBytes added in v0.2.3

type UsageDayBytes struct {
	Uplink   int64 `json:"u"`
	Downlink int64 `json:"d"`
}

UsageDayBytes is one uplink/downlink pair.

type UsageDayLine added in v0.2.3

type UsageDayLine struct {
	LineHashID string                   `json:"h,omitempty"`
	Uplink     int64                    `json:"u"`
	Downlink   int64                    `json:"d"`
	Users      map[string]UsageDayBytes `json:"us,omitempty"`
}

UsageDayLine is one inbound's traffic for one day. Users carries the named user counters that landed on this line, keyed by VpnUser id, plus the users an inbound counter was attributed to when the line carried no named users.

type UsageDayNode added in v0.2.3

type UsageDayNode struct {
	NodeID string                  `json:"n"`
	Day    string                  `json:"day"`
	Lines  map[string]UsageDayLine `json:"l,omitempty"`
}

UsageDayNode is one node's day, lines keyed by the core's inbound tag. An inbound the server could not join to a line still has its row here with an empty LineHashID: the bytes are real egress and are never dropped.

type UsageDayUser added in v0.2.3

type UsageDayUser struct {
	UserID     string                      `json:"uid"`
	Day        string                      `json:"day"`
	Uplink     int64                       `json:"u"`
	Downlink   int64                       `json:"d"`
	ByLine     map[string]UsageDayUserLine `json:"bl,omitempty"`
	LastSeenAt time.Time                   `json:"at,omitempty"`
}

UsageDayUser is one identity's counted traffic for the day, split by the line it was counted on. Only attributions that count toward the quota land here (named, credential, binding); estimates are derived at read time.

type UsageDayUserLine added in v0.2.3

type UsageDayUserLine struct {
	Uplink     int64     `json:"u"`
	Downlink   int64     `json:"d"`
	LastSeenAt time.Time `json:"at,omitempty"`
}

UsageDayUserLine is one identity's counted traffic on one line for the day.

type VpnUserCredentialPublic added in v0.2.3

type VpnUserCredentialPublic struct {
	Protocol string `json:"protocol"`
	Flow     string `json:"flow,omitempty"`
	Method   string `json:"method,omitempty"`
	Security string `json:"security,omitempty"`
}

type VpnUserCredentialSecret added in v0.2.3

type VpnUserCredentialSecret struct {
	Protocol string `json:"protocol"`
	UUID     string `json:"uuid,omitempty"`
	Password string `json:"password,omitempty"`
}

type VpnUserLineBinding added in v0.2.3

type VpnUserLineBinding struct {
	LineHashID   string `json:"line_hash_id"`
	Enabled      bool   `json:"enabled"`
	FlowOverride string `json:"flow_override,omitempty"`
}

type VpnUserPublicRecord added in v0.2.3

type VpnUserPublicRecord struct {
	ID                     string                    `json:"id"`
	Email                  string                    `json:"email"`
	Name                   string                    `json:"name,omitempty"`
	Enabled                bool                      `json:"enabled"`
	Credentials            []VpnUserCredentialPublic `json:"credentials"`
	Bindings               []VpnUserLineBinding      `json:"bindings"`
	QuotaBytes             int64                     `json:"quota_bytes,omitempty"`
	QuotaPeriod            string                    `json:"quota_period,omitempty"`
	QuotaResetDay          int                       `json:"quota_reset_day,omitempty"`
	ExpiresAt              time.Time                 `json:"expires_at,omitempty"`
	Group                  string                    `json:"group,omitempty"`
	Comment                string                    `json:"comment,omitempty"`
	MigratedFromProxyUser  string                    `json:"migrated_from_proxy_user,omitempty"`
	CreatedAt              time.Time                 `json:"created_at"`
	UpdatedAt              time.Time                 `json:"updated_at"`
	SubscriptionGeneration uint64                    `json:"subscription_generation"`
}

VpnUserPublicRecord is the non-secret half of a vpn-core identity. It is a typed store collection so generic KV APIs cannot enumerate or overwrite it.

type VpnUserSecretRecord added in v0.2.3

type VpnUserSecretRecord struct {
	Credentials []VpnUserCredentialSecret `json:"credentials"`
	SubID       string                    `json:"sub_id,omitempty"`
}

VpnUserSecretRecord is the independently encrypted private half. It has no generic HTTP or plugin surface.

Jump to

Keyboard shortcuts

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