PrimeBrickPrimeBrick
  • Docs
  • Contact
  • MIT License
  • Documentation
  • MCP Server
  • API Catalog
  • Services
  • Libraries
PrimeBrickPrimeBrick

© 2026 PrimeBrick. MIT License. v3.8.0

github
DAL Library
SDK Library
    OverviewGetting StartedAuthenticationExt-JSONRedis cache layerConfig tables & ConfigLoaderNATS™ ClientService RegistrationHTTP ServerSSE StandardPresenceAPI Reference
powered by Zudoku
SDK Library

<!-- AUTO-GENERATED -->

title: API Reference description: Complete API reference for @primebrick/sdk — every exported symbol.

API Reference

Every exported symbol from @primebrick/sdk, rendered mechanically from the TypeDoc extraction. This page is regenerated from docs/user-guide/_extracted/api.json on every pnpm extract-docs run — do not edit by hand.

Classes

AuthError

PropertyTypeDescription
internal_codestring

AuthError(internal_code, message)

ParameterTypeDescription
internal_codestring
messagestring

ConfigLoader

Dictionary-style config loader backed by a config table. Mirrors BE's loadAuthConfig / getAuthConfig / invalidateAuthConfig pattern (config.ts:150-180), generalized so every microservice can reuse it.

DB-agnostic: depends on ConfigRepositoryPort, NOT on any specific DAL. The consumer provides an adapter that implements ConfigRepositoryPort using their DAL (e.g. @primebrick/dal-pg, or raw SQL).

Load once at startup → cache in memory → get(key) on hot path (zero DB hits). Call invalidate() to force a reload on next load().

ConfigLoader(repo)

ParameterTypeDescription
repoConfigRepositoryPort

ConfigLoader.load(): Promise<Record<string, string \| null>>

Load all config rows from DB into in-memory cache. Call once at startup. Throws if DB is unreachable.

ConfigLoader.get(key): string \| null

Get a config value from cache. Returns null if key is missing or value is null. Throws if load() has not been called.

ParameterTypeDescription
keystring

ConfigLoader.require(key): string

Get a config value, throwing if it's missing or empty.

ParameterTypeDescription
keystring

ConfigLoader.getTyped(key, converter): T \| null

Get a typed config value via a converter function. Returns null if the key is missing.

ParameterTypeDescription
keystring
converter(v: string) => T

ConfigLoader.requireTyped(key, converter): T

Get a typed config value, throwing if it's missing.

ParameterTypeDescription
keystring
converter(v: string) => T

ConfigLoader.getAll(): Record<string, string \| null>

Get all config as a plain object.

ConfigLoader.invalidate()

Invalidate the cache so the next load() re-reads from DB.

GracefulShutdown

Graceful shutdown manager. Extracted from emailsender's index.ts:55-95.

  • Re-entrancy guard: second signal is a no-op.
  • Runs all cleanup functions in parallel (Promise.allSettled).
  • Always calls process.exit() explicitly.
  • Installs SIGTERM, SIGINT, SIGHUP + uncaughtException + unhandledRejection handlers.

Pure Node.js — no DB dependency. The consumer registers cleanup functions (e.g. getDal().close(), NatsClient.close()) via addCleanup().

GracefulShutdown(serviceName)

ParameterTypeDescription
serviceNamestring

GracefulShutdown.addCleanup(fn)

Register a cleanup function to run on shutdown.

ParameterTypeDescription
fnCleanupFn

GracefulShutdown.install()

Install signal + crash handlers.

GracefulShutdown.shutdown(reason, code): Promise<void>

ParameterTypeDescription
reasonstring
codenumber

HealthCheck

Health check utility. Extracted from BE's index.ts:126-148 pattern. Checks DB connectivity (via HealthCheckPort) and optional custom checks.

DB-agnostic: depends on HealthCheckPort, NOT on pg.Pool. The consumer provides an adapter that runs whatever their DB uses (e.g. SELECT 1 for PG).

HealthCheck(dbPing, customChecks)

ParameterTypeDescription
dbPingHealthCheckPort
customChecksRecord<string, () => Promise<HealthCheckResult>>

HealthCheck.checkDb(): Promise<HealthCheckResult>

HealthCheck.runAll(): Promise<Record<string, HealthCheckResult>>

HealthCheck.isHealthy(results): boolean

ParameterTypeDescription
resultsRecord<string, HealthCheckResult>

HttpHeaderProvider

Adapter for raw Node.js HTTP IncomingMessage (microservices using createHttpServer).

HttpHeaderProvider(req)

ParameterTypeDescription
reqIncomingMessage

HttpHeaderProvider.getHeader(name): string \| undefined

ParameterTypeDescription
namestring

NatsClient

Singleton NATS connection manager. Extracted from emailsender's nats/client.ts:1-31.

Requires nats as a peer dependency — consumers that don't need NATS can skip installing it and won't import this module. No DB dependency.

The publish(), subscribe(), and subscribeRequest() methods use Ext-JSON (BigInt-safe) serialization automatically. Consumers pass plain TS objects and receive plain TS objects — they never call extJson functions directly.

NatsClient.getConnection(url): Promise<NatsConnection>

ParameterTypeDescription
urlstring

NatsClient.getJetStream(): JetStreamClient

NatsClient.isConnected(): boolean

Check if the NATS connection is alive. Returns false if the connection was never established or has been closed.

NatsClient.close(): Promise<void>

NatsClient.publish(subject, data, hdrs): Promise<void>

