controlevent

package
v0.2.17 Latest Latest
Warning

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

Go to latest
Published: Jun 27, 2026 License: MPL-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package controlevent defines the control-plane event contract: the notification a control plane appends and a fleet chassis consumes.

See internal docs/todo-architecture-saas-fleet.md §3.1. An event is a semantic invalidation/version record — NEVER the payload itself. It carries identity, a content-addressed pointer (ArtifactRef), an integrity seal (Checksum) and the fleet position (ControlVersion). The bytes live in the artifact store, not in the log.

This package is the shared event contract: the chassis consumes events; a control plane produces them. It is intentionally dependency-free so both sides can depend on it.

Index

Constants

View Source
const (
	// ControlModelVersion is the version of the event/contract shape.
	ControlModelVersion = 1
	// CacheSchemaVersion is the version of the materialised local cache
	// shape an artifact restores into.
	CacheSchemaVersion = 1
)

Model/cache versions. A node rejects an artifact (or event) whose declared versions it cannot interpret (see snapshot.Verify). Bump deliberately when the on-the-wire contract or the materialised cache shape changes incompatibly.

View Source
const (
	TypeStackActivated     = "stack.activated"
	TypeTenantCreated      = "tenant.created"
	TypeHostnameBound      = "hostname.bound"
	TypeHostnameVerified   = "hostname.verified"
	TypeHostnameRevoked    = "hostname.revoked"
	TypeActorChanged       = "actor.changed"
	TypeKeyChanged         = "key.changed"
	TypeMembershipChanged  = "membership.changed"
	TypeEntitlementUpdated = "entitlement.updated"
	TypeSystemOpstack      = "system.opstack.updated"
	// TypeDNSZoneUpserted carries a dns_zones row (RowsArtifact, op=upsert;
	// revocation is an upsert with revoked_at set). Lets a data-plane node hold
	// the delegated-zone state so it re-derives `<label>.<origin>` routing hosts
	// itself and (with the dns personality) can serve the zone — see
	// internal docs/todo-dns-authority.md §9 fleet note.
	TypeDNSZoneUpserted = "dns.zone.upserted"
	// TypeCronSettingsUpserted carries a cron_settings row (RowsArtifact,
	// op=upsert; clearing a timezone is an upsert with timezone=”). Lets every
	// node localize a tenant's @cron.* wall-clock fields consistently.
	TypeCronSettingsUpserted = "cron.settings.upserted"
	// TypeSecretChanged carries a tenant_secrets OR tenant_secret_versions row
	// (RowsArtifact, op=upsert). A create/rotate emits two of these — the
	// version row first, then the parent — so a consumer never briefly sees a
	// parent pointing at an absent version. Decryptable fleet-wide only when the
	// master key is shared (TXCO_SECRET_MASTER_KEY_B64). The blob columns travel
	// base64-wrapped ({"$b64":…}); see controlapply.coerce.
	TypeSecretChanged = "secret.changed"
	// TypeSecretRevoked carries a tenant_secrets row with revoked_at set
	// (RowsArtifact, op=upsert — the consumer's INSERT OR REPLACE flips it
	// inactive, mirroring dns.zone revocation). No version row travels.
	TypeSecretRevoked = "secret.revoked"
)

Event types. These are the existing admin-API mutations, externalised.

Variables

View Source
var ErrInvalid = errors.New("controlevent: invalid event")

ErrInvalid wraps every Validate failure.

Functions

func CompatibleCacheSchema

func CompatibleCacheSchema(cacheSchemaVersion int) bool

CompatibleCacheSchema reports whether this chassis can load an artifact declaring the given cache-schema version.

func CompatibleModel

func CompatibleModel(modelVersion int) bool

CompatibleModel reports whether this chassis can interpret an artifact/event declaring the given control-model version. Same major line only; an older binary must refuse a newer model rather than guess.

Types

type Event

