App Backends Contract
Everything in this page is exported from @hellmai/lumenflow-packs-app-backends/contract (and
re-exported from the package root). Each type is a zod schema with an inferred
TypeScript type, for example ProvisionRequestSchema and ProvisionRequest. Every object schema is
strict: an unknown field is a contract violation, not a silent extension.
Resources
Section titled “Resources”The contract has two kinds of values.
- Management read models,
AppBackendandOperation. They are wire resources, so their field names aresnake_case. - SPI value types, everything an adapter takes or returns (
ProvisionRequest,BackendHandle,BackendStatus,MigrationBundle,RestorePoint, and so on). Their field names arecamelCase.
The split is deliberate and the only place the two conventions meet. A caller that renders an
AppBackend maps from SPI values in one place; an adapter never sees a snake_case field.
AppBackend (wire, snake_case)
Section titled “AppBackend (wire, snake_case)”| Field | Meaning |
|---|---|
backend_id | Opaque reference to the backend. |
workspace_id, tenant_subject, app_id | The isolation key. |
environment | production or preview:<name>. |
region | The region triple. |
adapter_id | The adapter that holds the backend. |
placement | One of the placement values, or null. |
state | A backend state. |
data_url, auth_url | Endpoints, or null before the data plane serves the backend. |
publishable_key | lf_pub_ followed by an opaque identifier, or null. See below. |
capabilities | The adapter capability document. |
recovery_window, usage | Read models, each null when unavailable. |
AppBackend carries no secret. The only key that reaches an application is the publishable key. It
selects a backend and maps to the anonymous role; it is an opaque, rotatable identifier, not a
bearer token.
Operation (wire, snake_case)
Section titled “Operation (wire, snake_case)”Every mutating call is asynchronous and yields an Operation that is polled or signalled:
operation_id, kind (one of the SPI operation names), backend_id (or null), state
(queued, running, succeeded, failed), error, created_at, updated_at. The schema enforces
that a failed operation carries a typed error and that no other state does.
Closed enums and the x- extension rule
Section titled “Closed enums and the x- extension rule”State and capability enums are closed. A value outside the list is rejected, so a typo or an
undeclared value never passes for a real one. Where a provider legitimately has a value of its own,
the enum is an extensible enum: it accepts the listed values and any value of the form x- followed
by a lowercase letter or digit and up to 62 more lowercase letters, digits, ., _ or -.
EXTENSION_PREFIX and isExtensionValue are exported for adapters and callers. Extension values are
provider-specific by definition.
| Enum | Values | Extensible |
|---|---|---|
BACKEND_STATES | provisioning, ready, degraded, read_only, restoring, deprovisioning, deleted, failed | no |
OPERATION_STATES | queued, running, succeeded, failed | no |
ACCESS_MODES | read_write, read_only | no |
CONNECTION_ROLES | data_api, auth, migrator | no |
ROTATION_TARGETS | the connection roles, plus operator and signing_key | no |
EXPORT_FORMATS | neutral-jsonl-v1, postgres-custom-dump | no |
PLACEMENTS | shared-cell, dedicated, provider-project, external | no |
PITR_SCOPES | backend, cell | no |
LEDGER_OUTCOMES | applied, failed | no |
LIST_SCOPES | isolation, adapter | no |
PRIVILEGES | tenant, operator | no |
SEALED_PURPOSES | the connection roles, plus export_sink and import_source | no |
ENGINE_FAMILIES | postgres | yes |
DATA_API_SEMANTICS | postgrest-compatible-v1 | yes |
AUTH_SERVERS | gotrue-compatible-v1 | yes |
SIZE_CLASSES | small, medium, large | yes |
RESTORE_POINT_REASONS | pre_migration, on_demand, scheduled | yes |
Isolation key
Section titled “Isolation key”Every backend belongs to exactly one IsolationKey: { workspaceId, tenantSubject, appId }. Each is a
non-empty identifier of at most 256 characters with no control characters.
The isolation key is the unit of tenancy for the whole SPI. Every operation that takes a handle checks
that the caller’s isolation key owns it and returns not_found otherwise, so a caller cannot tell an
absent backend from someone else’s. list is scoped to the caller’s isolation key. Idempotency
records are scoped by it too (see Adapter SPI).
Region triple
Section titled “Region triple”Region is { residency, provider, location }.
residencyis a neutral residency code (lowercase letters, digits and_, 2 to 32 characters). The contract fixes only its shape; the set of accepted codes belongs to whoever hosts the backend.providerandlocationare the adapter’s own vocabulary.
An adapter declares the regions it can serve in its capability document. A provision request for a
region the adapter did not declare is refused with unsupported_capability.
Environment
Section titled “Environment”production, or preview:<name> where <name> is lowercase letters, digits and -, up to 63
characters.
Handles and opaque names
Section titled “Handles and opaque names”A BackendHandle is { backendId, adapterId, placementRef, dbRef, region }, plus an optional
adoption: { mode } on a backend registered with adopt (a database the pack did not create). backendId,
placementRef and dbRef are opaque references: 8 to 128 URL-safe characters that carry no
workspace, tenant or app identifier. This matters because catalog names in a shared database server
are readable by every connected role. Adapters derive database and role names from random
references, never from an isolation key.
Sealed references and secrets
Section titled “Sealed references and secrets”No operation returns a credential in clear. Where a credential or a location of data must cross the
SPI, the contract uses a sealed reference: { custodyRef, purpose, expiresAt }, resolvable only
inside the data plane or custody service that issued it.
| Type | purpose | Returned or taken by |
|---|---|---|
SealedConnectionRef | data_api, auth or migrator | connectionFor returns |
SealedSinkRef | export_sink | exportSnapshot takes |
SealedSourceRef | import_source | importSnapshot takes |
SealedAdoptionRef | adoption_source | adopt takes |
Auth provider client secrets follow the same rule: AuthProviderSettings carries clientSecretRef
(an opaque reference) and never the secret.
The remaining value types
Section titled “The remaining value types”| Group | Types |
|---|---|
| Provisioning | ProvisionRequest, AuthSettings, CorsSettings, BackendHandle |
| Status | BackendStatus, UsageReport, RecoveryWindow, BackendLimits, CapacityReport |
| Migrations | MigrationBundle, MigrationPlan, MigrationLedgerEntry, ConsumerSignature |
| Restore | RestorePoint, RestoreTarget (point_in_time or restore_point), VerificationReport, SwapResult |
| Export and import | ExportManifest, ImportReport |
| Operator | ListFilter, ListPage, CancelResult, ReconcileReport |
A MigrationBundle is a bundleId plus 1 to 1000 migrations, each with a version, a name, the
sha256 of its SQL and the SQL text. A bundle may carry a detached consumerSignature
(ed25519, with key id, expiry and nonce); the adapter carries it and verification belongs to the
operator. VerificationReport.equal is true exactly when every table and sequence is equal and the
privilege diff is empty; the schema enforces that, and algorithmVersion names the table-hash
algorithm (VERIFY_ALGORITHM_VERSION is chained-sha256-v1: counts plus chained SHA-256).
Capability document
Section titled “Capability document”AdapterCapabilities is what an adapter declares about itself. Key fields:
engineFamily,engineVersions,dataApiSemantics,authServer.rowLevelPolicies: false means the adapter cannot host the policy contract. See Conformance for what that costs.transactionalMigrations,restorePoints,branching,crossRegionBackups.pitr:{ supported, maxWindowDays, granularitySeconds }. Whensupportedis false both numbers must be 0; when true both must be greater than 0.pitrScopeisbackendorcell.exportFormats(no repeats),regions,placement,extensions(the allowlist the adapter enforces).maxBackends(nullmeans unbounded),maxConnectionsPerBackend,maxDatabaseSizeGb.coldStartP95Ms: measured, never guessed.nullmeans not measured.
capabilitiesVersion is CAPABILITIES_VERSION (currently 1) and changes on any breaking change to the
document shape.