admin

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package admin holds the control plane's outbound ports (multi-tenancy.md §5).

The methods on the tenant row itself deliberately take no tenant parameter, the same shape as every other repository: the control plane opens an ordinary bounded transaction per tenant it touches, and row level security holds even here. The two exceptions say so in their comments - the enumerator, which is the one legitimate listing of tenants (multi-tenancy.md §2.1), and the instance journal, which belongs to no tenant at all.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Automations

type Automations interface {
	// DisableAll switches the tenant's enabled rules off and reports how many.
	DisableAll(ctx context.Context, now time.Time) (int, error)
}

Automations is the one switch the deletion request throws (§5, "automations disabled"): every enabled rule of the transaction's tenant, off in one stroke, visibly - the rows keep existing with enabled = false.

type Census

type Census struct {
	WorkspacesActive          int64
	WorkspacesSuspended       int64
	WorkspacesPendingDeletion int64
	AccountsActive            int64
	AccountsTotal             int64
	// WorkspacesServiceOnly are workspaces holding no person, only service accounts: not customers,
	// so the three workspace counts above leave them out (UC-INS-12 check 5).
	WorkspacesServiceOnly int64
}

Census is the installation at a glance (ADR-0070 §5): counts, states and limits, never rows. The tenant boundary is a database policy rather than a role, and the dashboard does not go around it - what it reads is five integers, through a function that can answer nothing else.

type Entitlements

type Entitlements interface {
	LeftOut(ctx context.Context) (entitlement.LeftOut, error)
}

Entitlements reads what a workspace's plan leaves out (multi-tenancy.md §4.2).

The workspace is the one the transaction is bound to, so the reader joins whatever transaction its caller already runs in: the engine's, the dispatcher's, a use case's. A workspace on no plan reads an empty set - every feature included.

type Footprint

type Footprint struct {
	Items        int64
	Containers   int64
	MediaObjects int64
	MediaBytes   int64
	OutboxEvents int64
	AuditEntries int64
}

Footprint is the §5 stores, counted: what the evidence entry records before the fall, and what the acceptance proof counts to zero afterwards. Search rides inside the items (their vectors are columns of the row), so the item count is the search count.

type Installation

type Installation interface {
	Census(ctx context.Context) (Census, error)
}

Installation answers the census.

type InstanceEvent

type InstanceEvent struct {
	ID         shared.ID
	OccurredAt time.Time
	// Action is the act, `tenant.provisioned` through `tenant.hard_deleted`.
	Action string
	// TenantID and TenantSlug name the workspace the act was about. Bare values, no reference:
	// the row they name is usually gone, which is the reason the journal exists.
	TenantID   shared.ID
	TenantSlug string
	// ActorLabel is the acting operator's own label - the installation's administrator, never a
	// tenant's person.
	ActorLabel string
	// Details carries the counts and moments of the act, as the evidence entry records them.
	Details map[string]any
}

InstanceEvent is one row of the installation's own journal: evidence of a control-plane act, where a per-tenant trail cannot hold it (audit.md §6). Identifiers, a slug, counts - never content, and never an end user's name.

type Journal

type Journal interface {
	// Record writes one entry. The table has no row-level-security policy, so this works inside
	// whatever transaction the act runs in - including the one that ends the tenant it names.
	Record(ctx context.Context, entry InstanceEvent) error

	// Page walks the journal backwards from `cursor`, empty for the first page. The entries and
	// whether there is another page behind them.
	Page(ctx context.Context, cursor string, size int) ([]InstanceEvent, PageInfo, error)
}

Journal is the instance's own record. Append-only by grant, and read by one screen: the dashboard's journal, newest first, one page at a time.

type PageInfo

type PageInfo struct {
	NextCursor string
	HasMore    bool
}

PageInfo is the walk's state, as every other listing in this product reports it.

type Plans

type Plans interface {
	// List answers every plan not removed, oldest first, with what each decides.
	List(ctx context.Context) ([]plan.Plan, error)

	// Find answers one plan not removed by its short name, or an error wrapping
	// shared.ErrNotFound.
	Find(ctx context.Context, name string) (plan.Plan, error)

	// Hold answers the identifier of a plan not removed and locks it against a removal until the
	// transaction ends, or an error wrapping shared.ErrNotFound. A move holds the plan it moves onto.
	Hold(ctx context.Context, name string) (shared.ID, error)

	// Ever reports whether any plan was ever created, removed ones included.
	Ever(ctx context.Context) (bool, error)

	// Workspaces answers how many workspaces are on each plan, by short name. It reads through
	// the control plane's enumerator, so it runs under the installation's read scope.
	Workspaces(ctx context.Context) (map[string]int64, error)

	// Insert writes a new plan with its values. A short name taken, also by a removed plan, is a
	// conflict error with code `admin.plan_name_taken`.
	Insert(ctx context.Context, created plan.Plan, by shared.ID) error

	// Replace writes a plan's display name and its values whole, raising its version. `expected`
	// nil writes whatever stands; otherwise false means the stored version is another one, or the
	// plan was removed - somebody acted first.
	Replace(ctx context.Context, changed plan.Plan, expected *int, by shared.ID, now time.Time) (bool, error)

	// Remove ends a plan unless a workspace is on it; its short name stays taken.
	Remove(ctx context.Context, id shared.ID, by shared.ID, now time.Time) (RemoveOutcome, error)
}

