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 ¶
- type Automations
- type Census
- type Entitlements
- type Footprint
- type Installation
- type InstanceEvent
- type Journal
- type JournalFilter
- type PageInfo
- type Plans
- type PlatformDeliveries
- type PlatformEvent
- type PlatformEvents
- type PlatformPosition
- type PlatformSecret
- type PlatformSubscriptions
- type Purge
- type RemoveOutcome
- type StoredPlatformSubscription
- type TenantFinder
- type TenantQuery
- type TenantRecord
- type Tenants
- type UsageLedger
- type UsageQuery
- type UsageRow
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 ¶
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, narrowed by
// `filter`. The entries and whether there is another page behind them.
Page(ctx context.Context, filter JournalFilter, 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 JournalFilter ¶
type JournalFilter struct {
// ActionPrefix keeps the entries whose action starts with it: `tenant.` or `tenant.suspended`.
ActionPrefix string
// TenantSlug keeps the entries about the workspace of this address, as the journal kept it.
TenantSlug string
}
JournalFilter narrows the journal; an empty field narrows nothing.
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 PlatformDeliveries ¶
type PlatformDeliveries interface {
// Insert queues a delivery, and reports whether it was new: a repeated fan-out writes nothing.
Insert(ctx context.Context, delivery integration.PlatformDelivery) (bool, error)
// Find answers one; ErrNotFound when there is none.
Find(ctx context.Context, id shared.ID) (integration.PlatformDelivery, error)
// Page answers a subscription's deliveries newest first, before `before` when it is set, of
// one status when it is set.
Page(ctx context.Context, subscriptionID shared.ID, status string, before shared.ID, size int) ([]integration.PlatformDelivery, error)
// Record writes one attempt's outcome, only while the delivery is still at the attempt before:
// a repeated job writes nothing, and the answer says so.
Record(ctx context.Context, delivery integration.PlatformDelivery) (bool, error)
// Requeue returns up to `batch` of a subscription's dead letters to pending, and answers which.
Requeue(ctx context.Context, subscriptionID shared.ID, at time.Time, batch int) ([]shared.ID, error)
// DeadLetters counts the installation's dead letters waiting.
DeadLetters(ctx context.Context) (int64, error)
// Sweep removes up to `batch` settled deliveries untouched since `cutoff`, and answers how many.
Sweep(ctx context.Context, cutoff time.Time, batch int) (int, error)
}
PlatformDeliveries are the deliveries of the installation's subscriptions: one per event and subscription, across its attempts.
type PlatformEvent ¶
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 ¶
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 PlatformSecret ¶
PlatformSecret is a subscription's signing secret as it is stored: sealed under the installation's key, never in clear.
type PlatformSubscriptions ¶
type PlatformSubscriptions interface {
Insert(ctx context.Context, subscription integration.PlatformSubscription, secret PlatformSecret) error
// List answers every subscription, oldest first, with its dead letters counted.
List(ctx context.Context) ([]integration.PlatformSubscription, error)
// Find answers one with its sealed secret; ErrNotFound when there is none.
Find(ctx context.Context, id shared.ID) (StoredPlatformSubscription, error)
// Update writes the target and the state, guarded on the version read.
Update(ctx context.Context, subscription integration.PlatformSubscription, expectedVersion int, at time.Time) (bool, error)
Rotate(ctx context.Context, id shared.ID, secret PlatformSecret, at time.Time) (bool, error)
Delete(ctx context.Context, id shared.ID) (bool, error)
// Active answers the subscriptions an event is sent to.
Active(ctx context.Context) ([]shared.ID, error)
// Outcome records the last attempt for the screen: the moment of an arrival, or a failure's code.
Outcome(ctx context.Context, id shared.ID, deliveredAt time.Time, lastError string) error
// MarkFromFile records that an enforcing instance file decides the subscription.
MarkFromFile(ctx context.Context, id shared.ID) error
}
PlatformSubscriptions are the installation's subscriptions for a purchase platform (multi-tenancy.md §4.3). They belong to no tenant and are read and written under the installation's or the system's scope.
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)
// ArchiveUsage moves the workspace's daily counts to the installation's ledger, keyed by the
// bare id with no name and no reference, before the row goes and the cascade takes them
// (multi-tenancy.md §4.3). It reports how many counts moved.
ArchiveUsage(ctx context.Context) (int64, 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 StoredPlatformSubscription ¶
type StoredPlatformSubscription struct {
Subscription integration.PlatformSubscription
Secret PlatformSecret
}
StoredPlatformSubscription is a subscription with its sealed secret, for the one reader that opens it - the delivery.
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.
type UsageLedger ¶
type UsageLedger interface {
Read(ctx context.Context, query UsageQuery) ([]UsageRow, error)
// CursorAfter is the opaque, signed cursor of a page whose last workspace was last.
CursorAfter(last shared.ID) string
}
UsageLedger reads every workspace's counts, under the installation's scope (multi-tenancy.md §4.3): the rows of at most Size workspaces, in the order of their ids.
type UsageQuery ¶
type UsageQuery struct {
From, To time.Time
Metric string
Tenant shared.ID
// Cursor is where the last page stopped, as CursorAfter wrote it; empty for the first.
Cursor string
Size int
}
UsageQuery is one read of the use: a range of days, optionally one metric and one workspace, a page of workspaces after the last one read.
type UsageRow ¶
type UsageRow struct {
Tenant shared.ID
Day time.Time
Metric string
Value int64
Closing *int64
Purged bool
}
UsageRow is one workspace's count of one metric on one day, live or purged. Closing is the day's last count for the counts kept as the day's highest, nil for the sums. A row before the range is answered too, the last one per workspace and metric, so its closing value can be carried in.