Publish a message with automatic Ext-JSON serialization. The data object is serialized with extJsonStringify (BigInt-safe) and encoded as UTF-8 before publishing.

ParameterTypeDescription
subjectstringNATS subject (e.g. "emailsender.send", "customer.created")
dataunknownAny serializable object (bigint values are preserved)
hdrsRecord<string, string>

NatsClient.subscribe(subject, handler): Promise<Subscription>

Subscribe to a NATS subject with automatic Ext-JSON deserialization. Each incoming message is decoded from UTF-8 and parsed with extJsonParse (BigInt-safe). The handler receives a typed object — no manual decode/parse.

ParameterTypeDescription
subjectstringNATS subject to subscribe to
handler(data: T, raw: Msg) => Promise<void>Async function receiving the parsed message data and raw Msg

NatsClient.subscribeRequest(subject, handler): Promise<Subscription>

Subscribe to a NATS subject with request-reply pattern. The handler receives the parsed request and returns a response that is automatically serialized with extJsonStringify and published back to msg.reply (if set).

ParameterTypeDescription
subjectstringNATS subject to subscribe to
handler(request: TRequest, raw: Msg) => Promise<TResponse>Async function receiving parsed request, returning response

NatsHeaderProvider

Adapter for NATS Msg headers (NATS subscribers).

NatsHeaderProvider(msg)

ParameterTypeDescription
msgMsg

NatsHeaderProvider.getHeader(name): string \| undefined

ParameterTypeDescription
namestring

RbacDeniedError

Error thrown when RBAC check fails.

PropertyTypeDescription
missingstring[]
requiredtypeOperator

RbacDeniedError(missing, required)

ParameterTypeDescription
missingstring[]
requiredtypeOperator

ServiceRegistrar

Registers a microservice via NATS lifecycle events and maintains a heartbeat.

NATS™-based: publishes to service.register / service.heartbeat / service.unregister subjects. The BE subscribes and persists to the service_registry table. The microservice never touches the DB directly.

The healthCheckFn is called on each heartbeat to include the current health status (HTTP + NATS + custom checks).

ServiceRegistrar(nats, config, healthCheckFn)

ParameterTypeDescription
natstypeof NatsClient
configServiceRegistrarConfig
healthCheckFnHealthCheckFn

ServiceRegistrar.register(): Promise<void>

ServiceRegistrar.sendHeartbeat(): Promise<void>

ServiceRegistrar.unregister(): Promise<void>

ServiceRegistrar.startHeartbeat(): Timeout

ServiceRegistrar.stopHeartbeat()

Interfaces

ApiKeyPort

ApiKeyRecord

Port for looking up API keys by hash.

Used by verifyApiKey() to verify machine-to-machine credentials. The api_keys table lives in the public schema — both BE and microservices can implement this (microservices read cross-schema from public.api_keys).

FieldTypeDescription
uuidstring
namestring
permissionsstring[]
is_systemboolean
is_activeboolean
expires_atDate | null

ApplyPatchesResult

FieldTypeDescription
appliedOrRegisterednumber
skippednumber

AuthConfig

Full auth configuration loaded at startup from the service's config table.

FieldTypeDescription
modeAuthMode
roles_pathstringPath expression used to extract the roles array from a JWT payload. Examples: "roles", "realm_access.roles", "resource_access.<client>.roles"
oidcOidcConfig
gatewayGatewayConfig
casdoor_endpointstring
casdoor_organizationstring
enable_email_verification_checkboolean

AuthConfigPort

Port for loading auth configuration from the service's config store.

BE implements this reading from auth_configurations table. Microservices implement this reading from their own config table.

AuthPorts

Ports needed ONLY by BE (STANDALONE mode). Microservices do NOT provide these.

ConfigRepositoryPort

Port interface for reading config rows from a DB config table.

The SDK's ConfigLoader depends on this port, NOT on any specific DAL. The consumer provides an adapter implementation using their DAL (e.g. @primebrick/dal-pg's dal.findAll, or a raw SQL query).

DatabasePort

Port interface for executing parameterized SQL queries.

The SDK's migration runner (applyPatches) depends on this port, NOT on pg.Pool. The consumer provides an adapter that wraps their DB driver (pg.Pool, mssql.ConnectionPool, mariadb.Pool, etc.).

The contract mirrors the minimal query(text, params?) shape that every SQL DB driver exposes.

EnvSchema

EnvValidationResult

FieldTypeDescription
validboolean
errorsstring[]
envRecord<string, string | undefined>

GatewayConfig

Gateway configuration (GATEWAY mode).

FieldTypeDescription
secretstring
secret_header_namestring
public_secretstring
public_secret_header_namestring
headers{ user_id: string; email: string; name: string; roles: string; idp_code: string; idp_org: string; idp_username: string; permissions: string; is_admin: string; is_system: string }

HeaderProvider

HealthCheckPort

Port interface for a DB health check (connectivity ping).

The SDK's HealthCheck depends on this port, NOT on pg.Pool. The consumer provides an adapter that runs whatever their DB uses (e.g. SELECT 1 for PG, SELECT 1 for MSSQL, etc.).

HealthCheckResult

FieldTypeDescription
okboolean

HttpServerOptions

FieldTypeDescription
portnumber
healthCheckHealthCheck
serviceNamestring
routeHandler(req: IncomingMessage, res: ServerResponse, url: URL) => Promise<boolean>Custom route handler — receives req/res, returns true if handled.

