Skip to content

@stratum-hq/lib

@stratum-hq/lib is the direct library for embedding Stratum in your Node.js application. It talks directly to PostgreSQL with no HTTP server in between, giving you maximum performance for tenant operations.

Terminal window
npm install @stratum-hq/lib @stratum-hq/core pg
Use Case Package
Node.js app, maximum performance @stratum-hq/lib
Serverless functions @stratum-hq/lib
Testing and scripting @stratum-hq/lib
Polyglot stack, service separation @stratum-hq/sdk + control plane
React admin UI @stratum-hq/sdk + @stratum-hq/react
import { Pool } from "pg";
import { Stratum } from "@stratum-hq/lib";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const stratum = new Stratum({ pool });
const tenant = await stratum.createTenant({
name: "Acme Corp",
slug: "acme_corp",
isolation_strategy: "SHARED_RLS",
});
const config = await stratum.resolveConfig(tenant.id);
const permissions = await stratum.resolvePermissions(tenant.id);
const stratum = new Stratum({
pool: pgPool, // Required: pg.Pool instance
keyPrefix: "sk_live_", // Optional: API key prefix (default: "sk_live_")
});

The pool is borrowed, not owned – Stratum never creates or closes the pool. You manage the pool lifecycle.

stratum.createTenant(input, audit?): Promise<TenantNode>
stratum.getTenant(id, includeArchived?): Promise<TenantNode>
stratum.listTenants(pagination): Promise<PaginatedResult<TenantNode>>
stratum.updateTenant(id, patch, audit?): Promise<TenantNode>
stratum.deleteTenant(id, audit?): Promise<void>
stratum.moveTenant(id, newParentId, audit?): Promise<TenantNode>
stratum.getAncestors(id): Promise<TenantNode[]>
stratum.getDescendants(id): Promise<TenantNode[]>
stratum.getChildren(id): Promise<TenantNode[]>
stratum.reorderTenant(id, position, audit?): Promise<TenantNode>
stratum.batchCreateTenants(inputs, audit?): Promise<BatchCreateResult>
stratum.getTenantContext(tenantId): Promise<TenantContext>
stratum.resolveConfig(tenantId): Promise<ResolvedConfig>
stratum.setConfig(tenantId, key, input, audit?): Promise<ConfigEntry>
stratum.deleteConfig(tenantId, key, audit?): Promise<void>
stratum.getConfigWithInheritance(tenantId): Promise<ResolvedConfig>
stratum.batchSetConfig(tenantId, entries, audit?): Promise<BatchSetConfigResult>
stratum.diffConfig(tenantIdA, tenantIdB): Promise<ConfigDiff>
stratum.resolvePermissions(tenantId): Promise<Record<string, ResolvedPermission>>
stratum.createPermission(tenantId, input, audit?): Promise<PermissionPolicy>
stratum.updatePermission(tenantId, policyId, input, audit?): Promise<PermissionPolicy>
stratum.deletePermission(tenantId, policyId, audit?): Promise<void>
stratum.createAbacPolicy(tenantId, input): Promise<AbacPolicy>
stratum.getAbacPolicies(tenantId): Promise<AbacPolicy[]>
stratum.resolveAbacPolicies(tenantId): Promise<ResolvedAbacPolicy[]>
stratum.evaluateAbac(tenantId, request): Promise<AbacEvaluationResult>
stratum.deleteAbacPolicy(tenantId, policyId): Promise<void>

ABAC policies inherit through the tenant hierarchy using the same LOCKED/INHERITED/DELEGATED modes as permissions. See the ABAC guide for details.

