state

package
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package state owns Harbor's persistence floor: the single mandatory `StateStore` interface that every persistence-shaped subsystem (sessions, tasks, governance accumulators, planner checkpoints, memory snapshots, steering events) saves through.

The surface is generic by design (D-027): identity-scoped CRUD keyed on `(identity.Quadruple, Kind string, Bytes []byte)` with idempotency on a caller-supplied `EventID` (ULID), plus ONE explicitly-elevated maintenance scan (`ListKind` — RFC §6.11, D-207). Consuming subsystems land their typed wrappers at their own layer atop this interface — a `SessionRegistry.Save(s Session)` reduces to `StateStore.Save(StateRecord{Identity: s.Identity, Kind: "session.lifecycle", Bytes: marshal(s)})`.

Three V1 drivers ship to the §9 persistence triad (in-memory, SQLite, Postgres). Phase 07 ships only the in-memory reference; SQLite (Phase 15) and Postgres (Phase 16) inherit the conformancetest suite verbatim.

Identity is mandatory at the API boundary. Any `Quadruple` whose tenant / user / session is empty is rejected with `ErrIdentityRequired`. Empty `RunID` is acceptable for state that is session-scoped rather than run-scoped.

Audit redaction is upstream of `Save`. The store stores opaque `Bytes`; mixing redaction into the persistence layer would couple a leaf package to the audit subsystem and split responsibility (D-020).

Index

Constants

View Source
const DefaultDriver = "inmem"

DefaultDriver is the Phase 07 production driver name. Phase 15 (SQLite) and Phase 16 (Postgres) register additional names; Open switches on cfg.Driver.

Variables

View Source
var (
	// ErrNotFound — Load / LoadByEventID was called for a key that
	// has no record. Wraps drivers' own not-found shapes.
	ErrNotFound = errors.New("state: record not found")
	// ErrIdempotencyConflict — Save with a previously-seen EventID
	// but different Bytes (or routed to a different key). Tells the
	// caller a retry policy bug exists upstream.
	ErrIdempotencyConflict = errors.New("state: idempotency conflict")
	// ErrIdentityRequired — Save / Load / Delete called with a
	// Quadruple missing one of (tenant, user, session). Empty RunID
	// is allowed for session-scoped state.
	ErrIdentityRequired = errors.New("state: identity triple incomplete")
	// ErrStoreClosed — Save / Load / Delete called after Close.
	ErrStoreClosed = errors.New("state: store is closed")
	// ErrInvalidRecord — record fails structural validation
	// (empty Kind, empty EventID).
	ErrInvalidRecord = errors.New("state: invalid record")
	// ErrUnknownDriver — Open was asked for a driver name no
	// registered factory handles.
	ErrUnknownDriver = errors.New("state: unknown driver")
	// ErrMaintenanceScopeRequired — ListKind was called without the
	// explicit ListScope.MaintenanceScoped claim. The cross-identity
	// scan fails closed (CLAUDE.md §6).
	ErrMaintenanceScopeRequired = errors.New("state: ListKind requires an explicit maintenance scope claim")
)

Sentinel errors. Callers compare via errors.Is.

Functions

func Register

func Register(name string, factory Factory)

Register installs a driver factory under name. Drivers self-register from their package init(); cmd/harbor blank-imports the production driver to trigger registration. Per AGENTS.md §4.4.

Re-registering the same name panics — the registration model is write-once-at-init and a duplicate signals a build mis-configuration.

func RegisteredDrivers

func RegisteredDrivers() []string

RegisteredDrivers returns a sorted list of driver names. Useful for boot-log output and for surfacing in error messages.

func ValidateIdentity

func ValidateIdentity(q identity.Quadruple) error

ValidateIdentity checks that the triple is fully specified. Empty RunID is accepted (session-scoped state). Returns wrapped ErrIdentityRequired when any of tenant/user/session is empty.

func ValidateListKind added in v1.3.0

func ValidateListKind(scope ListScope, kindPrefix string) error

ValidateListKind checks ListKind's fail-closed preconditions shared by every driver: the explicit maintenance scope claim must be set (ErrMaintenanceScopeRequired) and the kind prefix must be non-empty (ErrInvalidRecord — a whole-store dump is never a valid maintenance scan).

func ValidateRecord

func ValidateRecord(r StateRecord) error

ValidateRecord checks structural invariants Save needs before touching driver storage: identity triple present, EventID non-empty, Kind non-empty.

func WithStore

func WithStore(ctx context.Context, store StateStore) context.Context

WithStore attaches store to ctx for downstream handlers.

Types

type EventID

type EventID string

EventID is a caller-supplied ULID used as the canonical idempotency key for `Save`. ULID gives us monotonic, lexicographically sortable IDs that work as both primary keys and secondary indices.

Callers are free to construct the value externally; `NewEventID` is provided as a convenience that uses crypto-strong entropy.

func NewEventID

func NewEventID() EventID

NewEventID generates a fresh ULID-shaped EventID using crypto/rand. Implementations may use any ULID source; this helper exists so callers don't need a separate dependency just for generating idempotency keys.

type Factory

type Factory func(config.StateConfig) (StateStore, error)

Factory builds a StateStore from a StateConfig. Drivers expose one Factory each via init() → Register.

type ListScope added in v1.3.0

type ListScope struct {
	// MaintenanceScoped asserts the caller is a runtime maintenance
	// loop acting on each returned record under that record's own
	// identity. False (the zero value) is rejected with
	// ErrMaintenanceScopeRequired.
	MaintenanceScoped bool
}

