@lssm/integration.runtime-managed
v2.0.2
Published
Managed runtime compatibility wrapper for Builder.
Readme
@lssm/integration.runtime-managed
Managed runtime compatibility wrapper for Builder, plus the managed database
runtime preset (./database).
createDatabaseRuntime()
createDatabaseRuntime(options) composes the governed Postgres provider, the O2
observability binding, tenant-scoped RLS access, and the pre-bound governed
read / relational-read / mutation resolvers into one preset. It returns:
| member | description |
| ----------------- | ------------------------------------------------------------------------ |
| provider | Tenant-scoped DatabaseProvider (ambient tenant; fail-closed when none) |
| governedQuery | Pre-bound resolver factory (data-view and relational descriptors) |
| governedMutation| Pre-bound executeGovernedMutation |
| withTenant(id) | Returns a slice bound to a fixed tenant id |
| close() | Closes the underlying pool once |
import { createDatabaseRuntime } from '@lssm/integration.runtime-managed/database';
const db = createDatabaseRuntime({
connection: { connectionString: process.env.DATABASE_URL },
pooler: 'transaction-pooler',
instrumentation, // lib.observability Instrumentation facade (optional)
});
const tenant = db.withTenant('tenant-42');
const list = tenant.governedQuery({ descriptor }); // (envelope, ctx) => ContractResult
await tenant.governedMutation(mutationDescriptor);Tenant scoping (RLS, Critic N6)
The raw provider is never exposed. Every read/write runs inside a transaction
that first issues set_config('app.tenant_id', <id>, true), so Postgres RLS
policies see the active tenant. The top-level provider resolves the tenant
from resolveTenantId (or the auth seam) per call and fails closed when no
tenant context is present; use withTenant(id) in server contexts that already
know the tenant. TransportContext.principal is carried onto resolver ctx and
never stripped (AC14); policyDecisionRef / policy.auth enforcement stays in
the handlers.
Pooler modes (AC3)
pooler selects connection sizing and SSL. Prepared statements are always
disabled — SupabasePostgresProvider hard-sets prepare: false, which the
Supabase transaction pooler (pgBouncer transaction mode) requires.
| mode | maxConnections | sslMode | notes |
| -------------------- | -------------- | --------- | ------------------------------------------------ |
| transaction-pooler | 1 | require | Default. pgBouncer txn mode; no prepared stmts. |
| session-pooler | 10 | require | Backend held per connection for the session. |
| direct | 10 | prefer | Direct Postgres connection (no pooler). |
Override sizing/SSL via connection.maxConnections / connection.sslMode.
Observability (O2)
Pass an instrumentation (the lib.observability Instrumentation facade) and
each governed read/write emits one span plus a database.query /
database.mutation duration metric. The tenant set_config statement is
classified as a session statement and excluded from the data-latency histograms.