IConfigEntity

Shape of a dictionary-style config row. Every microservice config table mirrors this: one row per key, value stored as TEXT, type conversion performed at read time by ConfigLoader consumers.

Self-contained — does NOT extend IAuditableEntity from @primebrick/dal-pg. The SDK is DB-agnostic; audit fields are a DAL-specific concern handled by the consumer's entity class and adapter.

FieldTypeDescription
keystringUnique config key, e.g. "brevo_api_key".
valuestring | nullRaw TEXT value. null means "not set yet". Type conversion at read time.
label_keystringOptional i18n translation key for a short title (used by BE/FE for display).
description_keystringOptional i18n translation key for a longer description (used by BE/FE for display).

IServiceRegistry

Shape of a row in the service_registry table.

Self-contained interface — NO decorators, NO IAuditableEntity. The SDK is DB-agnostic; the consumer keeps their own decorated entity class (e.g. ServiceRegistryEntity with @Entity/@Column from @primebrick/dal-pg) and maps it to/from this interface in their adapter.

Previously duplicated in emailsender (service_registry_entity.ts:1-54) and BE (service_registry_entity.ts:1-42). Now the shared shape lives here.

FieldTypeDescription
codestring
base_urlstring
endpointsRecord<string, unknown>
namestring
descriptionstring
authorstring
github_repo_urlstring
service_versionstring
is_behind_scalerboolean
statusstring
last_health_check_atDate
is_enabledboolean
iconstring
icon_type"url" | "svg" | "base64" | "icon"

NormalizedIdpUser

FieldTypeDescription
idp_codestringIDP subject (JWT sub). Stable per-user IDP identifier.
emailstring | null
namestring | null
rolesstring[]
idp_orgstring | nullIDP organization (from owner or organization claim)
idp_usernamestring | nullIDP username (from name, username, or preferred_username claim)

OidcConfig

OIDC configuration (STANDALONE mode only).

FieldTypeDescription
issuer_urlstring
client_idstring
client_secretstring
audiencestring
issuer_typestring

RbacResult

FieldTypeDescription
allowedboolean
missingstring[]Missing permissions (only populated when not allowed)

ResolveInput

Port for resolving IDP subject to internal Primebrick UUID.

BE-ONLY port. Microservices do NOT implement this — they use GATEWAY-RESOLVED mode where the BE already resolved the user and forwards the full AuthUser in headers.

FieldTypeDescription
idp_codestring
emailstring | null
display_namestring | null
idp_orgstring | null
idp_usernamestring | null

RoleMappingEntry

Port for loading role-to-permission mappings from the database.

BE-ONLY port. Microservices do NOT implement this — they use GATEWAY-RESOLVED mode where the BE already expanded permissions and forwards them in headers.

FieldTypeDescription
permissionsstring[]
is_adminboolean
label_keystring

RoleMappingPort

ServiceHealthCheck

FieldTypeDescription
okboolean
errorstring

ServiceHeartbeatPayload

FieldTypeDescription
codestring
base_urlstring
service_versionstring
namestring
descriptionstring
authorstring
github_repo_urlstring
is_behind_scalerboolean
http_healthyboolean
nats_connectedboolean
checksRecord<string, ServiceHealthCheck>
iconstring
icon_type"url" | "svg" | "base64" | "icon"

ServiceRegisterPayload

FieldTypeDescription
codestring
base_urlstring
service_versionstring
namestring
descriptionstring
authorstring
github_repo_urlstring
is_behind_scalerboolean
http_healthyboolean
nats_connectedboolean
checksRecord<string, ServiceHealthCheck>
iconstring
icon_type"url" | "svg" | "base64" | "icon"
endpointsRecord<string, unknown>

ServiceRegistrarConfig

FieldTypeDescription
serviceCodestring
baseUrlstring
endpointsRecord<string, unknown>
heartbeatIntervalMsnumber
namestring
descriptionstring
authorstring
github_repo_urlstring
service_versionstring
is_behind_scalerboolean
iconstring
icon_type"url" | "svg" | "base64" | "icon"

ServiceRegistryPort

Port interface for CRUD operations on the service_registry table.

The BE implements this port using @primebrick/dal-pg's Repository. The SDK's ServiceRegistrar no longer uses this port — it publishes via NATS instead. The BE's NATS subscriber uses this port (via ServiceRegistryRepo) to persist incoming lifecycle events.

ServiceUnregisterPayload

FieldTypeDescription
codestring
base_urlstring
is_behind_scalerboolean

Session

Session payload carried per HTTP request. Mirrors the relevant subset of AuthUser plus future-proofing room (e.g. tenantId, requestId, locale).

Kept intentionally minimal & immutable: callers should treat it as read-only.

FieldTypeDescription
actorstringInternal Primebrick UUID of the authenticated user. Used as the value stored in audit columns (created_by, updated_by, deleted_by, ...). The literal string "system" is reserved for non-HTTP execution paths (database seeds, scheduled jobs, migrations, system API keys) and is only set via runAsSystem().
rolestypeOperatorRoles attached to the user / job, useful for low-level RBAC decisions inside services. May be empty for "system" callers.
idpCodestring | nullOriginal IDP sub for traceability. null for "system".
idpOrgstring | nullIDP organization (from owner or organization claim). null for "system".
idpUsernamestring | nullIDP username (from name, username, or preferred_username claim). null for "system".
isVerifiedbooleanEmail verification status from IDP. null for "system".
emailVerifiedbooleanEmail verification status (email-specific). null for "system".
issuerstringIDP issuer URL (from iss claim). null for "system".