Plans are the installation's plans (multi-tenancy.md §4.2). No method takes a tenant: `plan` carries no policy, like `instance_setting`, and what bounds a write is the control plane's use case behind `admin:tenants` and the register.

type PlatformEvent

type PlatformEvent struct {
	Number   int64
	Envelope event.Envelope
}

PlatformEvent is one installation event as it is kept: the envelope and its number, which is the feed's order.

type PlatformEvents

type PlatformEvents interface {
	// Append writes the event in the running transaction, numbered after every event whose
	// transaction committed before this one does: it takes the installation's numbering lock,
	// held until the transaction ends. A non-empty dedupe key writes the event once however often
	// it is appended, and Append reports whether this call wrote it.
	Append(ctx context.Context, envelope event.Envelope, dedupeKey string) (bool, error)

	// After reads up to `limit` events numbered after `after`, in their order.
	After(ctx context.Context, after int64, limit int) ([]PlatformEvent, error)

	// ForgetReference removes the platform's reference from the kept events of the transaction's
	// own workspace, as its purge removes it from the workspace.
	ForgetReference(ctx context.Context) error

	// Sweep removes up to `batch` events that occurred before `cutoff`, and answers how many.
	Sweep(ctx context.Context, cutoff time.Time, batch int) (int, error)
}

PlatformEvents are the installation's events for a purchase platform (multi-tenancy.md §4.3). Like the journal they belong to no tenant and carry no policy: written beside a workspace's change, in that workspace's transaction, and read under the installation's scope.

type PlatformPosition

type PlatformPosition struct {
	Number int64
	At     time.Time
}

PlatformPosition is where a reader of the installation's events stands: after the event numbered Number, having read up to the moment At. The moment is what decides whether events after the position may already have been swept.

type Purge

type Purge interface {
	// Footprint counts the stores before the fall.
	Footprint(ctx context.Context) (Footprint, error)

	// StorageKeys pages through the tenant's object keys, for the store-first byte deletion -
	// ReconcileMedia's order: an orphaned byte object is reclaimable, an orphaned row lies.
	StorageKeys(ctx context.Context, after string, batch int) ([]string, error)

	// DropStructure fells the structure in dependency order - collections, hubs, media rows,
	// automation rules, restore runs, retention rules - because a bare cascade would trip its
	// own RESTRICT edges. It reports how many rows fell.
	DropStructure(ctx context.Context) (int64, error)

	// DeleteOutbox removes one batch of the tenant's outbox and reports how many; the caller
	// loops until zero. No foreign key reaches the outbox.
	DeleteOutbox(ctx context.Context, batch int) (int, error)

	// DeleteIdempotency clears the replay guards; no foreign key reaches them either.
	DeleteIdempotency(ctx context.Context) (int, error)

	// DeleteJobs removes the tenant's queue rows except the one running this purge - the job
	// table has no policy, so the adapter writes the transaction's own tenant as an explicit
	// predicate.
	DeleteJobs(ctx context.Context, keep shared.ID) (int, error)

	// PurgeTrail removes the tenant's audit trail through the one narrow SECURITY DEFINER act
	// migration 0067 reasons through, aimed by the transaction's own scope, and reports how
	// many entries went.
	PurgeTrail(ctx context.Context) (int64, error)

	// HardDelete removes the tenant row itself; the cascade takes everything still standing.
	// The guard re-reads the two facts the grace could have changed - false means the state
	// moved, and the caller rolls the whole act back.
	HardDelete(ctx context.Context, now time.Time) (bool, error)

	// MarkStarted writes the purge's mark in the tenant row before the first byte goes, so that a
	// cancel can no longer land. False means the workspace is not pending or its deadline lies
	// ahead - a cancel came first, and nothing may be removed.
	MarkStarted(ctx context.Context, now time.Time) (bool, error)
}

Purge is the hard delete's surface (§5's final phase). Every method except the two that say otherwise runs inside the dying tenant's own bounded transaction.

type RemoveOutcome

type RemoveOutcome string

RemoveOutcome is what removing a plan came to.

const (
	PlanRemoved  RemoveOutcome = "REMOVED"
	PlanInUse    RemoveOutcome = "IN_USE"
	PlanNotFound RemoveOutcome = "NOT_FOUND"
)

type TenantFinder

type TenantFinder interface {
	// Query answers one page of the matching workspaces, oldest first. A cursor this server did not
	// produce is a validation error with code `shared.cursor_invalid`.
	Query(ctx context.Context, query TenantQuery) ([]TenantRecord, PageInfo, error)
}