type Event struct {
	ID             int64  `json:"id"`
	EventID        string `json:"event_id"`
	Type           string `json:"type"`
	TenantID       string `json:"tenant_id,omitempty"`
	StackID        string `json:"stack_id,omitempty"`
	Version        int64  `json:"version,omitempty"`
	BaseVersion    int64  `json:"base_version,omitempty"`
	ArtifactRef    string `json:"artifact_ref,omitempty"`
	Checksum       string `json:"checksum,omitempty"`
	ControlVersion uint64 `json:"control_version"`
	CreatedAt      string `json:"created_at,omitempty"`
}

Event is the control-plane event contract.

Field notes (deliberate, see plan):

  • EventID is the producer-assigned semantic identity (UUID; UUIDv7 via chassis/hxid is the conventional choice). It rides the event through retries and replays so consumers can recognise a duplicate even when broker-side dedup (e.g. JetStream Nats-Msg-Id) has expired. Required; an empty EventID fails Validate.
  • Version is the *stack* version (stack_versions.version_number).
  • BaseVersion is the producer's view of the prior value of the mutated resource — e.g. the active_version it saw before flipping it for a stack.activated event. Optional in v1; not enforced by the applier. Carried so a later revision can layer CAS-style optimistic concurrency on top without a wire-format change.
  • ControlVersion is the global monotonic *fleet cursor* — the resume position. Producer-side it is zero (the backend's broker stamps it on publish — JetStream stream_sequence in the NATS overlay; sequential ints in the file backend). Consumer-side Validate requires > 0.
  • ArtifactRef points at the event's artifact in the artifact store. It is "the artifact for THIS event", not necessarily a full bootstrap SQLite snapshot — hence ArtifactRef, not "snapshot_ref".
  • There is no "active_version": it was ambiguous against Version and is omitted until/unless it earns a distinct meaning.

func (Event) Validate

func (e Event) Validate() error

Validate checks the structural invariants every consumer relies on. It is deliberately strict: a malformed system event must fail loudly, never silently misroute.

Validate is consumer-side: it runs after the carrier has stamped ControlVersion (broker-assigned). Producers do not need to call it before queueing an outbox row — the Sink stamps ControlVersion at publish time, and the next applier on any chassis re-runs Validate.

type RowsArtifact

type RowsArtifact struct {
	DB    string           `json:"db"`    // "runtime" | "auth"
	Table string           `json:"table"` // must be whitelisted
	Op    string           `json:"op"`    // "upsert" | "delete"
	PK    []string         `json:"pk"`    // primary-key columns (delete)
	Rows  []map[string]any `json:"rows"`
}

RowsArtifact is the payload for the generic row event types (tenant.created, hostname.*, membership.changed, entitlement.updated, and the auth.* types). It carries authoritative rows for one table and how to apply them. DB selects the target database ("runtime" or "auth"); the table is checked against a hard whitelist on the consumer side before any SQL runs.

type StackActivatedArtifact

type StackActivatedArtifact struct {
	TenantID string              `json:"tenant_id"`
	Stack    string              `json:"stack"`
	Version  int64               `json:"version"`
	Files    []StackArtifactFile `json:"files"`
}

StackActivatedArtifact is the payload for a TypeStackActivated event: the full file set of the activated stack version. The node upserts these into stack_versions/stack_files then materialises them into ops.

type StackArtifactFile

type StackArtifactFile struct {
	Path        string `json:"path"`
	Content     string `json:"content,omitempty"`
	ContentHash string `json:"content_hash,omitempty"`
}

StackArtifactFile is one file of a StackActivatedArtifact. Path is "<scope>/<name>.txcl", the well-known "mock-request.json" / "mock-response.json", or a "FILES/**" static asset.

Rule/fixture files carry their bytes inline in Content. FILES/** static assets instead carry only ContentHash (the sha256 fingerprint): their bytes live in the shared content-addressed store (filecas), so the event stays small and data-plane nodes never inline tenant file bytes into the in-memory runtime DB. Both fields are omitempty so old (all-inline) and new (fingerprint) events parse in either direction.

Jump to

Keyboard shortcuts

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