UserResolverPort

Types & Enums

AuthMode

Authentication operating modes.

STANDALONE — the service itself validates the Bearer token against the IDP via OIDC discovery (jose + JWKS). Used by the BE.

GATEWAY — a trusted reverse proxy (or the BE proxy) forwards the fully resolved user identity via custom HTTP headers. The service verifies a shared secret header to defend against spoofing. Used by microservices (GATEWAY-RESOLVED — BE already resolved the user, microservice just deserializes headers).

Type: typeof AuthMode[typeOperator]

AuthUser

Authenticated user context. Produced by verifyAuth() (STANDALONE) or deserializeAuthUserFromHeaders() (GATEWAY-RESOLVED).

Identity model:

  • id → internal Primebrick user UUID (from user_profiles.uuid). The literal string "system" for system API keys.
  • idp_code → original IDP subject (the JWT sub). Traceability only.
  • roles → normalized role names from the IDP token.
  • permissions→ flattened set of permissions derived from roles.
  • isAdmin → if true, user bypasses all permission checks (admin role).
  • isSystem → if true, this is a system API key (not a user). Bypasses all RBAC. Actor defaults to "system" for audit fields.

CleanupFn

Type: () => Promise<void>

HealthCheckFn

Health check function — returns the result of local health checks (DB ping, NATS connectivity, etc.). The microservice injects this so the registrar can include health status in heartbeats.

Type: () => Promise<{ http_healthy: boolean; checks: Record<string, ServiceHealthCheck> }>

JwtClaims

Minimal shape of a decoded JWT payload (claims map).

Type: Record<string, unknown>

Permission

RBAC registry — single source of truth for permissions and role mappings.

Design:

  • Each HTTP action declares the EXACT permission(s) it requires (e.g. customers.read.all, emailsender.providers.create). The endpoint, not the role, determines what is needed.
  • Role → Permission mappings are stored in the role_mappings table (database). The auth middleware loads these mappings at startup and expands a user's roles into a flat Set<Permission> once per request.
  • The RBAC middleware evaluates the array with OR semantics by default (any-of). Use rbacHandler.all([...]) for AND semantics.
  • Roles marked with is_admin=true in the database grant ALL permissions (super-user wildcard).
  • API keys marked with is_system=true bypass all permission checks and set the actor to "system" for audit fields.

Two pseudo-permissions exist as sentinels handled directly by the middleware (they are NOT stored in role_mappings):

  • Permission.PUBLIC → endpoint reachable without a JWT.
  • Permission.AUTHENTICATED_USER → any caller with a valid identity passes, regardless of roles.

Type: typeof Permission[typeOperator]

Constants

AuthMode

Authentication operating modes.

STANDALONE — the service itself validates the Bearer token against the IDP via OIDC discovery (jose + JWKS). Used by the BE.

GATEWAY — a trusted reverse proxy (or the BE proxy) forwards the fully resolved user identity via custom HTTP headers. The service verifies a shared secret header to defend against spoofing. Used by microservices (GATEWAY-RESOLVED — BE already resolved the user, microservice just deserializes headers).

Type: { STANDALONE: "STANDALONE"; GATEWAY: "GATEWAY" }

FieldValue
STANDALONE"STANDALONE"
GATEWAY"GATEWAY"

PATCH_REGISTRY_DDL

Type: "CREATE TABLE IF NOT EXISTS public.primebrick_database_patches (\n patch_id text PRIMARY KEY,\n content_sha256 text NOT NULL,\n applied_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS primebrick_database_patches_sha_idx\n ON public.primebrick_database_patches (content_sha256);\n"

PATCH_REGISTRY_FQNAME

Type: "public.primebrick_database_patches"

Permission

RBAC registry — single source of truth for permissions and role mappings.

Design:

  • Each HTTP action declares the EXACT permission(s) it requires (e.g. customers.read.all, emailsender.providers.create). The endpoint, not the role, determines what is needed.
  • Role → Permission mappings are stored in the role_mappings table (database). The auth middleware loads these mappings at startup and expands a user's roles into a flat Set<Permission> once per request.
  • The RBAC middleware evaluates the array with OR semantics by default (any-of). Use rbacHandler.all([...]) for AND semantics.
  • Roles marked with is_admin=true in the database grant ALL permissions (super-user wildcard).
  • API keys marked with is_system=true bypass all permission checks and set the actor to "system" for audit fields.

Two pseudo-permissions exist as sentinels handled directly by the middleware (they are NOT stored in role_mappings):

  • Permission.PUBLIC → endpoint reachable without a JWT.
  • Permission.AUTHENTICATED_USER → any caller with a valid identity passes, regardless of roles.