ListScope is the explicit scope claim ListKind requires. The zero value fails closed: a caller must set MaintenanceScoped to assert it understands the call crosses identity boundaries (CLAUDE.md §6 rule 5; the §13 elevated-scope-claim rule applied to the persistence floor).

type StateRecord

type StateRecord struct {
	ID        EventID
	Identity  identity.Quadruple
	Kind      string
	Version   int
	Bytes     []byte
	UpdatedAt time.Time
}

StateRecord is the unit of persistence.

`Bytes` is opaque to the store — callers serialize their domain types and run them through audit redaction upstream of `Save`. The store does not interpret payloads or re-redact.

`Kind` is a free-form caller-namespaced key (e.g. "session.lifecycle", "task.checkpoint", "governance.cost"). Two records with the same (Quadruple, Kind) are treated as a single keyed slot — `Save` overwrites; `Load` returns the latest.

`Version` is a hint for optimistic-concurrency at the typed-wrapper layer (e.g. `SessionRegistry` MAY refuse to apply an update whose Version is stale). The StateStore itself does NOT enforce CAS — it stores and returns the int.

`UpdatedAt` is set by the store at `Save` time when zero; callers MAY override (useful for tests with controllable clocks).

type StateStore

type StateStore interface {
	// Save persists a record. Idempotent on `EventID`:
	//
	//   - Same EventID + byte-equal Bytes: no-op (no error, no
	//     duplicate write).
	//   - Same EventID + different Bytes: ErrIdempotencyConflict.
	//
	// If a record already exists at (Identity, Kind) but with a
	// different EventID, Save overwrites it (the new EventID becomes
	// the active one for that slot; the previous EventID is no
	// longer LoadByEventID-resolvable).
	Save(ctx context.Context, r StateRecord) error

	// Load returns the record at (id, kind). Returns ErrNotFound
	// (wrapped) when no record exists for that key.
	Load(ctx context.Context, id identity.Quadruple, kind string) (StateRecord, error)

	// LoadByEventID returns the record whose ID matches eventID.
	// Useful for replaying a specific event by its idempotency key.
	// Returns ErrNotFound (wrapped) when not present.
	LoadByEventID(ctx context.Context, eventID EventID) (StateRecord, error)

	// Delete removes the record at (id, kind). Returns nil when the
	// record is absent (idempotent), wrapped error on store failure.
	Delete(ctx context.Context, id identity.Quadruple, kind string) error

	// ListKind enumerates every record whose Kind starts with
	// kindPrefix — the store's ONE maintenance-scan surface
	// (RFC §6.11, D-207). Unlike every other method, the scan crosses
	// identity boundaries: it exists so runtime maintenance loops (the
	// pause sweeper's crash-orphan rescan is the first consumer) can
	// find records whose identities the process has never seen. That
	// elevation is explicit and fail-closed:
	//
	//   - scope.MaintenanceScoped MUST be true, or the call fails with
	//     ErrMaintenanceScopeRequired (CLAUDE.md §6 rule 5 / §13 "no
	//     cross-session queries without an explicit elevated scope
	//     claim"). There is no identity-scoped mode — identity-scoped
	//     reads stay on Load / LoadByEventID.
	//   - kindPrefix MUST be non-empty (ErrInvalidRecord) — a
	//     whole-store dump is never a valid maintenance scan.
	//   - Callers MUST act on each returned record under that record's
	//     OWN identity (every record carries its Quadruple); ListKind
	//     grants visibility for the scan, never a widened mutation
	//     scope.
	//
	// kindPrefix matches literally (no wildcard interpretation — SQL
	// drivers escape LIKE metacharacters). Result order is
	// unspecified; an empty result is ([]StateRecord{} or nil, nil),
	// never an error.
	ListKind(ctx context.Context, scope ListScope, kindPrefix string) ([]StateRecord, error)

	// Close releases driver resources. Subsequent calls return
	// ErrStoreClosed (wrapped). Implementations MUST honour ctx
	// during long teardowns.
	Close(ctx context.Context) error
}

StateStore is Harbor's persistence interface — single mandatory surface, no `Supports*` capability ceremony (AGENTS.md §4.4 + §9).

Implementations MUST be safe for concurrent use by N goroutines against a single shared instance (D-025). Mutable state must be guarded; per-call state lives in `ctx`, never on the driver.

func From

func From(ctx context.Context) (StateStore, bool)

From returns the StateStore in ctx and a presence bool. Use when absence is recoverable.

func MustFrom

func MustFrom(ctx context.Context) StateStore

MustFrom returns the StateStore in ctx; panics with ErrStoreClosed (used as the sentinel for "no store configured") when none is present. Use in handler/runtime paths where a store is mandatory.

func Open

Open returns the StateStore built by the factory whose name matches cfg.Driver (defaults to DefaultDriver when cfg.Driver is empty).

func OpenDriver

func OpenDriver(name string, cfg config.StateConfig) (StateStore, error)

OpenDriver opens a specific driver by name; useful for tests that want to exercise the registry against a non-default driver.

Directories

Path Synopsis
Package conformancetest exposes the canonical correctness suite every state.StateStore driver must pass.
Package conformancetest exposes the canonical correctness suite every state.StateStore driver must pass.
drivers
inmem
Package inmem is Harbor's V1 in-memory StateStore driver.
Package inmem is Harbor's V1 in-memory StateStore driver.
postgres
Package postgres is Harbor's V1 Postgres-backed StateStore driver.
Package postgres is Harbor's V1 Postgres-backed StateStore driver.
sqlite
Package sqlite is Harbor's SQLite-backed `state.StateStore` driver (Phase 15).
Package sqlite is Harbor's SQLite-backed `state.StateStore` driver (Phase 15).

Jump to

Keyboard shortcuts

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