stratum.createApiKey(tenantId, nameOrOptions?, expiresAt?): Promise<CreatedApiKey>
stratum.validateApiKey(key): Promise<ValidatedApiKey | null>
stratum.revokeApiKey(keyId): Promise<boolean>
stratum.rotateApiKey(keyId, newName?): Promise<CreatedApiKey>
stratum.listApiKeys(tenantId?): Promise<ApiKeyRecord[]>
stratum.listDormantKeys(dormantDays?): Promise<ApiKeyRecord[]>
stratum.createWebhook(input, audit?): Promise<Webhook>
stratum.getWebhook(id): Promise<Webhook>
stratum.listWebhooks(tenantId?): Promise<Webhook[]>
stratum.updateWebhook(id, input, audit?): Promise<Webhook>
stratum.deleteWebhook(id, audit?): Promise<void>
stratum.testWebhook(id): Promise<TestResult>
stratum.queryAuditLogs(query): Promise<AuditEntry[]>
stratum.getAuditEntry(id): Promise<AuditEntry | null>
stratum.grantConsent(tenantId, input, audit?): Promise<ConsentRecord>
stratum.revokeConsent(tenantId, subjectId, purpose, audit?): Promise<boolean>
stratum.listConsent(tenantId, subjectId?): Promise<ConsentRecord[]>
stratum.getActiveConsent(tenantId, subjectId, purpose): Promise<ConsentRecord | null>
stratum.exportTenantData(tenantId): Promise<Record<string, unknown>>
stratum.purgeTenant(tenantId, audit?): Promise<void>
stratum.purgeExpiredData(retentionDays?): Promise<{ deleted_count: number }>
stratum.createRegion(input, audit?): Promise<Region>
stratum.getRegion(id): Promise<Region>
stratum.listRegions(): Promise<Region[]>
stratum.updateRegion(id, input, audit?): Promise<Region>
stratum.deleteRegion(id, audit?): Promise<void>
stratum.migrateRegion(tenantId, newRegionId, audit?): Promise<void>
stratum.createRole(input, audit?): Promise<Role>
stratum.getRole(id): Promise<Role | null>
stratum.listRoles(tenantId?): Promise<Role[]>
stratum.updateRole(id, input, audit?): Promise<Role | null>
stratum.deleteRole(id, audit?): Promise<boolean>
stratum.assignRoleToKey(keyId, roleId): Promise<boolean>
stratum.removeRoleFromKey(keyId): Promise<boolean>
stratum.resolveKeyScopes(keyId): Promise<string[]>
stratum.getDeliveryStats(tenantId?): Promise<DeliveryStats>
stratum.listFailedDeliveries(limit?, tenantId?): Promise<FailedDelivery[]>
stratum.retryDelivery(deliveryId): Promise<boolean>
stratum.retryFailedDeliveries(tenantId?): Promise<number>
stratum.listWebhookDeliveries(webhookId): Promise<Record<string, unknown>[]>
import { encrypt, decrypt, reEncrypt } from "@stratum-hq/lib";
encrypt(plaintext: string): string // "v1:iv:tag:ciphertext"
decrypt(ciphertext: string): string
reEncrypt(ciphertext: string, oldKey: string, newKey: string): string
stratum.rotateEncryptionKey(oldKey, newKey, audit?): Promise<KeyRotationResult>

Low-level helpers for advanced use:

import { withClient, withTransaction } from "@stratum-hq/lib";
const result = await withClient(pool, async (client) => {
return client.query("SELECT * FROM tenants WHERE id = $1", [id]);
});
await withTransaction(pool, async (client) => {
await client.query("INSERT INTO ...");
await client.query("UPDATE ...");
});

@stratum-hq/lib assumes the Stratum database schema exists. Run the control plane migrations first, or apply the migration SQL manually:

Terminal window
# Option 1: Start the control plane (runs migrations automatically)
node packages/control-plane/dist/index.js
# Option 2: Apply SQL directly
psql -d stratum -f packages/control-plane/src/db/migrations/001_init.sql

All errors come from @stratum-hq/core:

import {
TenantNotFoundError,
TenantArchivedError,
ConfigLockedError,
PermissionLockedError,
PermissionRevocationDeniedError,
} from "@stratum-hq/core";
try {
await stratum.setConfig(childId, "locked_key", { value: 500 });
} catch (err) {
if (err instanceof ConfigLockedError) {
console.log("Cannot override locked key");
}
}