Type: { PUBLIC: "_public"; AUTHENTICATED_USER: "_authenticated_user"; MODULES_READ_ALL: "modules.read.all"; MODULES_READ_SINGLE: "modules.read.single"; MODULES_UPDATE: "modules.update.single"; MODULES_DELETE: "modules.delete.single"; MODULES_CONFIG_READ: "modules.config.read"; MODULES_CONFIG_UPDATE: "modules.config.update"; PROFILE_READ: "profile.read"; PROFILE_UPDATE: "profile.update"; USER_PROFILE_READ_AUDIT: "userprofile.read.audit"; USERS_READ_ALL: "users.read.all"; USERS_READ_SINGLE: "users.read.single"; USERS_CREATE_SINGLE: "users.create.single"; USERS_UPDATE_SINGLE: "users.update.single"; USERS_DELETE_SINGLE: "users.delete.single"; USERS_RESTORE_SINGLE: "users.restore.single"; ORGANIZATIONS_READ_ALL: "organizations.read.all"; ORGANIZATIONS_READ_SINGLE: "organizations.read.single"; ORGANIZATIONS_READ_AUDIT: "organizations.read.audit"; ORGANIZATIONS_CREATE_SINGLE: "organizations.create.single"; ORGANIZATIONS_UPDATE_SINGLE: "organizations.update.single"; ORGANIZATIONS_DELETE_SINGLE: "organizations.delete.single"; ORGANIZATIONS_RESTORE_SINGLE: "organizations.restore.single"; CUSTOMERS_READ_ALL: "customers.read.all"; CUSTOMERS_READ_SINGLE: "customers.read.single"; CUSTOMERS_READ_AUDIT: "customers.read.audit"; CUSTOMERS_CREATE_SINGLE: "customers.create.single"; CUSTOMERS_CREATE_BULK: "customers.create.bulk"; CUSTOMERS_UPDATE_SINGLE: "customers.update.single"; CUSTOMERS_UPDATE_BULK: "customers.update.bulk"; CUSTOMERS_DELETE_SINGLE: "customers.delete.single"; CUSTOMERS_DELETE_BULK: "customers.delete.bulk"; CUSTOMERS_RESTORE_SINGLE: "customers.restore.single"; CUSTOMERS_RESTORE_BULK: "customers.restore.bulk"; CUSTOMERS_DUPLICATE_BULK: "customers.duplicate.bulk"; CUSTOMERS_EXPORT: "customers.export"; EMAILSENDER_PROVIDERS_READ_ALL: "emailsender.providers.read.all"; EMAILSENDER_PROVIDERS_READ_SINGLE: "emailsender.providers.read.single"; EMAILSENDER_PROVIDERS_CREATE: "emailsender.providers.create"; EMAILSENDER_PROVIDERS_UPDATE: "emailsender.providers.update"; EMAILSENDER_PROVIDERS_DELETE: "emailsender.providers.delete"; EMAILSENDER_SEND: "emailsender.send"; EMAILSENDER_LOG_CREATE: "emailsender.log.create" }

FieldValue
PUBLIC"_public"
AUTHENTICATED_USER"_authenticated_user"
MODULES_READ_ALL"modules.read.all"
MODULES_READ_SINGLE"modules.read.single"
MODULES_UPDATE"modules.update.single"
MODULES_DELETE"modules.delete.single"
MODULES_CONFIG_READ"modules.config.read"
MODULES_CONFIG_UPDATE"modules.config.update"
PROFILE_READ"profile.read"
PROFILE_UPDATE"profile.update"
USER_PROFILE_READ_AUDIT"userprofile.read.audit"
USERS_READ_ALL"users.read.all"
USERS_READ_SINGLE"users.read.single"
USERS_CREATE_SINGLE"users.create.single"
USERS_UPDATE_SINGLE"users.update.single"
USERS_DELETE_SINGLE"users.delete.single"
USERS_RESTORE_SINGLE"users.restore.single"
ORGANIZATIONS_READ_ALL"organizations.read.all"
ORGANIZATIONS_READ_SINGLE"organizations.read.single"
ORGANIZATIONS_READ_AUDIT"organizations.read.audit"
ORGANIZATIONS_CREATE_SINGLE"organizations.create.single"
ORGANIZATIONS_UPDATE_SINGLE"organizations.update.single"
ORGANIZATIONS_DELETE_SINGLE"organizations.delete.single"
ORGANIZATIONS_RESTORE_SINGLE"organizations.restore.single"
CUSTOMERS_READ_ALL"customers.read.all"
CUSTOMERS_READ_SINGLE"customers.read.single"
CUSTOMERS_READ_AUDIT"customers.read.audit"
CUSTOMERS_CREATE_SINGLE"customers.create.single"
CUSTOMERS_CREATE_BULK"customers.create.bulk"
CUSTOMERS_UPDATE_SINGLE"customers.update.single"
CUSTOMERS_UPDATE_BULK"customers.update.bulk"
CUSTOMERS_DELETE_SINGLE"customers.delete.single"
CUSTOMERS_DELETE_BULK"customers.delete.bulk"
CUSTOMERS_RESTORE_SINGLE"customers.restore.single"
CUSTOMERS_RESTORE_BULK"customers.restore.bulk"
CUSTOMERS_DUPLICATE_BULK"customers.duplicate.bulk"
CUSTOMERS_EXPORT"customers.export"
EMAILSENDER_PROVIDERS_READ_ALL"emailsender.providers.read.all"
EMAILSENDER_PROVIDERS_READ_SINGLE"emailsender.providers.read.single"
EMAILSENDER_PROVIDERS_CREATE"emailsender.providers.create"
EMAILSENDER_PROVIDERS_UPDATE"emailsender.providers.update"
EMAILSENDER_PROVIDERS_DELETE"emailsender.providers.delete"
EMAILSENDER_SEND"emailsender.send"
EMAILSENDER_LOG_CREATE"emailsender.log.create"

resetAuthConfigForTest

Test helper alias (backward compat).

Type: () => void

SERVICE_SUBJECTS

NATS™ subjects and payload types for microservice lifecycle events.

Microservices publish these events via NATS™. The BE subscribes and persists the state to the service_registry table.