TenantFinder finds workspaces across the installation (multi-tenancy.md §4.3), through the one narrow door that filters and pages across tenant_self.

type TenantQuery

type TenantQuery struct {
	ID          shared.ID
	Slug        string
	Reference   string
	Plan        string
	WithoutPlan bool
	Status      identity.TenantStatus
	Cursor      string
	Size        int
}

TenantQuery is the control plane's filters over the workspaces; an empty field filters nothing.

type TenantRecord

type TenantRecord struct {
	ID              shared.ID
	Slug            string
	DisplayName     string
	Status          identity.TenantStatus
	DefaultLocale   string
	DefaultTimeZone string
	CreatedAt       time.Time
	// PurgeAfter is set while a deletion request stands: when the grace runs out. Zero otherwise.
	PurgeAfter time.Time
	// PasswordOpening is an operator's opening of the password, as the row holds it (ADR-0078 §3).
	// Whether it still stands is the reader's to judge against its clock.
	PasswordOpening identity.PasswordOpening
	// LegalHold says a legal hold is in force in the workspace, so it is not deleted
	// (data-protection.md §5). A state only: which hold, on what and why stay the workspace's.
	LegalHold bool
	// StatusBeforeDeletion is where the workspace stood when its deletion was requested, and where
	// a cancel returns it. Empty on a row requested before the column existed.
	StatusBeforeDeletion identity.TenantStatus
	// PurgeStartedAt is when the grace job began removing the workspace. Zero until then; from then
	// on a cancel is refused, because the bytes are already going.
	PurgeStartedAt time.Time
	Version        int
	// PlanName is the short name of the workspace's plan, empty for none (multi-tenancy.md §4.2).
	PlanName string
	// ExternalReference is a purchase platform's own reference for the workspace, empty for none
	// (multi-tenancy.md §4.3). It may name a customer: it goes into no log, metric or journal.
	ExternalReference string
}

TenantRecord is one workspace as the control plane sees it: the row, no settings document - what the lifecycle needs and the listing answers, nothing a tenant configures.

type Tenants

type Tenants interface {
	// List answers every workspace, oldest first. It reads through the installation-scoped
	// SECURITY DEFINER enumerator migration 0067 pins down - the one legitimate place tenants
	// are enumerated (multi-tenancy.md §2.1).
	List(ctx context.Context) ([]TenantRecord, error)

	// Insert writes the row, inside the new tenant's own scope: the first write a tenant ever
	// sees is already bounded to it. A taken slug is a conflict error with code
	// `admin.slug_taken`.
	Insert(ctx context.Context, record TenantRecord) error

	// Find answers the row of the transaction's own tenant, or an error wrapping
	// shared.ErrNotFound.
	Find(ctx context.Context) (TenantRecord, error)

	// SetStatus moves one lifecycle edge on the transaction's own tenant. False means the
	// origin status did not stand - the state moved under the caller, who reports the conflict
	// rather than overwriting it.
	SetStatus(ctx context.Context, from, to identity.TenantStatus, now time.Time) (bool, error)

	// RequestDeletion moves either living status to PENDING_DELETION and stamps the grace
	// deadline - the one edge with two origins (§5). False means the workspace was already
	// leaving, or gone.
	RequestDeletion(ctx context.Context, purgeAfter, now time.Time) (bool, error)

	// CancelDeletion moves a pending workspace back to the status it was requested from, or to
	// SUSPENDED where none was kept, and clears the deadline. It answers the status written; false
	// means the workspace was not pending, or its purge had begun - the statement guards both.
	CancelDeletion(ctx context.Context, now time.Time) (identity.TenantStatus, bool, error)

	// OpenPassword writes an operator's opening of the password on the transaction's own tenant,
	// replacing one that stands (ADR-0078 §3). False means the workspace is not active - suspended,
	// leaving, or gone.
	OpenPassword(ctx context.Context, opening identity.PasswordOpening, now time.Time) (bool, error)

	// SetReference sets a platform's reference on the transaction's own tenant, or clears it where
	// reference is empty, raising its version. False means the version moved since `version` was
	// read, or the purge has begun. A reference another workspace holds is a conflict error with code
	// `admin.reference_taken`, carrying neither the value nor the database's detail.
	SetReference(ctx context.Context, reference string, version int, now time.Time) (bool, error)

	// SetPlan puts the transaction's own tenant on a plan, or none where planID is zero, raising its
	// version. False means the version moved since `version` was read, or the purge has begun.
	SetPlan(ctx context.Context, planID shared.ID, version int, now time.Time) (bool, error)

	// ClosePassword ends the opening on the transaction's own tenant: whatever stands where `due` is
	// zero, and only one whose end has come by `due` otherwise - so a close at the old end leaves a
	// later opening alone. False means there was none to end.
	ClosePassword(ctx context.Context, due, now time.Time) (bool, error)
}

Tenants is the lifecycle of the tenant row.

Jump to

Keyboard shortcuts

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