Flow:

  • On startup: microservice publishes service.register
  • Every 30s: microservice publishes service.heartbeat
  • On graceful shutdown: microservice publishes service.unregister
  • On NATS reconnect: microservice publishes immediate service.heartbeat

Type: { REGISTER: "service.register"; HEARTBEAT: "service.heartbeat"; UNREGISTER: "service.unregister" }

FieldValue
REGISTER"service.register"
HEARTBEAT"service.heartbeat"
UNREGISTER"service.unregister"

SYSTEM_ACTOR

Sentinel actor used by non-HTTP code paths (seeds, migrations, scheduled jobs, system API keys). Audit columns will record the literal string "system" so it is trivially distinguishable from any user UUID.

Type: "system"

Functions

applyPatches(patchesDir, db): Promise<ApplyPatchesResult>

Apply database SQL patches from a directory.

Strategy (adapted from BE's scripts/database-patch-apply.ts:1-148):

  • Read .sql files from patchesDir sorted by filename.
  • For each file, consult public.primebrick_database_patches (patch_id + content_sha256):
    • Same patch_id + same SHA → skip (already applied).
    • Same patch_id + different SHA → fail (immutable patch changed).
    • Missing patch_id but same SHA exists → register without re-executing.
    • Otherwise → BEGIN; apply SQL; INSERT registry row; COMMIT.

DB-agnostic: depends on DatabasePort, NOT on pg.Pool. The consumer provides an adapter that wraps their DB driver.

ParameterTypeDescription
patchesDirstringAbsolute path to the directory containing .sql patch files.
dbDatabasePortDatabasePort adapter (wraps the consumer's DB driver).

buildAuthUser(internalUuid, normalized, permissions, isAdmin): AuthUser

Combine a normalized IDP user with the internal Primebrick UUID. Permissions are NOT computed here - they are computed separately using the database-driven role mapping.

Note: id here is the internal UUID from user_profiles.uuid, NEVER the IDP sub. The mapping is performed by the UserResolverPort.

ParameterTypeDescription
internalUuidstring
normalizedNormalizedIdpUser
permissionsSet<string>
isAdminboolean

buildNatsAuthHeaders(user, config): Record<string, string>

Build NATS headers from a resolved AuthUser (publisher side, BE). Wraps serializeAuthUserToHeaders() — includes gateway secret for anti-spoofing.

ParameterTypeDescription
userAuthUser
configAuthConfig

checkRbac(user, requiredPermissions, mode): RbacResult

Evaluate RBAC for a user against a list of required permissions.

ParameterTypeDescription
userAuthUserThe authenticated AuthUser
requiredPermissionstypeOperatorList of accepted permissions for this endpoint
mode"any" | "all""any" (OR, default) or "all" (AND)

coerceRoles(raw): string[]

Coerce any role payload shape into a clean array of strings.

  • Strings stay as-is (after String() coercion)
  • Objects with a name field are reduced to that name
  • Anything else is stringified
  • Empty / non-array inputs become []
ParameterTypeDescription
rawunknown

createHttpServer(options): Promise<Server<typeof IncomingMessage, typeof ServerResponse>>

Minimal HTTP server with health endpoint. Uses native http module (no Express). All errors (unhandled routes, route handler crashes, auth errors) are returned as RFC 7807 Problem Details JSON — same format as the BE error handler.

ParameterTypeDescription
optionsHttpServerOptions

deserializeAuthUserFromHeaders(headers, config): AuthUser

Deserialize an AuthUser from headers (microservice side, GATEWAY-RESOLVED mode). The gateway secret is verified separately by verifyAuthGatewayResolved().

ParameterTypeDescription
headersHeaderProvider
configAuthConfig

enforceHttpRbac(user, requiredPermissions, mode): void

Enforce RBAC for an HTTP request. Throws RbacDeniedError on denial.

ParameterTypeDescription
userAuthUserThe authenticated AuthUser
requiredPermissionstypeOperatorList of accepted permissions for this endpoint
mode"any" | "all""any" (OR, default) or "all" (AND)

enforceNatsRbac(user, requiredPermissions, mode): void

Enforce RBAC for a NATS message. Throws RbacDeniedError on denial. Same logic as enforceHttpRbac — separated for semantic clarity.

ParameterTypeDescription
userAuthUser
requiredPermissionstypeOperator
mode"any" | "all"

expandPermissions(roles, getRoleMappingFn): Promise<{ patterns: string[]; isAdmin: boolean }>

Expand a list of role names into patterns and admin status. This function queries the role_mappings table to resolve roles to permissions. Roles marked with is_admin=true bypass all permission checks.

ParameterTypeDescription
rolestypeOperatorRole names from the IDP (as extracted from JWT via roles_path)
getRoleMappingFn(role: string) => Promise<{ permissions: string[]; is_admin: boolean } | null>Function that returns the mapping for a specific role

extJsonMiddleware(): (req: Request, res: Response, next: NextFunction) => void

Express middleware that replaces res.json() with Ext-JSON serialization. Install once in the Express app, before any routes.

Example: app.use(extJsonMiddleware());

Wire format: standard JSON with numbers (not strings) for bigint values.

extJsonParse(text): T

Parse an Ext-JSON string.

ALL integers are returned as native bigint (via reviver — alwaysParseAsBig option in json-bigint v1.0.0 is broken for floats, so we use a reviver instead). Floats (values with decimal point or scientific notation) are returned as number. Strings, booleans, null are unaffected.

This makes types predictable: every integer is always bigint, every float is always number. No number | bigint ambiguity.

ParameterTypeDescription
textstring

extJsonStringify(data): string

Serialize a value to an Ext-JSON string. BigInt values are serialized as JSON numbers (e.g. 42n → "42"). Floats are serialized as JSON numbers (e.g. 3.14 → "3.14").

ParameterTypeDescription
dataunknown

generateApiKey(): { key: string; prefix: string }

Generate a new random API key string. Format: pbk_<32 random hex chars> (36 chars total, 8-char prefix for display).

getAuthConfig(): AuthConfig

Return the cached config. Throws if not loaded yet. Does NOT touch the DB on the hot path.

getSession(): Session \| undefined

Read the current session, or undefined when called from outside any als.run() scope (e.g. before the auth middleware, or in a top-level script).

hashApiKey(key): string

Hash an API key string using SHA-256. Returns a hex-encoded string.

ParameterTypeDescription
keystring

initAuthConfig(p): void

Initialize the auth config with a port implementation. Called once at application startup.

ParameterTypeDescription
pAuthConfigPort

invalidateAuthConfig(): void

Invalidate the cache so the next loadAuthConfig() re-reads from the port.

isPatchBodyAlreadyRecorded(db, contentSha256): Promise<boolean>

ParameterTypeDescription
dbDatabasePort
contentSha256string

isPermissionGranted(userPermissions, requiredPermission): boolean

Check if a permission is granted given a set of user permissions. Supports wildcard patterns in user permissions.

ParameterTypeDescription
userPermissionsSet<string>Set of permissions granted to user (may contain wildcards)
requiredPermissionstringPermission required by the endpoint

isPermissionSentinel(p): boolean

true when the given permission is a sentinel (PUBLIC / AUTHENTICATED_USER) handled directly by the rbac middleware rather than by role expansion.

ParameterTypeDescription
pstring

loadAuthConfig(): Promise<AuthConfig>

Load auth config from the port into the in-memory cache. Called once at startup (and on invalidation). Throws if the port is not initialized or the DB is unreachable.

matchesWildcard(pattern, permission): boolean

Check if a permission string matches a pattern (supports * wildcard).

ParameterTypeDescription
patternstringPattern with optional * wildcard (e.g., "customers.read.*")
permissionstringPermission string to match (e.g., "customers.read.single")

normalizeIdpToken(payload, rolesPath): NormalizedIdpUser

Build a normalized user shape from a JWT payload using a configurable roles path. Throws on empty payload or missing sub claim.

ParameterTypeDescription
payloadJwtClaims | null | undefined
rolesPathstring

patchIdFromFilename(filename): string

ParameterTypeDescription
filenamestring

requireActor(): string

Read the current actor (UUID or "system"). Throws if no session is in scope — meaning the caller forgot to wrap the code in runAsSystem() or is running before the auth middleware.

requireEnv(schema): Record<string, string \| undefined>

Validate env vars and throw if any required ones are missing.

ParameterTypeDescription
schemaEnvSchema

resetOidcRuntimeForTest(): void

Test helper: drop all cached OIDC runtimes so new discovery happens.

runAsSystem(fn): T

Run fn with a synthetic "system" session in scope. The only legitimate use cases are bootstrap scripts (seeds, migrations) and well-isolated background jobs that have no real authenticated user.

Do NOT use this from inside an HTTP handler to bypass auth.

ParameterTypeDescription
fn() => T

runWithSession(session, fn): T

Run fn with the given session attached to the current async chain.

Prefer the higher-level runAsSystem() / the auth middleware over calling this directly, unless you are writing infrastructure code.

ParameterTypeDescription
sessionSession
fn() => T

serializeAuthUserToHeaders(user, config): Record<string, string>

Serialize a fully resolved AuthUser into a headers object for forwarding to microservices (HTTP proxy or NATS™). Also includes the gateway secret header for anti-spoofing.

ParameterTypeDescription
userAuthUser
configAuthConfig

sha256Hex(body): string

ParameterTypeDescription
bodystring

slugifyPatchSegment(s): string

ParameterTypeDescription
sstring

utcTimestampForFilename(d): string

ParameterTypeDescription
dDate

validateEnv(schema): EnvValidationResult

Centralized env var validation. Replaces scattered inline checks (emailsender: dal.ts:18-20, http-server.ts:5-9, webhook-service.ts:9-14, email-service.ts:12-17; BE: src/db/pool.ts).

Pure process.env — no DB dependency.

ParameterTypeDescription
schemaEnvSchema

verifyAccessToken(token, oidc): Promise<JwtClaims>

Verify a Bearer access token against the configured IDP.

Validations performed:

  • JWT signature (via JWKS published by the IDP)
  • exp (not expired) and nbf (not used before)
  • iss matches the configured issuer
  • aud matches oidc.audience if configured (otherwise ignored)

Throws on any failure. Callers should catch and translate to 401.

ParameterTypeDescription
tokenstringThe raw JWT access token string
oidcOidcConfigOIDC configuration (issuer_url, audience, etc.)

verifyApiKey(headers, apiKeyPort): Promise<AuthUser>

Verify an API key from headers and return an AuthUser.

Accepts two header formats:

  • Authorization: ApiKey <key>
  • Authorization: Bearer <key> (when the key starts with "pbk_")
ParameterTypeDescription
headersHeaderProviderHeader provider (HTTP or NATS™)
apiKeyPortApiKeyPortPort for looking up API keys by hash

verifyAuth(headers, config, ports): Promise<AuthUser>

Verify auth in STANDALONE mode (BE only). Needs AuthPorts (UserResolverPort + RoleMappingPort).

ParameterTypeDescription
headersHeaderProvider
configAuthConfig
portsAuthPorts

verifyAuthGatewayResolved(headers, config): Promise<AuthUser>

Verify auth in GATEWAY-RESOLVED mode (microservices). NO ports needed. Just verifies gateway secret + deserializes AuthUser from headers.

ParameterTypeDescription
headersHeaderProvider
configAuthConfig

verifyHttpRequest(req, config, ports): Promise<AuthUser>

Verify auth from an HTTP request.

ParameterTypeDescription
reqIncomingMessageRaw Node.js IncomingMessage (or Express Request which extends it)
configAuthConfigAuth configuration
portsAuthPortsAuth ports (UserResolverPort + RoleMappingPort). Required for STANDALONE mode (BE). Omit for GATEWAY-RESOLVED mode (microservices).

verifyNatsMessage(msg, config): Promise<AuthUser>

Verify auth from a NATS message (microservice subscriber side). GATEWAY-RESOLVED mode — no ports needed.

ParameterTypeDescription
msgMsg
configAuthConfig

<!-- END -->

Last modified on July 26, 2026
Presence
On this page
  • Classes
    • AuthError
    • ConfigLoader
    • GracefulShutdown
    • HealthCheck
    • HttpHeaderProvider
    • NatsClient
    • NatsHeaderProvider
    • RbacDeniedError
    • ServiceRegistrar
  • Interfaces
    • ApiKeyPort
    • ApiKeyRecord
    • ApplyPatchesResult
    • AuthConfig
    • AuthConfigPort
    • AuthPorts
    • ConfigRepositoryPort
    • DatabasePort
    • EnvSchema
    • EnvValidationResult
    • GatewayConfig
    • HeaderProvider
    • HealthCheckPort
    • HealthCheckResult
    • HttpServerOptions
    • IConfigEntity
    • IServiceRegistry
    • NormalizedIdpUser
    • OidcConfig
    • RbacResult
    • ResolveInput
    • RoleMappingEntry
    • RoleMappingPort
    • ServiceHealthCheck
    • ServiceHeartbeatPayload
    • ServiceRegisterPayload
    • ServiceRegistrarConfig
    • ServiceRegistryPort
    • ServiceUnregisterPayload
    • Session
    • UserResolverPort
  • Types & Enums
    • AuthMode
    • AuthUser
    • CleanupFn
    • HealthCheckFn
    • JwtClaims
    • Permission
  • Constants
    • AuthMode
    • PATCH_REGISTRY_DDL
    • PATCH_REGISTRY_FQNAME
    • Permission
    • resetAuthConfigForTest
    • SERVICE_SUBJECTS
    • SYSTEM_ACTOR
  • Functions
    • applyPatches(patchesDir, db): Promise<ApplyPatchesResult>
    • buildAuthUser(internalUuid, normalized, permissions, isAdmin): AuthUser
    • buildNatsAuthHeaders(user, config): Record<string, string>
    • checkRbac(user, requiredPermissions, mode): RbacResult
    • coerceRoles(raw): string[]
    • createHttpServer(options): Promise<Server<typeof IncomingMessage, typeof ServerResponse>>
    • deserializeAuthUserFromHeaders(headers, config): AuthUser
    • enforceHttpRbac(user, requiredPermissions, mode): void
    • enforceNatsRbac(user, requiredPermissions, mode): void
    • expandPermissions(roles, getRoleMappingFn): Promise<{ patterns: string[]; isAdmin: boolean }>
    • extJsonMiddleware(): (req: Request, res: Response, next: NextFunction) => void
    • extJsonParse(text): T
    • extJsonStringify(data): string
    • generateApiKey(): { key: string; prefix: string }
    • getAuthConfig(): AuthConfig
    • getSession(): Session \| undefined
    • hashApiKey(key): string
    • initAuthConfig(p): void
    • invalidateAuthConfig(): void
    • isPatchBodyAlreadyRecorded(db, contentSha256): Promise<boolean>
    • isPermissionGranted(userPermissions, requiredPermission): boolean
    • isPermissionSentinel(p): boolean
    • loadAuthConfig(): Promise<AuthConfig>
    • matchesWildcard(pattern, permission): boolean
    • normalizeIdpToken(payload, rolesPath): NormalizedIdpUser
    • patchIdFromFilename(filename): string
    • requireActor(): string
    • requireEnv(schema): Record<string, string \| undefined>
    • resetOidcRuntimeForTest(): void
    • runAsSystem(fn): T
    • runWithSession(session, fn): T
    • serializeAuthUserToHeaders(user, config): Record<string, string>
    • sha256Hex(body): string
    • slugifyPatchSegment(s): string
    • utcTimestampForFilename(d): string
    • validateEnv(schema): EnvValidationResult
    • verifyAccessToken(token, oidc): Promise<JwtClaims>
    • verifyApiKey(headers, apiKeyPort): Promise<AuthUser>
    • verifyAuth(headers, config, ports): Promise<AuthUser>
    • verifyAuthGatewayResolved(headers, config): Promise<AuthUser>
    • verifyHttpRequest(req, config, ports): Promise<AuthUser>
    • verifyNatsMessage(msg, config): Promise<AuthUser>