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

© 2026 PrimeBrick. MIT License. v3.11.2

github
DAL Library
SDK Library
    Developer Guide
      OverviewGetting StartedAnatomy of a MicroserviceAuthenticationClient Shield & Rotating SecretsHTTP ServerRPC RouterEntity RouterOpenAPI GenerationDocs PackConfig tables & ConfigLoaderDatabase MigrationsTranslationsCurrency UtilitiesNATS™ ClientService RegistrationSSE StandardPresenceExt-JSONRedis cache layerTelemetry & LoggingAPI Reference
Decimal Library
powered by Zudoku
Developer Guide

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/dev-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

CacheKeyBuilder

Builds stable cache keys from entity class + result rows.

Key format: dal:{tableName}:{value} where tableName is the snake_case DB table name read via Reflect.getMetadata("primebrick:tableName", ctor). The DAL's @Entity decorator writes this metadata via Reflect.defineMetadata (standard JS reflection API — no import from the DAL needed). Falls back to ctor.name (JS class name) if the DAL is not used or the metadata is absent.

Key resolution order for a row (deterministic, dev-controlled, no magic):

  1. The property marked @CacheKey() → dal:{table}:{row[propertyKey]}
  2. Else row.uuid (JS property convention — the DAL returns rows with JS property keys, and the convention is the property is named uuid) → dal:{table}:{row.uuid}
  3. Else the @Key() column value, read via Reflect.getMetadata("primebrick:keyColumn", ctor) (written by the DAL's @Key decorator) → dal:{table}:{row[keyPropertyKey]}
  4. Else, if the class has NO @CacheKey() AND NO @Key() Reflect metadata at all → throw (genuine entity misconfiguration — the dev must add @CacheKey() or @Key()). This is the dev guardrail.
  5. Else (the class HAS a key source, but THIS row carries none of the key values — e.g. an aggregate/projection result like { cnt: 123n } from COUNT(*)) → return null → the wrapper skips port.set silently. Caching a non-entity- shaped row would be wrong anyway (no stable identity to key on); the row is returned to the caller unchanged.

The key is always derived from the RESULT ROW, never from the input argument of findById / findByUUID. This ensures findById(42) and findByUUID(<uuid>) on the same row produce the SAME cache key, avoiding duplicate entries and partial-invalidation bugs.

Why table name via Reflect and not class name: the table name is snake_case and matches the DB — more familiar when debugging Redis keys (dal:customers:<uuid> vs dal:CustomerEntity:<uuid>). Stable across class renames (the class can be renamed without breaking cache keys, as long as the table name stays). The Reflect API is standard JS — no import from the DAL, just a shared metadata-key convention.

CacheKeyBuilder.forEntity(entityClass): string

Build the entity prefix: dal:{tableName}:. Reads the snake_case table name from Reflect.getMetadata("primebrick:tableName", ctor) (written by the DAL's @Entity decorator). Falls back to ctor.name if absent.

ParameterTypeDescription
entityClass(args: any[]) => any

CacheKeyBuilder.forRowFromMeta(entityClass, row): string \| null

Build the cache key for a single result row.

Resolution order: @CacheKey() → row.uuid → @Key() column (via Reflect) → null (silent skip) if the class has a key source but the row lacks the value, or throw if the class has NO key source at all (genuine misconfiguration).

ParameterTypeDescription
entityClass(args: any[]) => any
rowRecord<string, unknown>

CachingApiKeyPort

CachingApiKeyPort(inner, cache)

ParameterTypeDescription
innerApiKeyPort
cacheApiKeyCacheSource

CachingApiKeyPort.findByHash(hash): Promise<ApiKeyRecord \| null>

Find an API key by its SHA-256 hash. Returns null if not found.

ParameterTypeDescription
hashstring

ClientShieldRegistry

ClientShieldRegistry.start(): Promise<void>

Subscribe to invalidation events first, then keep retrying the snapshot until the BE answers — the BE may be down at boot (same stateless-retry model as service.register). Non-blocking: until the first snapshot lands the allowlist is empty and every internal call is rejected (fail-closed).

ClientShieldRegistry.addChangeListener(cb)

Register a callback fired after a reload that actually changed row material — the service registrar hooks this to re-register when its own key_generation was bumped by a BE rotation.

ParameterTypeDescription
cb() => void

ClientShieldRegistry.generationFor(uaPrefix): number

The generation the BE issued for uaPrefix — a derived-mode service re-derives its key as HMAC(secret, prefix:generation). Returns 0 for unknown/static rows (matches the DB default).

ParameterTypeDescription
uaPrefixstring

ClientShieldRegistry.verifyKey(uaPrefix, presentedKey): boolean

Current-hash or grace-windowed previous-hash comparison for the matched prefix.

ParameterTypeDescription
uaPrefixstring
presentedKeystring

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.

ConfigValidationError

Error thrown when a config value fails validation. The error_label_key is an i18n translation key — the caller (BE router) passes it to the FE, which translates it using the current locale.

PropertyTypeDescription
error_label_keystringi18n translation key for the error message (e.g. "app.common.validation.invalidBigint").
rulestringWhich rule failed (min, max, url, email, regex, required, type).
config_keystringThe config key that failed validation (for context).

ConfigValidationError(error_label_key, rule, config_key, message)

ParameterTypeDescription
error_label_keystring
rulestring
config_keystring
messagestring

Decimal

DecimalError

Errors raised by @primebrick/decimal.

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. Hot-reload safe: a module reload creates a NEW GracefulShutdown whose cleanups reference the NEW service objects — the previous instance's listeners are removed via the process registry instead of stacking (observed: N reloads → N "shutting down" logs + N exit races on one signal).

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).

Produces a unified HealthResponse via toResponse() — used by both the BE (Express) and US (createHttpServer) /health endpoints.

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>

HealthCheck.toResponse(service, version, url): Promise<HealthResponse>

Build a unified HealthResponse from all checks. Used by both BE and US /health endpoints.

ParameterTypeDescription
servicestring
versionstring
urlstring

HttpHeaderProvider

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

HttpHeaderProvider(req)

ParameterTypeDescription
reqIncomingMessage

HttpHeaderProvider.getHeader(name): string \| undefined

ParameterTypeDescription
namestring

MfaConsumeUnavailableError

The BE consume route could not be reached — transient, NOT a denial.

MfaConsumeUnavailableError(cause)

ParameterTypeDescription
causeunknown

MfaStepUpRequiredError

The token is missing, invalid, expired, already used, bound to a different action/resource, or issued for a different user. All of these map to the same response contract: 403 + mfa_step_up_required so the FE re-runs the step-up dialog.

PropertyTypeDescription
internal_codestring
actionstring
target_resourcestring

MfaStepUpRequiredError(spec, code)

ParameterTypeDescription
specMfaStepUpSpec
codestring

NatsApiKeyPort

NatsApiKeyPort(timeoutMs)

ParameterTypeDescription
timeoutMsnumberNATS correlation timeout. Keep it short: this port
backs authentication prechecks — fail fast, fail closed.

NatsApiKeyPort.findByHash(hash): Promise<ApiKeyRecord \| null>

Find an API key by its SHA-256 hash. Returns null if not found.

ParameterTypeDescription
hashstring

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() and subscribe() 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.getServerVersion(): string \| null

Returns the NATS server version (from the INFO handshake), or null if the connection was never established or the version was not available.

NatsClient.getServerUrl(): string \| null

Returns the NATS server URL used for the connection, or null if the connection was never established.

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, opts): 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
opts{ queue: string }

NatsClient.ensureStream(name, subjects, opts): Promise<void>

Ensure a JetStream stream exists (idempotent). Creates it if missing; if it already exists and the requested options differ (e.g. the allow_msg_schedules feature flag was added later), the stream config is UPDATED in place — "ensure" means the declared state holds, not just that the name exists. Requires the server to run with JetStream enabled (nats -js).

ParameterTypeDescription
namestringStream name (e.g. "WEBHOOK")
subjectsstring[]Subjects bound to the stream (e.g. ["webhook.>"]). For
scheduled-message streams BOTH the schedule subjects AND the fire
targets must be covered (ADR-51).
opts{ allowMsgSchedules: boolean; syncSubjects: boolean }

NatsClient.jetstreamPublish(subject, data, hdrs): Promise<bigint>

Publish a message to JetStream and await the PubAck (durable write confirmed server-side). Same Ext-JSON serialization and header propagation as publish().

ParameterTypeDescription
subjectstring
dataunknown
hdrsRecord<string, string>

NatsClient.jetstreamSubscribe(opts): Promise<{ close: }>

Subscribe to a JetStream stream via a durable pull consumer (at-least-once). Creates the durable consumer if missing (idempotent), then loops fetch(): auto-ack() after the handler resolves, nak(5000) (redelivery) when it throws.

Shield model — the verify callback runs BEFORE the handler, same convention as NatsRouteVerify:

  • returns AuthUser → verified, handler receives authUser
  • returns undefined → verified, no user context (system lane)
  • returns null → denied: term() — auth failures are TERMINAL, redelivery would just re-deny (same policy as webhook-subscriber)
  • throws → transient: nak() redelivery applies

consumer tunes the durable: ackWaitMs is the redelivery delay (size it above the handler's worst case), maxAckPending caps in-flight messages (concurrency), maxDeliver bounds retries.

ParameterTypeDescription
opts{ stream: string; durable: string; filterSubject: string; consumer: { batch: number; fetchExpiresMs: number; ackWaitMs: number; maxDeliver: number; maxAckPending: number }; verify: (msg: { subject: string; headers: MsgHdrs }) => Promise<AuthUser | null | undefined>; handler: (data: T, msg: { subject: string; headers: MsgHdrs; info: { redeliveryCount: number }; authUser: AuthUser }) => Promise<void> }

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

RedisCachePort

Cache port interface. Implementations MUST be best-effort safe: the withCache wrapper already wraps every call in try/catch, but implementations should not throw on common conditions (key not found → return null, not throw).

RedisCachePort(redis)

ParameterTypeDescription
redisRedisClientType

RedisCachePort.get(key): Promise<T \| null>

Read a value from the cache.

ParameterTypeDescription
keystring

RedisCachePort.set(key, value, ttl): Promise<void>

Write a value to the cache.

ParameterTypeDescription
keystring
valueT
ttlnumberTime-to-live in milliseconds. Omit for no expiry — use this ONLY for
genuinely immutable data (the cached value can never change). For mutable data,
pick a TTL that bounds the staleness window if Redis is intermittently unavailable
during invalidation. Recommended starting point for mutable data: 300_000 (5 min).
There is NO implicit default — omitting ttl means "no TTL, immutable".

RedisCachePort.del(key): Promise<void>

Delete a single key. No-op if the key does not exist.

ParameterTypeDescription
keystring

RedisCachePort.delByPrefix(prefix): Promise<void>

Delete all keys matching a prefix. Used for entity-level invalidation (e.g. dal:customers: evicts every cached row of the customers entity). Implementations should use SCAN + DEL (not KEYS) to avoid blocking Redis.

ParameterTypeDescription
prefixstring

RedisCachePort.ping(): Promise<boolean>

Check if the cache backend is reachable and responsive. Used by the /health endpoint. Returns true if the backend responds to a ping/echo command within the timeout.

Implementations should NOT throw — return false on any error.

RedisPresencePort

RedisPresencePort(redis, ttlMs)

ParameterTypeDescription
redisRedisClientType
ttlMsnumber

RedisPresencePort.upsertReading(entityType, entityUuid, entry): Promise<void>

Upsert a READING entry for a user on an entity. Refreshes the TTL on the user hash and the tabs set. If the user was previously EDITING, their status becomes READING and the field/value are cleared.

ParameterTypeDescription
entityTypestring
entityUuidstring
entryPresenceEntry

RedisPresencePort.upsertEditing(entityType, entityUuid, entry): Promise<void>

Upsert an EDITING entry for a user on an entity. Refreshes the TTL on the user hash, the editors hash, and the tabs set. The field and value from the entry are stored.

ParameterTypeDescription
entityTypestring
entityUuidstring
entryPresenceEntry

RedisPresencePort.remove(entityType, entityUuid, userUuid): Promise<void>

Remove a user entirely from an entity's presence (LEAVE). Removes from the users hash, the editors hash, and deletes the tabs set. No-op if the user was not present.

ParameterTypeDescription
entityTypestring
entityUuidstring
userUuidstring

RedisPresencePort.heartbeat(entityType, entityUuid, userUuid, sessionId): Promise<void>

Refresh the TTL for a user's presence on an entity (HEARTBEAT). Updates last_seen_at on the entry. No-op if the user is not present (heartbeat is only sent by tabs that previously sent READING/EDITING).

ParameterTypeDescription
entityTypestring
entityUuidstring
userUuidstring
sessionIdstring

RedisPresencePort.getSnapshot(entityType, entityUuid): Promise<PresenceSnapshot>

Build the full snapshot for an entity: readers, editors, changed marker. Called on SSE connect (snapshot event) and on GET presence.

ParameterTypeDescription
entityTypestring
entityUuidstring

RedisPresencePort.setChanged(marker, ttlMs): Promise<void>

Set the "changed" marker for an entity (server-side save hook). Overwrites any existing marker. TTL is ttlMs (default 300000 = 5 min).

ParameterTypeDescription
markerEntityChangedMarker
ttlMsnumber

RedisPresencePort.clearChanged(entityType, entityUuid): Promise<void>

Clear the "changed" marker for an entity (e.g. on explicit dismiss). No-op if no marker exists.

ParameterTypeDescription
entityTypestring
entityUuidstring

RotatingSecretEngine

RotatingSecretEngine(redis, opts)

ParameterTypeDescription
redisRedisClientType
opts{ rotationMs: number; graceMs: number }

RotatingSecretEngine.get(ref): Promise<ClientShieldSecretSlot>

Read the slot; generate on cold start (TTL-expired or first boot).

ParameterTypeDescription
refRotatingSecretRef

RotatingSecretEngine.rotate(ref): Promise<ClientShieldSecretSlot>

Rotate ref: previous := current, current := fresh random. Serialized by a NX lock so concurrent owners (BE replicas, get-vs-timer races) rotate once.

ParameterTypeDescription
refRotatingSecretRef

RotatingSecretEngine.startAutoRotation(ref)

Interval driver — ensure existence now, then rotate every rotationMs. Idempotent per ref: a lifecycle re-run (e.g. after a NATS outage) must not stack a second interval on the same rotation.

ParameterTypeDescription
refRotatingSecretRef

SecretProvider

SecretProvider.get(ref): Promise<ClientShieldSecretSlot>

ParameterTypeDescription
refRotatingSecretRef

SecretProvider.invalidate(scope, name)

ParameterTypeDescription
scopestring
namestring

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.onRegistryChanged(currentGeneration)

Derived-key mode (B14): invoked by the client-shield registry after a reload that changed row material. currentGeneration is the generation the BE snapshot now advertises for this service — when the BE bumped it (rotate endpoint), re-register with the newly derived key.

ParameterTypeDescription
currentGenerationnumber

ServiceRegistrar.sendHeartbeat(): Promise<void>

ServiceRegistrar.unregister(): Promise<void>

ServiceRegistrar.startHeartbeat(): Timeout

Start the heartbeat loop. The first heartbeat is sent immediately — the register ack already proved the BE is listening, so there's no reason to wait a full interval before the BE sees fresh health data.

ServiceRegistrar.stopHeartbeat()

TranslationsCache

Translations module — framework-agnostic i18n service + cache helpers.

DB-agnostic: does NOT import from @primebrick/dal-pg. The BE resolves entity metadata (schema, qualified table name) and passes it to the service methods.

TranslationsCache(port, schema)

ParameterTypeDescription
portCachePort | null
schemastring

TranslationsCache.getI18nDict(language): Promise<CacheEntry<I18nDict> \| null>

ParameterTypeDescription
languagestring

TranslationsCache.setI18nDict(language, dict): Promise<void>

ParameterTypeDescription
languagestring
dictI18nDict

TranslationsCache.invalidate(language): Promise<void>

ParameterTypeDescription
languagestring

TranslationsService

TranslationsService(opts)

ParameterTypeDescription
optsTranslationsServiceOptions

TranslationsService.getI18nDict(entity, schema, qualifiedTable, language): Promise<I18nDict>

Get the flat i18n dict for a language from a specific module's schema. PG builds the dict natively via jsonb_object_agg — no Node-side post-processing. Cache-first; falls back to DB on miss.

ParameterTypeDescription
entityunknownThe DAL entity class (used by the Repository for CRUD)
schemastringThe schema name (e.g. "public", "system") — for cache keying
qualifiedTablestringThe schema-qualified table name (e.g. "public"."translations")
— resolved by the BE from getQualifiedTableName(entity)
languagestringBCP 47 language tag (e.g. "it-IT")

TranslationsService.list(entity, schema, query): Promise<{ rows: TranslationRow[]; total: bigint }>

ParameterTypeDescription
entityunknown
schemastring
query{ page: number; page_size: number; language: string }

TranslationsService.create(entity, schema, data, actor): Promise<TranslationRow>

ParameterTypeDescription
entityunknown
schemastring
data{ key: string; language: string; value: string }
actorstring

TranslationsService.update(entity, schema, uuid, data, actor): Promise<TranslationRow>

ParameterTypeDescription
entityunknown
schemastring
uuidstring
data{ key: string; language: string; value: string }
actorstring

TranslationsService.softDelete(entity, schema, uuid, actor): Promise<void>

ParameterTypeDescription
entityunknown
schemastring
uuidstring
actorstring

TranslationsService.restore(entity, schema, uuid, actor): Promise<void>

ParameterTypeDescription
entityunknown
schemastring
uuidstring
actorstring

ValidationError

ValidationError(message)

ParameterTypeDescription
messagestring

Interfaces

AdvancedFilter

One advanced-filter entry (filters sheet / filters[i][...] URL params).

FieldTypeDescription
idstring
fieldstring
operatorFilterOperator
valueunknown

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 BE-owned public schema — only the BE may read it directly (DomainApiKeyPort); microservices resolve through the governed auth.apikey.byHash NATS route (NatsApiKeyPort). Both are wrapped by CachingApiKeyPort for the shared Redis/in-memory cache.

FieldTypeDescription
uuidstring
namestring
permissionsstring[]
is_systemboolean
is_activeboolean
expires_atDate | null

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
idp_endpointstring
idp_organizationstring
enable_email_verification_checkboolean
enable_webauthnbooleanWhether WebAuthn / passkey passwordless authentication is enabled.
When false, the BE WebAuthn endpoints return 503 and the FE hides the
passkey button. Parsed from the "true"/"false" string in the DB.
passkey_requiredbooleanWhether passkey enrollment is mandatory. When true, the FE passkey prompt
cannot be dismissed and the "do not show again" checkbox is hidden.
Parsed from the "true"/"false" string in the DB.
enable_mfabooleanWhether MFA / 2FA is enabled (login MFA + step-up MFA).
When false, login never branches to MFA and the step-up middleware
passes through. Parsed from the "true"/"false" string in the DB.
redis_urlstringRedis cache URL (e.g. redis://localhost:6379 or rediss://host:6380 for TLS).
Empty string or undefined = cache disabled — the system runs without cache, with
warn logs. Cache is a feature, not a requirement; the system is fully valid without
it. Read from the auth_configurations table by the BE's initCache() at startup.

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.

BuildDocsPackOptions

FieldTypeDescription
repostringRepo/service code written into the manifest (KB ingest key).
docsDirstringSource docs directory (e.g. "docs/user-guide") — copied verbatim.
Optional: dev-only services may ship an openapi-only pack.
outDirstringOutput dir (e.g. "dist/docs-pack"). Recreated each build.
generatorsGenerateDocsFn[]Metadata-derived doc generators — run after the raw copy.
openapiPathstringOptional OpenAPI spec source — copied to outDir/openapi.json.
openapiRecord<string, unknown>Optional OpenAPI spec object — serialized to outDir/openapi.json.
Preferred over openapiPath: services generate their spec at module
load via buildOpenApiSpec/entityCrudSpec, so the pack always
carries the runtime spec with zero file plumbing.

CacheableRepository

Structural interface — a DAL Repository satisfies this without any import from the DAL. TypeScript structural typing: if it has these methods with compatible signatures, it's assignable. No extends, no import type from @primebrick/dal-pg.

The signatures are intentionally loose (any for entity class and options) because the DAL's Repository uses generics that we cannot (and should not) replicate here without importing the DAL's types. The wrapper preserves the runtime behavior; type safety on the entity class is the caller's responsibility (the BE passes a real Repository).

CacheEntry

Redis cache entry — wraps the cached value with its ETag.

FieldTypeDescription
dataT
etagstring
cached_atnumberEpoch ms (debugging only — not used for TTL).

CacheLogger

Logger port — matches the DAL's LoggerPort shape (structural typing, no import).

CachePort

Cache port interface. Implementations MUST be best-effort safe: the withCache wrapper already wraps every call in try/catch, but implementations should not throw on common conditions (key not found → return null, not throw).

CacheResponseMeta

HTTP response metadata for cached endpoints.

FieldTypeDescription
etagstring
cachedbooleanTrue if the data came from Redis cache (vs fresh DB read).

ClientKeyMaterial

Row-shaped key material for verification (subset of system.client_shield_registry used by both the SDK consumer cache and the BE publisher-identity verifier).

FieldTypeDescription
client_key_hashstring
previous_key_hashstring | null
previous_valid_untilstring | Date | null

ClientShieldRegistryRow

FieldTypeDescription
ua_prefixstring
client_key_hashstring
is_enabledboolean
key_generationnumberKey-rotation state (B14). key_generation is the BE-issued generation a
derived-mode service must use to re-derive its key; previous_* carries
the old hash during the grace window.
previous_key_hashstring | null
previous_valid_untilstring | Date | nullISO string on the wire; Date when the BE passes rows in-process.

ClientShieldSecretSlot

FieldTypeDescription
currentstring
previousstring | null
rotated_atnumberEpoch ms of the last rotation.

CollaborationMeta

Collaboration fragment injected by assembleMeta at serve time.

FieldTypeDescription
enabledboolean
expose_editing_valueboolean

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).

ConfigTypeMoneyConfig

type_config shape for money config type. The amount is stored in the value column (as a TEXT string); the currency and optional allowed-currencies list live in type_config JSON.

FieldTypeDescription
currencystringISO 4217 currency code, e.g. "EUR", "USD". Set by user, stored in type_config.
allowed_currenciesstring[]Optional: restrict selectable currencies. If absent, all ISO 4217 codes are allowed.

ConfigTypePhoneConfig

type_config shape for phone config type. The phone number is stored in the value column (E.164 format); the country code and optional allowed-countries list live in type_config JSON.

FieldTypeDescription
countrystringISO 3166-1 alpha-2 country code, e.g. "IT", "US". Drives formatting and validation.
allowed_countriesstring[]Optional: restrict selectable countries. If absent, all countries are allowed.

ConfigTypeUrlConfig

type_config shape for url config type. The URL is stored in the value column; the default protocol and allowed-protocols list live in type_config JSON.

FieldTypeDescription
default_protocolstringDefault protocol prepended when the user doesn't type one. e.g. "https".
allowed_protocolsstring[]Allowed URL protocols, e.g. ["http", "https", "redis", "rediss", "tcp"].

ConfigValidation

Validation rules for a config row, stored inside type_config JSON under the validation key. This is the shared schema used by SDK consumers (BE, microservices) and the FE JSON-to-Zod builder.

Example type_config JSON:

Code
{ "values": { ... }, "validation": { "required": true, "rules": { "min": { "value": 1, "error_label_key": "config.auth.x.errors.min" }, "max": { "value": 90, "error_label_key": "config.auth.x.errors.max" } } } }
FieldTypeDescription
requiredbooleanIf true, empty/null values are rejected (except secrets with empty = "leave unchanged").
required_error_label_keystringOptional i18n key for the required error message. Falls back to "app.common.validation.required".
unsignedbooleanIf true, numeric values (bigint/number/money) are treated as unsigned:
  • Sign characters (-, +) are rejected at the type-validation level.
  • The effective minimum defaults to 0 when no explicit rules.min is set. Absent or false → signed (default, backward compatible). Only unsigned: true activates unsigned behavior. | | rules | ConfigValidationRules | Map of rule type → rule config. Each rule has its own error_label_key for i18n. |

ConfigValidationRules

FieldTypeDescription
minValidationRuleMinMinimum value (for bigint/number/money) or minimum length (for string/secret/url/email/phone).
maxValidationRuleMaxMaximum value (for bigint/number/money) or maximum length (for string/secret/url/email/phone).
urlValidationRuleUrlURL protocol validation.
emailValidationRuleEmailEmail format validation.
regexValidationRuleRegexRegex pattern validation (for string/text/secret/url/email/phone).

CurrencyInfo

ISO 4217 currency metadata.

FieldTypeDescription
codestring
namestring
symbolstring
symbolNativestring
numericstring
decimalsnumber

DbMigrationBootOptions

FieldTypeDescription
engineDbMigrationEnginePortdal-pg migrations surface — import * as m from "@primebrick/dal-pg" satisfies this.
pool() => unknownLive pg pool — evaluated lazily right after getDal() ran.
schema() => stringOwning service schema (e.g. env.DATABASE_SCHEMA) — registry lives there.
patchesDirstringDirectory containing the .sql patch files (usually <svc>/db-meta/patches).
dbMetaDirstringdb-meta dir with MANIFEST.json + keys.json — defaults to dirname(patchesDir).
allowedTrusttypeOperatorManifest signature trust levels accepted by this deploy. Default ["release"].
entityRegistrytypeOperatorEntity registry of this service — enables the post-apply drift check.

DbMigrationEnginePort

Structural port satisfied by @primebrick/dal-pg migrations exports (import * as dalMigrations from "@primebrick/dal-pg" → engine: dalMigrations). Params are any at the boundary: strictFunctionTypes contravariance would otherwise reject the dal signatures, and the concrete types cannot be named here anyway (SDK must not import dal-pg).

DocsPackContentFile

One file of pack content — wire format.

FieldTypeDescription
pathstringPath relative to the pack's docs/ dir (e.g. "dev-guide/index.mdx").
contentstringUTF-8 file content — the KB's source of truth.

DocsPackFile

FieldTypeDescription
pathstring
sha256stringsha256 hex of the file content — incremental ingest key.
bytesnumber

DocsPackManifest

FieldTypeDescription
schema1
repostringOwning repo/service code — the KB ingest key (e.g. "ai", "be").
generated_atstring
filesDocsPackFile[]Doc-relative paths (e.g. "dev-guide/index.mdx") with content hashes.
has_openapibooleanPresent when the pack carries an openapi.json at root.

DocsPackPayload

FieldTypeDescription
manifestDocsPackManifest
filesDocsPackContentFile[]
openapistringRaw openapi.json content, when the pack carries one.

EnsureJobScheduleOpts

FieldTypeDescription
scheduleSubjectstringUnique schedule subject — jobs.schedule.<svc>.<name>[.<entity-id>].
One schedule per subject; republishing replaces it atomically.
schedulestring"@every 30s"
targetstringSubject the stored message is republished to at fire time —
jobs.due.user.* or jobs.due.system.*. Must be covered by the same
stream's subjects.
payloadunknownPayload stored on the schedule message — fired verbatim unless
source is set. Serialized via extJson by jetstreamPublish.
sourcestringFire-time indirection (ADR-51): at fire time the scheduler reads the
LATEST message on this subject and fires THAT as the payload — the
schedule declaration never holds business state.
ttlstringNats-Schedule-TTL — auto-purge the schedule after this duration.
forwardHeadersRecord<string, string>Extra headers stamped on the declaration and forwarded VERBATIM to the
fired message. For jobs.due.user.* targets pass the triggering
message's auth headers via forwardAuthHeaders() so the executor can
run the full L2+RBAC chain on the ORIGINATING user.

EntityAction

One entry of meta.actions — the derived capability contract. EVERY standard op is present; enabled: false means the op is unavailable for everyone (route missing or product-disabled).

FieldTypeDescription
opstringOperation name — canonical (list, get, delete.bulk, ...) or the
last path segment for non-standard routes (change_password).
permissionsstring[]Declared non-sentinel permissions, verbatim (OR semantics).
sentinelEntityActionSentinelSentinel gate when the route declares one instead of real perms.
enabledbooleanVisibility flag — false hides the CTA for everyone.
endpointEntityActionEndpointThe registered route that backs this op — present on every enabled
action, absent when no route exists (enabled: false). Consumers
dispatch writes to the declared endpoint instead of recomputing the
/api/v1/entities/{entity} convention — required for ops living under
non-standard prefixes (e.g. /api/v1/auth/users).

EntityActionEndpoint

Backing route of an EntityAction — emitted by the BE route scan.

FieldTypeDescription
methodstringHTTP method of the backing route (GET, POST, PUT, DELETE, ...).
pathstringFull registered API path with :param placeholders verbatim
(e.g. /api/v1/auth/users/:uuid/restore). Consumers substitute
placeholders from the row (:uuid ← meta.uid).

EntityActionMeta

FieldTypeDescription
opstring
permissionsstring[]
sentinelstring
enabledboolean

EntityChangedMarker

Marker published server-side when an auditable entity is saved. Stored in Redis with a 5-minute TTL so late-joining clients can see the most recent change without scanning the audit log.

FieldTypeDescription
entity_typestring
entity_uuidstring
versionnumberThe new version after the save.
audit_log_idnumberThe audit_log_id of the change (for the GET audit/ diff endpoint).
changed_bystringuser_uuid of the writer.
changed_atnumberEpoch ms of the save.

EntityMeta

Static entity meta — the shape every *.meta.ts file MUST satisfy. The schema is CLOSED for authored metas: undeclared properties are compile errors, so new meta props require extending this type first.

FieldTypeDescription
entitystringEntity name, snake_case singular (e.g. user_profile).
translation_keystringi18n prefix, snake_case singular — used to build dynamic keys.
entities_nsstringNamespace OWNING this entity's label keys — the module's own namespace
(job → job.entities.<translation_key>.*). Absent → system
(BE/core-owned entities keep system.entities.*).
This is a READ-side reference declared by the owning module — the
namespace write boundary (see translation-keys.ts) guarantees the
keys actually live in that module's <code>.translations schema.
title_keystringOptional i18n key overriding the canonical page title.
When absent, list/create/edit titles are built from the generic
system.entities.page_title.{list|create|edit} templates interpolated
with system.entities.<translation_key>.{plural|singular}.
Set only for custom pages needing a bespoke title — CRUD entity metas
must not declare it.
uidstringField key holding the entity primary key (usually uuid).
display_fieldstringCanonical display field — the column key identifying the entity's
human-readable label. Drives first-column/sticky table identity and
default display resolution.
display_namestringDisplay expression template (${field} syntax, dot-paths allowed).
OPTIONAL here: assembleMeta materializes it as
"${" + display_field + "}" when absent — it is always present in the
served response. Used automatically for edit-page titles; reusable for
comboboxes, lists, chips.
actions_overridesActionsOverridesPer-op enable overrides — may ONLY toggle enabled on route-backed
ops; a missing route already produces enabled: false.
columnsMetaColumn[]Entity field dictionary (root-level, transversal).
tableTableMetaTable options — omit when the entity has no table page
(e.g. a self-service form meta).

EntityRouteDecl

Entity declaration for makeServiceRoutes. spec only shapes the generated OpenAPI block — the router ignores it. Operation set is derived from permissions/meta/duplicate, never restated.

FieldTypeDescription
entityNamestringEntity URL segment, e.g. "ai_model" → /api/v1/entities/ai_model/*
serviceEntityServiceLike<TEntity>
permissionsPartial<Record<EntityAction, typeOperator>>Permission per action — presence registers the route.
metaRecord<string, unknown> & { display_field: string; actions_overrides: Record<string, { enabled: boolean }> }Static entity meta — served by GET /meta with derived
display_name + actions + collaboration. When omitted, the meta
route is not registered even if permissions.meta is set.
auditablebooleanThe entity is auditable (@AuditTrail) — drives collaboration.enabled
in the meta response (the BE derives it from entity persistence meta;
the SDK cannot inspect decorators, so the caller declares it).
schemas{ listQuery: (query: Record<string, unknown>) => Record<string, unknown>; createBody: (body: unknown) => { entity: Record<string, unknown> }; updateBody: (body: unknown) => { entity: Record<string, unknown> }; auditQuery: (query: Record<string, unknown>) => { page: number; limit: number }; duplicateBody: (body: unknown) => { uuids: string[] } }Validators — plain (raw) => T functions. listQuery/auditQuery
receive the parsed bracket-notation query object; createBody/
updateBody/duplicateBody receive the parsed JSON body and MUST
validate the {entity} envelope (strict — reject translations).
Throw ValidationError (or anything — it is wrapped) on failure.
duplicatefalseduplicate is ON by default (BE E.8): registers with the conventional
<entity>.duplicate.bulk permission when not declared. false
disables the route entirely.
guardsPartial<Record<EntityAction, (ctx: RpcRouteContext) => void | Promise<void>>>Async per-action guards invoked before the service call — the US
replacement for Express action middlewares (MFA step-up, B9).
spec{ tag: string; label: string; listResponse: JsonSchema }

EntityRouterConfig

FieldTypeDescription
entityNamestringEntity URL segment, e.g. "ai_model" → /api/v1/entities/ai_model/*
serviceEntityServiceLike<TEntity>
permissionsPartial<Record<EntityAction, typeOperator>>Permission per action — presence registers the route.
metaRecord<string, unknown> & { display_field: string; actions_overrides: Record<string, { enabled: boolean }> }Static entity meta — served by GET /meta with derived
display_name + actions + collaboration. When omitted, the meta
route is not registered even if permissions.meta is set.
auditablebooleanThe entity is auditable (@AuditTrail) — drives collaboration.enabled
in the meta response (the BE derives it from entity persistence meta;
the SDK cannot inspect decorators, so the caller declares it).
schemas{ listQuery: (query: Record<string, unknown>) => Record<string, unknown>; createBody: (body: unknown) => { entity: Record<string, unknown> }; updateBody: (body: unknown) => { entity: Record<string, unknown> }; auditQuery: (query: Record<string, unknown>) => { page: number; limit: number }; duplicateBody: (body: unknown) => { uuids: string[] } }Validators — plain (raw) => T functions. listQuery/auditQuery
receive the parsed bracket-notation query object; createBody/
updateBody/duplicateBody receive the parsed JSON body and MUST
validate the {entity} envelope (strict — reject translations).
Throw ValidationError (or anything — it is wrapped) on failure.
duplicatefalseduplicate is ON by default (BE E.8): registers with the conventional
<entity>.duplicate.bulk permission when not declared. false
disables the route entirely.
guardsPartial<Record<EntityAction, (ctx: RpcRouteContext) => void | Promise<void>>>Async per-action guards invoked before the service call — the US
replacement for Express action middlewares (MFA step-up, B9).

EntityServiceLike

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 }

GeneratedDoc

A generated doc emitted by a repo's extractor.

FieldTypeDescription
pathstringPath relative to the pack root (e.g. "generated/entity-field-reference.mdx").
contentstring

GenerateDocsContext

Context handed to each generator — repos read their own metadata.

FieldTypeDescription
repostringRepo/service code as declared in the manifest.
outDirstringAbsolute path of the pack output dir (already contains raw docs).

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

A single health check result. ok is mandatory; other fields are optional metadata (version, type, error message).

FieldTypeDescription
okboolean
versionstringServer version (e.g. "18.0" for PostgreSQL, "8.8.0" for Redis).
typestringService type (e.g. "Casdoor" for IDP).
errorstringError message if ok is false.

HealthResponse

The unified health response returned by /health on both BE and US.

FieldTypeDescription
okbooleantrue only if ALL checks pass.
servicestringService identifier (e.g. "primebrick-api", "emailsender").
versionstringService version (from package.json).
urlstringBase URL the service is listening on (if applicable).
checksRecord<string, HealthCheckResult>Map of check name → result. Keys are service-specific (db, redis, nats, idp).

HttpServerOptions

FieldTypeDescription
portnumber
healthCheckHealthCheck
serviceNamestring
serviceVersionstringService version (from package.json) — included in /health response.
serviceUrlstringBase URL the service is listening on — included in /health response.
routeHandler(req: IncomingMessage, res: ServerResponse, url: URL) => Promise<boolean>Custom route handler — receives req/res, returns true if handled.
clientShieldRegistryClientShieldRegistryClient-identity gate (B11): when set, every non-/health request must
carry a registry-listed User-Agent prefix + valid
x-primebrick-client-shield-key before the route handler runs.
identityExemptPathsRegExp[]URL paths exempt from the client-identity gate — for services that
expose a genuinely public surface (e.g. the webhook ingress, whose
callers are external providers that cannot carry our identity headers).
/health is always public and does not need listing.

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.
typeConfigTypeConfig value type — drives SDK coercion and FE widget selection. See ConfigType.
type_configstring | nullJSONB-text extra per-type configuration (e.g. badge inline values, select API URL).
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).
reservedbooleanIf true, the row is system-critical: editable but not deletable.
group_keystring | nullOptional grouping key for UI display. null/empty = ungrouped (top of list).

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"

ListFilterParamsOptions

FieldTypeDescription
filterValuesRecord<string, unknown>Toolbar quick-filter values keyed by column key.
columnstypeOperatorEntity columns (needed to map filterValues to the right operator).
advancedFilterstypeOperatorFiltersPanel conditions.
connectorstringGlobal connector for advanced filters (AND

ListResponse

The paged list response returned by every entity list endpoint. total is a bigint — parse JSON responses with a bigint-aware parser (extJsonParse from @primebrick/sdk), never JSON.parse.

FieldTypeDescription
rowsTRow[]
pagenumber
page_sizenumber
totalbigint

LogMeta

FieldTypeDescription
tagsstring[]Reserved key: textual tags rendered as [tag1] [tag2] right before the
message, in amber — visually distinct from level colors, timestamp and
the [service#version] tag. The message itself must carry ONLY the
message; subsystem labels belong here. In json format they appear as
a structured tags array.

MappedDalError

FieldTypeDescription
statusnumber
bodyRfc7807Body

MfaStepUpSpec

Route declaration: which action/resource the token must authorize.

FieldTypeDescription
actionstring
target_resourcestring

MicroserviceContext

FieldTypeDescription
envRecord<string, string | undefined>Validated environment variables (from requireEnv).
configLoaderConfigLoaderConfigLoader instance (config loaded from DB).
natsClienttypeof NatsClientNatsClient singleton (connected).
registrarServiceRegistrarServiceRegistrar instance (registered + heartbeat started).
healthCheckHealthCheckHealthCheck instance (wired with DB + custom checks).
serverServerHTTP server instance (listening on configured port).
shutdownGracefulShutdownGracefulShutdown instance (cleanup handlers installed).
authConfigAuthConfigAuth config (loaded from DB via AuthConfigPort).
apiKeyPortApiKeyPortAPI key port (if provided).
serviceVersionstringService version (from package.json or option).
baseUrlstringBase URL (from service_base_url config key).

MicroserviceOptions

FieldTypeDescription
serviceNamestringService name for logging and health endpoint (e.g. "emailsender").
serviceDisplayNamestringHuman-readable name shown in service registry (e.g. "Email Sender").
serviceDescriptionstringService description for service registry.
iconstringIcon name for service registry (e.g. "mail").
iconType"url" | "svg" | "base64" | "icon"Icon type for service registry.
isBehindScalerbooleanWhether the service is behind a scaler (default false).
serviceVersionstringService version — if omitted, reads from package.json in CWD.
envSchemaEnvSchemaEnvironment variable schema — validated via requireEnv.
initDal() => voidDAL init callback — called first, before everything else.
The SDK is DB-agnostic so the consumer provides this.
Typically reads DATABASE_URL / DB_SCHEMA from env and calls getDal().
dbBanner() => Promise<{ name: string; version: string | null; url: string }>Optional DB startup banner — called right after initDal, rendered
via logModuleStartup (PostgreSQL 18.4 connected (url)). Keeps the
SDK DB-agnostic: the service supplies a probe, e.g.
dbBanner: () => pgServerBanner(getDal().getPool(), process.env.DATABASE_URL!).
dalClose() => Promise<void>DAL close callback — called during graceful shutdown.
The SDK is DB-agnostic so the consumer provides this.
Typically async () => { await getDal().close(); }.
configRepositoryAdapter() => ConfigRepositoryPortFactory for ConfigRepositoryPort — called after initDal.
Reads config rows from the microservice's config table.
authConfigPort(configLoader: ConfigLoader) => AuthConfigPortFactory for AuthConfigPort — called after ConfigLoader is created.
Receives the ConfigLoader so it can read auth-related config keys.
apiKeyPort() => ApiKeyPortFactory for ApiKeyPort — called after auth config is loaded.
Optional — only needed if the microservice uses API key auth (e.g. webhooks).
Standard wiring: new CachingApiKeyPort(new NatsApiKeyPort(3000), () => getSdkCachePort())
— microservices NEVER read public.api_keys directly; resolution goes
through the governed BE route auth.apikey.byHash. The factory runs
before the SDK cache port is set, so pass getSdkCachePort lazily.
healthCheckPort() => HealthCheckPortFactory for HealthCheckPort — called after initDal.
Adapts the DB pool to the SDK's HealthCheckPort interface.
routeHandler(req: IncomingMessage, res: ServerResponse, url: URL) => Promise<boolean>Custom route handler — MUST be produced by an approved factory
(makeRpcRouter, makeOpenApiHandler, composeRouteHandlers).
Arbitrary handlers are rejected at startup: the middleware chain
(auth → RBAC → validation → RFC 7807) is not optional.
If omitted, only the /health endpoint is served.
docsPackDirstringOverride the docs-pack dir served/declared by this service.
Default: <service cwd>/dist/docs-pack — produced by buildDocsPack
in the service's build script.
identityExemptPathsRegExp[]Paths exempt from the client-identity gate — only for services with a
genuinely public surface (e.g. the webhook ingress, whose callers are
external providers). Everything else is gated by default.
authDependencySetters(cfg: AuthConfig, apiKeyPort: ApiKeyPort) => void[]Auth dependency setters — called after auth config is loaded.
Each setter receives (authConfig, apiKeyPort?) and typically wires
auth dependencies into route handlers (e.g. setAuthDependencies(cfg, apiKeyPort)).
endpointsRecord<string, string>Service-specific endpoints (relative paths, e.g. { webhook: "/webhook" }).
The health endpoint is added automatically.
Full URLs are built as ${baseUrl}${path} for the service registry.
navModuleNavDeclarationModule navigation self-declaration — the wiring that lets THIS module
own its FE pages. Persisted verbatim in service_registry.endpoints.nav
via service.register; the BE merges it into /modules/:code/meta,
/modules (route_prefixes) and the routes census. Declaring pages
inside another module's static nav registry is the anti-pattern this
replaces: each module presents its own nav.

route_prefixes resolve the active module (longest match wins — a page under a shared prefix like /system/jobs belongs to THIS module, not to the module owning /system). component_url route fields are self-described by the service (e.g. /ws/<code>/api/v1/module-assets/…). | | partOf | string | ROOT LEVEL this module is part of (service_registry.part_of) — 'system' for platform services, 'crm' for the CRM domain, … Combined with the service code it derives the module's FE route prefix /{part_of}/{code}; the BE rejects registration whose declared route_prefixes/nav/routes live outside it. No default — a module states its level explicitly. | | isDefault | boolean | DEFAULT-MODULE claim for the module's root level — true declares this service as the landing module of partOf (app select → this module → its nav[0].href). At most one holder per root: the BE keeps the existing winner on conflict (claim stored as false, warn-logged, registration still succeeds). Declaring false/omitting never downgrades an admin-set flag. | | labelKey | string | i18n key for the module's localized display name — sent as label_key in service.register and resolved by every UI surface (breadcrumbs, app launcher, module menus) against this service's own translation namespace. Fallback when absent/unresolved: the raw name. | | customHealthChecks | Record<string, () => Promise<HealthCheckResult>> | Custom health checks beyond DB — added to the HealthCheck constructor. NATS check is added automatically if not provided. | | dbPatches | DbMigrationBootOptions | Database migration boot step — runs immediately after initDal, before ConfigLoader and any other table read. When set, the service owns its schema patches (db-meta/patches + signed MANIFEST.json) and applies them before registration. A critical failure boots the service in DEGRADED mode: only /health + the db-migration admin routes answer, domain traffic gets 503, heartbeat reports db_migration.ok=false. | | onReady | (ctx: MicroserviceContext) => Promise<void> | Callback fired after everything is wired (NATS subscriptions, business logic init, etc.). Receives the full MicroserviceContext. SKIPPED entirely when the service booted degraded (migration failure). |

MigrationBootState

FieldTypeDescription
resultMigrationRunResultLike | nullnull when migrations never ran (option not set).
degradedbooleanBoot-time mode actually used.
lastErrorstring | nullMonotonic flag the heartbeat/health surface reads live.
appliedCountnumber

MigrationEngineOptsLike

FieldTypeDescription
poolany
schemastring
patchesDirstring
dbMetaDirstring
allowedTrusttypeOperator
approvedReviewstypeOperator
actorstring
lockWaitMsnumber
loggerany

MigrationRunResultLike

Structural mirror of dal-pg's MigrationRunResult — no dal import.

FieldTypeDescription
modestring
degradedboolean
appliedstring[]
failed{ patch_name: string; error: string }[]
pendingReviewstring[]
skippedstring[]
securityEventsstring[]

NatsRequestRoute

FieldTypeDescription
subjectstring
queuestring | null
permissionstypeOperatorRBAC permissions — MANDATORY unless verify is set.
rbacMode"any" | "all"
verifyNatsRouteVerifyService-identity verification — see NatsRoute.verify.
enrollmentbooleanEnrollment channel — see NatsRoute.enrollment.
validate(raw: unknown) => TReq
handler(ctx: NatsRouteContext<TReq>) => Promise<TRes>Returns the reply payload — published to msg.reply.

NatsRoute

FieldTypeDescription
subjectstringNATS subject, e.g. "emailsender.send".
queuestring | nullQueue group. Default: the service code (one copy round-robin across
replicas). Pass null ONLY for broadcast listeners that must see every
message — that intent stays visible in the declaration.
permissionstypeOperatorRBAC permissions — MANDATORY unless verify is set.
rbacMode"any" | "all"
verifyNatsRouteVerifyService-identity verification (e.g. BE client-shield check) instead of
the default AuthUser+RBAC chain. Use for service-to-BE subjects where
the caller is a service, not a user.
enrollmentbooleanEnrollment channel — the route's verify REPLACES the global Layer-1
client-shield check entirely (it must do the equivalent check itself,
typically with allowNewPrefix). Only legal on routes whose purpose
is first-time client enrollment (service.register): everywhere else a
skipped L1 is a security hole and the flag must never be set.
validate(raw: unknown) => TReqPayload validator — throws on invalid shape (zod .parse or manual).
handler(ctx: NatsRouteContext<TReq>) => Promise<void>The ONE service call.

NatsRouteContext

FieldTypeDescription
userAuthUserResolved caller (GATEWAY-RESOLVED headers). undefined for
service-authenticated routes (verify shield check) — services have
no AuthUser.
requestTReqValidated request payload.
msgMsgRaw NATS message — headers, subject, reply.

NatsRouterDeps

FieldTypeDescription
serviceCodestringQueue-group default — the registered service code.
connectionNatsConnectionInjectable for tests — defaults to NatsClient.getConnection().

NatsSseBridgeMapping

Mapping from a NATS subject to an SSE event type.

  • subject: The NATS subject to subscribe to (e.g. service.heartbeat).
  • eventType: The SSE event: field value (e.g. service.heartbeat).
  • transform: Converts the NATS payload into an { id, data } pair for the SSE event. The id should be deterministic for deduplication.
FieldTypeDescription
subjectstring
eventTypestring
transform(payload: unknown) => { id: string; data: unknown }

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
idp_typestring

OpenApiSpecInput

FieldTypeDescription
info{ title: string; version: string; description: string }
serverUrlstringService base URL — emitted as servers[0].url.
tags{ name: string; description: string }[]
pathsRecord<string, unknown>

PackageIdentity

FieldTypeDescription
namestring
versionstring
capabilitiesstring[]Declared capabilities from package.json ("capabilities": [...]),
snake_case lowercase. Sent in service.register → persisted in
service_registry.capabilities — refreshed at every registration.
service_codestringStatic service-code override declared in package.json
("service_code": "..."). When absent, the code is derived from
name (primebrick-<code> → <code>). This is the ONLY way a
service may carry a code that does not derive from its package name —
it is part of the shipped artifact, not runtime-editable config.

PresenceDelta

Delta published on the NATS presence.{entityType}.{entityUuid} subject. Consumed by the BE SSE bridge and forwarded to connected FE clients.

FieldTypeDescription
user_uuidstringThe user whose presence changed.
actionPresenceActionThe action that triggered this delta.
entryPresenceEntry | nullThe updated entry (for READING/EDITING/HEARTBEAT), or null (for LEAVE).
emitted_atnumberEpoch ms of the delta (for dedup / ordering on the FE).

PresenceEntry

A single user's presence on an entity. Stored in Redis as JSON. One entry per (entity_type, entity_uuid, user_uuid).

FieldTypeDescription
user_uuidstring
user_namestring
avatar_colorstring | null
avatar_initialsstring | null
statusPresenceStatus
fieldstringEDITING only — the field being edited.
valueunknownEDITING only — the current draft value of the field.
last_seen_atnumberEpoch ms of the last signal/heartbeat from this user.
tab_countnumberNumber of open tabs for this user on this entity (deduped by session_id).

PresencePort

PresenceSignal

Wire payload for POST /api/v1/entities/:entity/:uuid/presence. Sent by the FE on open, on field focus, on field blur, on heartbeat, and on close.

FieldTypeDescription
actionPresenceAction
fieldstringEDITING only — the field name being edited.
valueunknownEDITING only — the current draft value of the field (full value).
loaded_versionnumberREADING / HEARTBEAT — the version the client loaded (for staleness detection).
session_idstringTab session id — deduplicates tabs for the same user. FE-generated (uuid).

PresenceSnapshot

Snapshot returned by GET /api/v1/entities/:entity/:uuid/presence and sent as the initial SSE snapshot event on connect.

FieldTypeDescription
readersPresenceEntry[]Users in READING status (not editing).
editorsPresenceEntry[]Users in EDITING status.
changedEntityChangedMarker | nullMost recent server-side change marker, or null if none in the TTL window.
current_versionnumberCurrent version of the entity (read from DB on snapshot build).

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

Rfc7807Body

FieldTypeDescription
typestring
titlestring
statusnumber
detailstring
instancestring
internal_codestring
severitystring
extraRecord<string, unknown>Structured extras — issues is the key the FE RfcErrorDialog renders.

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

RotatingSecretRef

FieldTypeDescription
scopestring"global" or a service code — scopes the key namespace.
namestring

RowCustomAction

UI configuration for a non-standard row CTA (route-backed bare op).

FieldTypeDescription
action_namestringAction/op name — matches the bare op derived from the route
(e.g. change_password).
translation_keystringi18n key for the CTA label.
iconstringIcon name (Lucide in the reference FE).
text_colorstringOptional CSS color class for the label.
disabled_when_deletedbooleanDisable the CTA on soft-deleted rows.
required_permissionstring | string[]Permission required to see/use the action (filtered client-side
against the user's permission set).

RpcRoute

FieldTypeDescription
methodRpcMethod
pathstringAbsolute path, :param segments supported (e.g. "/api/v1/ai/conversations/").
permissionstypeOperatorRBAC permissions — MANDATORY for auth: "user"/"api_key" routes.
Only auth: "public" routes may omit it.
rbacMode"any" | "all"
authRpcAuthKindAuth kind. Default "user" (gateway-resolved).
body(raw: unknown) => TBodyBody validator — receives the raw parsed JSON (or the raw text when
rawBody is set) and returns the parsed body. Throw anything to
produce a 400 VALIDATION_ERROR (use zod's .parse, or a manual
shape check).
rawBodybooleanRead the body as raw text instead of parsing JSON — for pass-through
routes (e.g. webhook ingress) that must forward the payload verbatim.
streaming"sse""sse" = governed streaming route: handler gets ctx.sse, owns res.
mfaMfaStepUpSpecMFA step-up — only legal on auth: "user" routes. The client must send
a single-use action authorization token in x-mfa-action-authorization
(minted by BE step-up endpoints); the router delegates the consume to
the BE via auth.mfa.consume. Missing/invalid → 403 with
extra.mfa_step_up_required so the FE shows the step-up dialog.
openapiRpcRouteOpenApiOpenAPI metadata — consumed by rpcSpec()/buildOpenApiSpec() to emit
this route in the service's auto-generated /api/v1/openapi.json.
Routes without it are still documented (path+method+security derived),
but summary/description/schemas make them first-class.
handler(ctx: RpcRouteContext<TBody, TParams>) => Promise<unknown>The ONE service call — business logic lives behind it, not here.

RpcRouteContext

FieldTypeDescription
userAuthUserResolved caller — undefined only for auth: "public" routes.
paramsTParamsPath params extracted from :name segments.
queryURLSearchParams
bodyTBodyParsed+validated body (output of the route's body validator).
reqIncomingMessage
resServerResponse
sseSseWriterPresent only when streaming: "sse" — handler owns the response.

RpcRouteOpenApi

OpenAPI metadata declared on a single RpcRoute.

FieldTypeDescription
summarystringShort human/LLM summary — REQUIRED when openapi is present.
descriptionstring
tagsstring[]
requestSchemaJsonSchemaJSON schema of the request body (POST/PUT/PATCH).
responseSchemaJsonSchemaJSON schema of the 200 response body.
paramSchemaRecord<string, JsonSchema>Path params override (default: :name segments → required string).

RpcRouterDeps

FieldTypeDescription
apiKeyPortApiKeyPort | () => ApiKeyPort | null | undefinedRequired only if any route uses auth: "api_key". Either the port
itself or a lazy getter resolved at request time (for ports wired
after router construction, e.g. authDependencySetters).
mfaConsumeMfaConsumeFnOverride for the MFA action-authorization consume — defaults to the
governed auth.mfa.consume correlation call to the BE. Injectable for
tests only.

SecretGetRequest

FieldTypeDescription
scopestring"global" or a service code — scopes the key namespace.
namestring

SecretGetResponse

FieldTypeDescription
currentstring
previousstring | null
rotated_atnumberEpoch ms of the last rotation.

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"
pkg_namestringPackage name from the service's package.json — with service_version
it forms the canonical identity {pkg_name}/{version} (ua_prefix in
system.client_shield_registry, service_registry.pkg_name, log messages).

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"
pkg_namestringPackage name from the service's package.json — with service_version
it forms the canonical identity {pkg_name}/{version} (ua_prefix in
system.client_shield_registry, service_registry.pkg_name, log messages).
endpointsRecord<string, unknown>
part_ofstringROOT LEVEL the module belongs to (part of) — e.g. 'system' for
platform services, 'crm' for the CRM domain. Persisted in
service_registry.part_of; combined with code it derives the
module's FE route prefix /{part_of}/{code} — every declared
route_prefixes entry, nav href and routes entry must live under it
(enforced by the BE at registration).
is_defaultbooleanDEFAULT-MODULE CLAIM for the service's root level — true declares
this module as the landing app module of part_of (sidebar selects it
when the app is chosen → its nav[0].href). The BE applies keep-winner:
a claim colliding with a different holder is stored as false, never
rejected. false/absent never downgrades — an admin-set default
survives reboots.
label_keystringi18n key for the module's localized display name — resolved by the FE
against this service's OWN translation namespace (same contract as
nav[].label_key). Persisted in service_registry.label_key; every UI
surface (breadcrumbs, app launcher, module menus) renders it, falling
back to the raw name when absent or the key is unresolved.
client_key_hashstringsha256 of the service's client shield key (module config
service_client_shield_key). The raw key NEVER travels — the BE
stores only this hash in client_shield_registry and verifies inbound
x-primebrick-client-shield-key by hashing+comparing.
key_mode"static" | "derived"Key-rotation contract (B14): key_mode='derived' means the hash was
produced from HMAC(derivation_secret, ua_prefix:key_generation) — the
BE stores key_generation on the row and a rotate endpoint bumps it,
triggering re-registration with the next derived key. 'static' rows
carry a service-configured key and rotate only by admin action.
key_generationnumber
capabilitiesstring[]Capabilities declared in the service's package.json
("capabilities": [...], snake_case lowercase) — e.g.
llm_orchestrator, vectorizing_engine. Persisted by the BE in
service_registry.capabilities and refreshed at every registration,
so the registry always reflects the deployed artifact.
schemasstring[]PG schema ownership claim — the service's DATABASE_SCHEMA env list
(validated by assertDbEnv). The first schema is the owned one.
Persisted in service_registry.schemas: the BE rejects registration
when a claimed schema is owned by a different code, or is one of the
platform schemas (the BE's own DATABASE_SCHEMA, e.g.
public/system/custom). The claim survives offline/stale rows and is
released only by service.unregister or admin purge.
code_source"derived" | "override"How code was resolved (B3 — static service identity):
derived — code === pkg_name minus the primebrick- prefix;
the BE re-derives and rejects on mismatch.
override — explicit "service_code" declared in the service's
package.json; the BE accepts any valid code.
Absent (legacy payloads) is treated as derived — a code that does
not match its package name must be declared in the artifact.

ServiceRegistrarConfig

FieldTypeDescription
serviceCodestring
baseUrlstring
endpointsRecord<string, unknown>
partOfstringRoot level this module is part of (e.g. 'system', 'crm') — sent as
part_of in service.register; the module's FE routes must live under
/{part_of}/{serviceCode}.
isDefaultbooleanDefault-module claim for partOf — true asks the BE to mark this
service as its root level's landing module. Keep-winner on conflict;
absent/false never downgrades a stored flag.
labelKeystringi18n key for the module's localized display name — sent as label_key
in service.register; resolved against this service's own translation
namespace by every UI surface (fallback: raw name).
heartbeatIntervalMsnumber
namestring
descriptionstring
authorstring
github_repo_urlstring
service_versionstring
is_behind_scalerboolean
iconstring
icon_type"url" | "svg" | "base64" | "icon"
clientKeystringThis service's own client key (module config service_client_shield_key) — paired with
the UA prefix in system.client_shield_registry. Only its sha256 hash is sent
in service.register; the raw key never leaves the service.
keyDerivationSecretstringDerived-key mode (B14): the service's derivation secret (module config
service_key_derivation_secret). When set, every service.register
materializes the key as HMAC(secret, ua_prefix:generation) for the
generation the BE advertises — the row becomes key_mode='derived' and
a BE-side rotate (generation bump) auto-triggers re-registration with
the newly derived key. No key material rests in config or on disk.
registryGeneration() => numberReads this service's current key_generation from the client-shield
registry snapshot (wired by create-microservice). Required for derived
mode; unused otherwise.
generationSlot{ value: number }Shared slot (wired by create-microservice): the registrar writes the
generation of each ACKED register so outbound calls keep deriving the
acked key while the post-rotation handshake completes (zero-gap).
gatewayUrlstringAPI gateway base URL (the BE, e.g. http://localhost:3001) — used only
as a log tag on lifecycle messages so the target of registration and
heartbeats is visible on the service's stdout.
docsPackDirstringAbsolute path of the built docs pack (e.g. dist/docs-pack). When it
contains a manifest.json, the docs_pack capability is added to the
registration automatically — no package.json edit needed.
schemasstring[]PG schema ownership claim — the service's DATABASE_SCHEMA list
(validated by assertDbEnv, first = owned schema). Sent in
service.register → persisted in service_registry.schemas so the
BE can enforce one-module-one-schema (SCHEMA_OWNERSHIP_CONFLICT).
codeSource"derived" | "override"How serviceCode was resolved: "override" when package.json
declares an explicit service_code, else "derived" (package name
minus primebrick-). Wired by create-microservice — the BE
re-derives derived codes and rejects mismatches.

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.

ServiceRoutesInput

FieldTypeDescription
info{ title: string; version: string; description: string }OpenAPI document info — title is required, version recommended.
serverUrlstringEmitted as servers[0].url when present.
tags{ name: string; description: string }[]Extra top-level OpenAPI tags.
entitiesEntityRouteDecl<unknown>[]Entity CRUD routers — routes AND spec derived from one config.
rpcRpcRoute<unknown, Record<string, string>>[] | { routes: RpcRoute<unknown, Record<string, string>>[]; deps: RpcRouterDeps }[]RPC route tables — one makeRpcRouter per entry; openapi metadata
on each route feeds rpcSpec. An entry may be a bare RpcRoute[]
or { routes, deps } when the router needs wiring (e.g. apiKeyPort).

ServiceStalePayload

Payload for service.stale — published by the BE's stale-detection job when a service's last_health_check_at exceeds the stale threshold.

This is NOT published by microservices. It is a BE-internal event that allows all BE instances (and their SSE clients) to learn about stale services in real time.

FieldTypeDescription
codestring
base_urlstring
is_behind_scalerboolean
statusstringStatus the stale-detection job just assigned: 'stale' or 'offline'.
last_health_check_atstringISO 8601 timestamp of the last heartbeat received.

ServiceUnregisterPayload

FieldTypeDescription
codestring
base_urlstring
is_behind_scalerboolean
pkg_namestring
service_versionstring

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(). | | roles | typeOperator | Roles attached to the user / job, useful for low-level RBAC decisions inside services. May be empty for "system" callers. | | idpCode | string \| null | Original IDP sub for traceability. null for "system". | | idpOrg | string \| null | IDP organization (from owner or organization claim). null for "system". | | idpUsername | string \| null | IDP username (from name, username, or preferred_username claim). null for "system". | | isVerified | boolean | Email verification status from IDP. null for "system". | | emailVerified | boolean | Email verification status (email-specific). null for "system". | | issuer | string | IDP issuer URL (from iss claim). null for "system". |

SharedConfig

Shape of the shared config object exchanged via NATS config.get. Extensible — future fields can be added without breaking consumers. All fields are optional: the BE only includes what it has configured.

FieldTypeDescription
redis_urlstringRedis cache URL, e.g. redis://localhost:6379. Empty/undefined = cache disabled.
telemetryTelemetrySharedConfigTelemetry/logging config — BE-owned (config_entries rows), shared with
all microservices. Single point of configuration; changes are pushed
via the config.changed broadcast subject.

ShieldRegistryLike

Structural view of the client registry needed for shield checks.

FieldTypeDescription
allowedPrefixestypeOperator

SqlVersionProbe

Minimal server-version probe — satisfied structurally by dal-pg's Dal (dal.serverVersion()) without the SDK depending on @primebrick/dal-pg types. Replaces the old pool-based probe so callers no longer need the raw pg.Pool.

SseEvent

A single SSE event to be sent to a client.

  • id: Optional unique event identifier. Used by the FE for Last-Event-ID on reconnect and for deduplication. Omitted for one-shot streams (e.g. AI chat) where reconnection is not applicable. Format when present: <resource>:<timestamp>:<nonce> or any deterministic string.
  • event: Event type in dot notation (e.g. service.heartbeat, snapshot).
  • data: Payload object. Serialized via extJsonStringify (BigInt-safe). Date objects are serialized as ISO strings (standard JSON behavior).
FieldTypeDescription
idstring
eventstring
dataT

SseEventBus

In-process event bus for distributing SSE events to all connected clients within a single BE instance.

Multi-instance fanout is handled by NATS: every BE instance subscribes to the same NATS subjects and bridges them to its local bus. Redis pubsub is NOT used — NATS is the messaging backbone.

SseEventBusSubscription

Subscription handle returned by SseEventBus.subscribe(). Call unsubscribe() to stop receiving events and clean up.

SseWriter

Writer abstraction over an Express SSE response.

Handles SSE wire format (W3C EventSource): id:, event:, data: fields followed by a blank line. Comments are sent as : <text>\n\n.

FieldTypeDescription
closedbooleanWhether the underlying connection has been closed.

TableMeta

Table options — present only on entities with a table page.

FieldTypeDescription
default_viewViewNameInitial view (default "table").
default_sort{ key: string; dir: SortDir }
default_page_sizenumber
page_size_optionsnumber[]
row_custom_actionsRowCustomAction[]UI config for non-standard row actions (e.g. change password).
Standard ops (edit/delete/duplicate/preview) are derived from
meta.actions — never declared here.
filter_presetstypeOperatorMutually-exclusive preset WHERE filters — each rendered as its own
toolbar control; the active option's conditions AND into every list
request (see FilterPreset).

TelemetryConfig

FieldTypeDescription
enabledbooleanMaster switch. false → no exporters, zero span overhead.
otlp_endpointstringOTLP collector base URL, e.g. "http://collector:4318".
otlp_headersRecord<string, string>Extra headers for the collector (API keys etc.).
sampler"always_on" | "always_off" | "traceidratio"Sampler: always_on
sampler_argnumberRatio for traceidratio sampler (0..1).

TelemetrySharedConfig

Telemetry/logging block distributed via SharedConfig.

FieldTypeDescription
enabledboolean
otlp_endpointstring
otlp_headersRecord<string, string>
sampler"always_on" | "always_off" | "traceidratio"
sampler_argnumber
log_format"json" | "pretty"
log_level"error" | "debug" | "info" | "warn"

TranslationRow

Row shape returned by the DAL Repository for translation entities.

FieldTypeDescription
idstring | bigint
uuidstring
keystring
languagestring
valuestring
created_atstring | Date
created_bystring
updated_atstring | Date
updated_bystring
versionnumber
deleted_atstring | Date | null
deleted_bystring | null

TranslationsRepository

Structural interface — a DAL Repository satisfies this without any import. TypeScript structural typing: if it has these methods, it's assignable.

TranslationsServiceOptions

FieldTypeDescription
repositoryTranslationsRepository
queryableQueryable
cachePortCachePort | null

TypeCapabilities

Full capability record for one ConfigType.

FieldTypeDescription
validationValidationCapabilities
widgetPartial<Record<WidgetCapability, true>>Present widget-level type_config props for this type.

UserResolverPort

ValidationCapabilities

Validation-level capabilities (props under type_config.validation).

FieldTypeDescription
requiredbooleanvalidation.required toggle — shown for every type.
unsignedbooleanvalidation.unsigned — numeric types only.
minBoundsKindvalidation.rules.min — 'length' for string-derived, 'value' for numeric, absent otherwise.
maxBoundsKindvalidation.rules.max — same semantics as min.
regexbooleanvalidation.rules.regex — string-derived types only.

ValidationRuleEmail

FieldTypeDescription
error_label_keystring

ValidationRuleMax

FieldTypeDescription
valuenumber | bigint
error_label_keystring

ValidationRuleMin

FieldTypeDescription
valuenumber | bigint
error_label_keystring

ValidationRuleRegex

FieldTypeDescription
patternstringRegex pattern string (parsed via new RegExp(pattern, flags)).
flagsstringRegex flags passed as the second argument to new RegExp(pattern, flags).
Common values: "g" (global), "i" (ignore case), "m" (multiline).
Empty string or undefined = no flags. Flags are NOT parsed from the pattern string.
error_label_keystring

ValidationRuleUrl

FieldTypeDescription
protocolsstring[]Allowed URL protocols, e.g. ["http", "https", "redis", "rediss", "tcp"].
error_label_keystring

Types & Enums

ActionsOverrides

Per-op enable overrides — may ONLY toggle enabled on route-backed ops.

Type: Record<string, { enabled: boolean }>

ApiKeyCacheSource

Type: CachePort \| () => CachePort \| null \| null \| undefined

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.

BoundsKind

Whether min/max bounds are expressed as string length or numeric value.

Type: "length" \| "value"

CacheBootstrapResult

CacheScope

Cache scope — which browser storage the FE should use.

Type: "local" \| "session"

CleanupFn

Type: () => Promise<void>

ClientIdentityResult

Type: { ok: true; ua: string } \| { ok: false; status: 401 \| 403; error: string }

ConfigEntry

Standard Config Table entry.

The BE coerces value to its native JS type before serialization:

  • bigint → native bigint (preserved by ext-json response parsing)
  • number → native number
  • money → native number (amount only; currency is in type_config)
  • secret → null (masked by BE)
  • other → string

Write path: callers send the native type (bigint/number/string); the BE serializes it to the DB string form via serializeConfigValue.

ConfigEntryType

Config value type vocabulary — drives widget selection and validation. Mirrors the SDK ConfigType and the BE type column.

  • bigint: integer values backed by JS BigInt (renamed from integer)
  • number: decimal/float values backed by JS Number
  • money: numeric amount with currency metadata in type_config

Type: "string" \| "text" \| "boolean" \| "bigint" \| "number" \| "money" \| "badge" \| "single_select" \| "multi_select" \| "url" \| "secret" \| "json" \| "date" \| "datetime" \| "time" \| "email" \| "phone"

ConfigType

Config value type vocabulary — single source of truth for SDK coercion and FE widget selection.

  • string / text / url / secret / json: string as-is (SDK) / various FE widgets.
  • boolean: SDK coerces via value === "true".
  • bigint / number: SDK coerces via BigInt / Number.
  • money: SDK coerces via Number (amount only; currency is in type_config).
  • badge: static set of options defined inline in type_config.values.
  • single_select: single-selection dropdown (was list). Options from type_config.api_url or type_config.values_source.
  • multi_select: multi-selection dropdown. Options from type_config.api_url or type_config.values_source. Value stored as comma-separated string.
  • email: validated as RFC 5322 simplified email. Stored as string.
  • phone: validated as E.164 via libphonenumber-js. Stored as string.
  • date / datetime / time: ISO date/datetime/time strings.

Type: "string" \| "text" \| "boolean" \| "bigint" \| "number" \| "money" \| "badge" \| "single_select" \| "multi_select" \| "url" \| "secret" \| "json" \| "date" \| "datetime" \| "time" \| "email" \| "phone"

DbEnv

DecimalInput

Anything coercion functions accept: Decimal, native number, bigint, or a decimal string.

Type: Decimal \| number \| bigint \| string

DeletionFilterMode

Soft-deletion visibility mode — maps to the deleted_records list param on the wire (non_deleted → EXCLUDED, deleted → ONLY, all → INCLUDED; absent param means the BE default, currently EXCLUDED).

Type: "non_deleted" \| "deleted" \| "all"

EntityActionSentinel

Sentinel gates a route may declare instead of concrete permissions. Values mirror the Permission enum in @primebrick/sdk.

Type: "_public" \| "_authenticated_user" \| "_authenticated_admin"

EntityCrudOp

Type: "meta" \| "list" \| "get" \| "create" \| "update" \| "delete" \| "purge" \| "restore" \| "audit" \| "export" \| "duplicate" \| "bulk_delete" \| "bulk_restore"

EntityMetaResponse

The served GET /api/v1/entities/{entity}/meta response: static meta + runtime-injected display_name, actions, collaboration.

Type: EntityMeta & { display_name: string; actions: EntityAction[]; collaboration: CollaborationMeta }

FilterCondition

One WHERE condition of a filter preset — serialized verbatim as a filters[i] entry on the /list query string (same vocabulary as AdvancedFilter conditions: field/op/value).

FilterOperator

Filter operators available for advanced filters.

Type: "=" \| "!=" \| ">" \| "<" \| ">=" \| "<=" \| "contains" \| "startsWith" \| "endsWith" \| "BETWEEN"

FilterPreset

A mutually-exclusive preset filter group rendered as its own control in the table toolbar (tandem with the deletion-filter group). Exactly one option is always active — there is no "clear" affordance. Declared in meta.table.filter_presets; example: a schedule_type scope selector (recurring / once / all) on a jobs table.

FilterPresetOption

A single selectable option of a table filter preset.

GenerateDocsFn

Type: (ctx: GenerateDocsContext) => Promise<GeneratedDoc[]>

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> }>

HostApiFn

ctx.components.hostApi — the host's authenticated full-path fetch (apiFetch): reaches BE-owned /api/v1/… endpoints that ctx.api (ws-bound to the owning service) cannot. Remote pages bind it through bindRemoteCtx(ctx) → getServiceApi('host').

Type: (path: string, init: RequestInit) => Promise<Response>

HostMountHandle

Handle returned by ModulePageHost.mount_host — imperative channel to a host-mounted component root.

I18nDict

Flat i18n dict — keys are full dot-paths (e.g. "app.auth.login.title").

Type: Record<string, string>

InternalIdentityProvider

Internal HTTP client — the ONLY legal way to call another Primebrick service over HTTP (BE→US, US→US).

internalFetch is a thin wrapper over fetch that force-injects the caller's service-identity headers (User-Agent + x-primebrick-client-shield-key) provided by configureInternalClient. Identity headers are applied AFTER the caller's own headers so they can never be overridden or spoofed.

Fail-closed: if no provider was configured at boot, every call throws — a raw fetch() to a microservice would be rejected by the identity gate anyway, so bypassing this client is pointless. This is what makes the "forgot the identity headers" class of bug impossible.

External calls (Casdoor, Brevo, LLM providers, …) MUST keep using plain fetch — they are not governed by the client registry.

Type: () => Record<string, string>

JsonSchema

Type: Record<string, unknown>

JwtClaims

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

Type: Record<string, unknown>

ListConnector

Global connector joining the advanced-filter group.

Type: "AND" \| "OR"

LogFormat

Type: "pretty" \| "json"

LogLevel

Type: "debug" \| "info" \| "done" \| "warn" \| "error"

MetaColumn

Entity field descriptor — the root-level columns dictionary. Transversal to every page type: table cells, form tooltips, labels.

Declared as a type alias (not interface): object literal types get an implicit index signature, keeping MetaColumn assignable to Record<string, unknown> — host/remotes pass columns through generic cell-snippet args typed as Record<string, unknown>.

MetaColumnType

UI column renderer type. NOTE: this is the UI type, not the DB type — e.g. roles is "text" here but jsonb on disk (never ILIKE-searchable).

Type: "text" \| "badge" \| "boolean" \| "color" \| "date" \| "datetime" \| "number" \| "sparkline"

MfaConsumeFn

Type: (token: string, action: string, targetResource: string) => Promise<MfaConsumeResult>

MfaConsumeResult

Discriminated result returned by the BE consume route.

Type: { ok: true; user_uuid: string } \| { ok: false; code: string }

MfaStepUpFn

ctx.components.mfaStepUp — host-bound step-up MFA bridge. The remote calls it exactly like the host's useMfaStepUp().executeWithToken: requestFn runs once with token = null; on a 403 response carrying mfa_step_up_required the host shows ITS OWN step-up dialog (host root, host runtime — nothing crosses the boundary), awaits verification, then retries requestFn with the action-authorization token. The promise resolves with the final Response — success or failure.

Type: (requestFn: (token: string \| null) => Promise<Response>, context: { action: string; target_resource: string }) => Promise<Response>

ModuleHostBase

Host-side context base handed to EVERY remote mount — pages (mountPage) and sheets (mountSheet) alike. Versioned via host_version — remote chunks can refuse incompatible hosts.

HARD CONSTRAINT — plain values and functions ONLY: Svelte rune state ($state/$derived/$effect) is bundle-local and cannot cross this boundary. UI building blocks come from @primebrick/ui bundled inside the remote chunk, not from ctx; components is reserved for the few host-coupled cases (see components doc).

ModuleHostComponents

ctx.components — the reserved well-known keys plus open caller-provided entries. remote is assigned by the host loader after the chunk resolves; mfaStepUp/openSheet/hostApi are built-ins the host always provides (a caller may override them in tests).

ModuleNavDeclaration

A module's self-described navigation declaration — the payload a service publishes in service.register (endpoints.nav) so the FE can render the module's sidebar nav and the census can resolve its routes WITHOUT the BE hardcoding another module's pages. This is the wiring that lets each module present its own nav — a remote page is owned by the service that ships it, never declared inside a host module's registry.

route_prefixes drive active-module resolution (longest-prefix match — a page under a shared prefix like /system/jobs resolves to the owning module, not to the module owning the broader /system prefix).

ModuleNavLink

One sidebar navigation link contributed by a module. children is the collapsible-group mechanism (the shell renders children under the parent).

ModuleNotifyInput

Notification payload accepted by ctx.notify — the minimal shape of the host's pushNotification input. Plain values only.

ModulePageHost

Host ctx handed to a remote page's mountPage(el, ctx) — the shared ModuleHostBase plus the page's own route axis (nav/meta).

Type: ModuleHostBase & { nav: PageRouteDeclaration; meta: EntityMeta }

ModuleSheetDecl

A remote sheet panel declared by a service inside endpoints.nav.sheets — the sheet analogue of a remote PageRouteDeclaration: the host owns the sheet ENGINE (open/close/chrome/state), the owning service owns the CONTENT — an esm-mount chunk exporting mountSheet(el, ctx), mounted at runtime into the host's global sheet surface.

key is the open-sheet address, <module>.<name> namespaced so a declared key can never collide across services (enforced at enrollment by the nav-declaration validator). Anyone holding the key can open it — host openSheet, ctx.components.openSheet — visibility is gated by the sheet's derived access key (access.<ns>.<name>), never by import reachability.

ModuleSheetDefaults

Chrome defaults a sheet declaration may suggest. The caller's openSheet options always win over these — they are a hint, not a policy. content_class maps to the host engine's contentClass option.

ModuleSheetHost

Host ctx handed to a remote sheet's mountSheet(el, ctx) — the shared ModuleHostBase plus the sheet control surface: the declared key, the caller's props (opaque at the boundary — the owning module knows their shape), the current chrome, and the commands to mutate the host-owned frame. No nav/meta — a sheet is not a route; domain context arrives through sheet.props.

Type: ModuleHostBase & { sheet: { key: string; props: Record<string, unknown>; side: ModuleSheetSide; modal: boolean; close: () => void; replace: (props: Record<string, unknown>) => void; setExpanded: (expanded: boolean) => void } }

ModuleSheetSide

Sheet side — the two chrome slots the host sheet engine owns.

Type: "left" \| "right"

MountPageFn

The esm-mount chunk's entry contract — export function mountPage(el, ctx). May return the mount() result (disposed via unmount()), a custom dispose function, or a promise of either.

Type: (el: HTMLElement, ctx: ModulePageHost) => unknown

MountSectionFn

A chunk's section-mount export — same host ctx as mountPage, plus the section's own declaration (id, props, placement) as third argument.

Type: (el: HTMLElement, ctx: ModulePageHost, section: PageSectionDecl) => unknown

MountSheetFn

The sheet chunk's entry contract — export function mountSheet(el, ctx). The chunk is declared in endpoints.nav.sheets[] (ModuleSheetDecl) and mounted inside the host's global sheet surface. Same dispose rules as MountPageFn.

Type: (el: HTMLElement, ctx: ModuleSheetHost) => unknown

NatsRouteVerify

Custom caller verification. Replaces the default verifyNatsMessage+enforceNatsRbac chain. Return:

  • AuthUser → caller verified (user available in ctx.user)
  • undefined → caller verified, no user (service-to-BE shield identity)
  • null → denied (auth error to the caller / drop for pub/sub)

Type: (msg: Msg) => Promise<AuthUser \| null \| undefined>

OpenApiRouteDoc

The subset of RpcRoute fields the spec generator reads — lets services document routes that aren't (yet) served through makeRpcRouter without fabricating handler bodies.

Type: Pick<RpcRoute, "method" \| "path"> & Partial<Pick<RpcRoute, "auth" \| "permissions" \| "body" \| "streaming" \| "openapi">>

OpenSheetFn

ctx.components.openSheet — opens a sheet panel by key: a host-registered panel (shell.*/entity.*/config.*) or a remote-declared sheet (<module>.<name> from nav.sheets[], mounted at runtime). The panel registry, remote declarations and sheet state all live in the host — a remote page requests the panel, it never ships the engine. props are plain values (callback fns are allowed — they cross the boundary); options are the host's sheet options (side, contentClass, modal, keepMountedState). Unknown keys are rejected with a host notification — the remote cannot smuggle an undeclared panel.

Type: (panelKey: string, props: Record<string, unknown>, options: Record<string, unknown>) => void

PageLayoutDecl

Spatial arrangement of a page's sections — ONE archetype: CSS grid. A vertical stack is the degenerate case (columns defaults to "1fr", sections auto-flow in declaration order); a split is columns: "280px 1fr"; a dashboard is columns: "repeat(3, 1fr)" + per-section span. New container classes, when genuinely needed, arrive as new optional fields — the contract never grows a second placement vocabulary.

PageRouteComponentFormat

Remote component delivery format. Extensible — new literals are additive and non-breaking. esm-mount: a precompiled ESM chunk that exports mountPage(el, ctx) (or a default component as fallback).

Type: "esm-mount"

PageRouteDeclaration

One declared frontend route owned by a module. Modules MUST declare every FE path they own that is not deducible from nav links or entity conventions — the AI route census (derived from the consolidated GET /api/v1/modules discovery payload) depends on it. param marks dynamic segments using the FE convention ([uuid], [code]).

PageRouteKind

Page archetype vocabulary — what the route renders, regardless of delivery.

Type: "list" \| "detail" \| "create" \| "page"

PageRouteProps

page_props — free-form per-route metadata with a typed layout axis.

PageSectionDecl

One mountable block inside a layout — placement is grid-cell based.

PageSectionSource

Section mount target — remote:<export> = a named mount export of the page's own chunk (mod.mountModels(el, ctx, section)); host:<key> = a component registered in the host's section registry. The remote: form is the default for module-owned blocks; host: composes host-owned building blocks without shipping a chunk at all.

Type: templateLiteral \| templateLiteral

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. customer.read.all, emailsender.provider.create.single). 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.

Three 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.
  • Permission.AUTHENTICATED_ADMIN→ only callers with isAdmin === true pass. Use for high-risk non-CRUD operations (e.g. admin change-password).

Type: typeof Permission[typeOperator]

PresenceAction

Client → BE signal describing what the user is doing on an entity.

  • READING: user opened the entity (loaded_version is what they fetched).
  • EDITING: user is editing a field (field + value are the current draft).
  • HEARTBEAT: keep-alive (refreshes the Redis TTL; no state change).
  • LEAVE: user closed the tab / navigated away.

Type: "READING" \| "EDITING" \| "LEAVE" \| "HEARTBEAT"

PresenceStatus

Persistent status derived from the latest non-heartbeat signal. HEARTBEAT and LEAVE do not change status (LEAVE removes the entry).

Type: "READING" \| "EDITING"

Queryable

Queryable — same type the DAL Repository uses (Pick<Pool, "query">).

RedisInfo

RemoteDepsManifest

<chunk>.deps.json — emitted next to each remote chunk at build time, declaring the vendor specifiers/versions it was compiled against.

RoundingMode

Type: "UP" \| "DOWN" \| "CEILING" \| "FLOOR" \| "HALF_UP" \| "HALF_DOWN" \| "HALF_EVEN"

RouteHandler

Type: (req: IncomingMessage, res: ServerResponse, url: URL) => Promise<boolean>

ServiceRoutesHandler

The composed route handler returned by makeServiceRoutes — carries the built OpenAPI document so docs-pack builds (buildDocsPack) can embed handler.spec without a second spec declaration.

Type: RouteHandler & { spec: Record<string, unknown> }

SnakeCaseSingular

Branded type that enforces snake_case singular (no uppercase letters). Used for translation_key in entity meta — the i18n key prefix.

The brand is applied via asSnakeCaseSingular(), which performs a runtime check rejecting strings containing A-Z. This catches camelCase (userProfile), PascalCase (RoleMapping), and UPPER_CASE (USER_PROFILE) at both compile time and runtime.

Type: string & { __snakeCaseSingularBrand: typeOperator }

SortDir

Sort direction for server-side list queries and header UI.

Type: "asc" \| "desc"

StandardOp

Type: typeof STANDARD_OPS[number]

TooltipPriority

Tooltip visual severity — drives icon and title color in priority tooltip renderers.

Type: "INFORMATION" \| "WARNING" \| "ERROR" \| "QUESTION" \| "HINT" \| "SUCCESS"

VendorManifest

Host vendor-manifest.json — specifier → published vendor asset.

Type: Record<string, { url: string; version: string }>

VendorMismatch

Type: { specifier: string; kind: "missing"; required: string } \| { specifier: string; kind: "version"; required: string; provided: string }

VendorResolution

Type: "external" \| "reject" \| "bundle"

VendorSpecifier

Type: typeof VENDOR_SPECIFIERS[number]

ViewName

Table view names supported by entity list pages.

Type: "table" \| "cards" \| "cards_list"

WidgetCapability

Widget-level type_config capabilities (top-level type_config props).

Type: "currency" \| "country" \| "badge_values" \| "select_source" \| "url_protocols"

Constants

API_KEY_CACHE_TTL_MS

Shared cache key + TTL for api_keys lookups.

Every cached ApiKeyPort is a CachingApiKeyPort: the BE resolves public.api_keys directly, microservices resolve via the governed auth.apikey.byHash NATS route — but all of them cache under the SAME key, so a single warm-up serves every reader and one invalidation would cover all of them.

Type: number

AUTH_MFA_CONSUME_SUBJECT

Type: "auth.mfa.consume"

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"

CACHE_CONTROL_CACHED

Cache-Control directive for cached GET responses.

private, no-cache, must-revalidate means:

  • private — only the browser may cache (not shared proxies/CDNs)
  • no-cache — the browser MUST revalidate with the server before using a cached copy
  • must-revalidate — once stale, the browser MUST not serve stale without revalidation

This is the correct directive for ETag-based caching — it prevents the browser from serving stale responses without asking the server first (via If-None-Match).

Type: "private, no-cache, must-revalidate"

CACHE_HEADERS

HTTP header names used by the Primebrick cache protocol.

Type: { ETAG: "ETag"; IF_NONE_MATCH: "If-None-Match"; PB_CACHED: "X-PB-Cached"; PB_CACHE_SCOPE: "X-PB-Cache-Scope" }

FieldValue
ETAG"ETag"
IF_NONE_MATCH"If-None-Match"
PB_CACHED"X-PB-Cached"
PB_CACHE_SCOPE"X-PB-Cache-Scope"

CLIENT_SHIELD_KEY_HEADER

Type: "x-primebrick-client-shield-key"

CLIENT_SHIELD_REGISTRY_SUBJECTS

Type: { GET: "system.clientShieldRegistry.get"; CHANGED: "system.client_shield_registry.changed" }

FieldValue
GET"system.clientShieldRegistry.get"
CHANGED"system.client_shield_registry.changed"

CLIENT_SHIELD_SECRET_REF

Canonical ref of the rotating client-shield secret.

Type: { scope: "global"; name: "service_client_shield_secret" }

FieldValue
scope"global"
name"service_client_shield_secret"

CONFIG_CHANGED_SUBJECT

NATS broadcast subject for config-change notifications. The BE publishes { keys: [...] } (fire-and-forget) whenever relevant config rows are written; microservices re-fetch config.get and apply.

Type: "config.changed"

consumeMfaActionAuthorization

Default consume: governed correlation call to the BE.

Type: MfaConsumeFn

DOCS_PACK_CAPABILITY

Docs-pack contract — the manifest describing a service's documentation bundle, built at build time and pulled by the AI service on registration.

Pack layout on disk (dist/docs-pack/):

Code
manifest.json — this schema docs/... — raw .mdx/.md files + _order.json (verbatim source) openapi.json — optional service OpenAPI spec

Wire format (GET /api/v1/system/docs/pack):

Code
{ manifest: DocsPackManifest, files: [{ path, content }] }

Text is always the source of truth — no precomputed vectors in the contract (embeddings are model-bound; the consumer re-embeds).

Type: "docs_pack"

DOCS_PACK_ROUTE

Type: "/api/v1/system/docs/pack"

DOCS_PACK_SCHEMA_VERSION

Type: 1

DOCS_PACK_SHIELD_HEADER

Canonical header carrying the rotating client-shield secret on s2s calls.

Type: "x-client-shield-secret"

fromPG

Alias of dec() — parse a PostgreSQL numeric/decimal text value.

Type: typeof dec

fromString

Type: typeof dec

fromWire

Alias of dec() — tolerates the "…d" wire suffix.

Type: typeof dec

JOB_SCHEDULE_HEADERS

Type: { schedule: "Nats-Schedule"; target: "Nats-Schedule-Target"; ttl: "Nats-Schedule-TTL"; source: "Nats-Schedule-Source"; next: "Nats-Schedule-Next"; scheduler: "Nats-Scheduler" }

FieldValue
schedule"Nats-Schedule"
target"Nats-Schedule-Target"
ttl"Nats-Schedule-TTL"
source"Nats-Schedule-Source"
next"Nats-Schedule-Next"
scheduler"Nats-Scheduler"

JOB_SCHEDULE_REQUEST_SUBJECT

Control-plane route for schedule declarations (primebrick-job).

Type: "jobs.ctl.schedule.request"

JOB_STREAM_SUBJECTS

Canonical JOBS stream capture set — schedule/due/result/cancel lanes only.

Type: typeOperator

logger

Type: { debug: (msg: string, meta: LogMeta) => void; info: (msg: string, meta: LogMeta) => void; done: (msg: string, meta: LogMeta) => void; warn: (msg: string, meta: LogMeta) => void; error: (msg: string, meta: LogMeta) => void }

FieldValue
debug""
info""
done""
warn""
error""

MFA_ACTION_AUTH_HEADER

Type: "x-mfa-action-authorization"

MIGRATION_ROUTE_PREFIX

Type: "/api/v1/system/db-migration"

natsCarrier

TextMapCarrier over NATS MsgHdrs (Map<string, string[]> wrapper).

Type: { get: ; set: ; keys: }

FieldValue
get""
set""
keys""

NUMERIC_MIN_SIG_DIGITS

Minimum significant digits preserved by automatic div scale — mirrors PostgreSQL NUMERIC_MIN_SIG_DIGITS (16). See pgDivScale.

Type: 16

NUMERIC_TYPES

Numeric types — min/max mean VALUE, unsigned flag applies. Mirrors the numeric checks in config-validator.ts.

Type: typeOperator

ONE

Type: Decimal

PAGE_ROUTE_KINDS

Type: Set<string>

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. customer.read.all, emailsender.provider.create.single). 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.

Three 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.
  • Permission.AUTHENTICATED_ADMIN→ only callers with isAdmin === true pass. Use for high-risk non-CRUD operations (e.g. admin change-password).

Type: { PUBLIC: "_public"; AUTHENTICATED_USER: "_authenticated_user"; AUTHENTICATED_ADMIN: "_authenticated_admin"; MODULES_READ_ALL: "modules.read.all"; MODULES_READ_SINGLE: "modules.read.single"; MODULES_UPDATE_SINGLE: "modules.update.single"; MODULES_DELETE_SINGLE: "modules.delete.single"; MODULES_CONFIG_READ_SINGLE: "modules.config.read.single"; MODULES_CONFIG_UPDATE_SINGLE: "modules.config.update.single"; AUTH_EVENT_READ_ALL: "auth_event.read.all"; AUTH_EVENT_READ_SINGLE: "auth_event.read.single"; AUTH_EVENT_READ_AUDIT: "auth_event.read.audit"; AUTH_EVENT_EXPORT: "auth_event.export"; AUTH_EVENT_CREATE_SINGLE: "auth_event.create.single"; AUTH_EVENT_CREATE_BULK: "auth_event.create.bulk"; AUTH_EVENT_UPDATE_SINGLE: "auth_event.update.single"; AUTH_EVENT_UPDATE_BULK: "auth_event.update.bulk"; AUTH_EVENT_DELETE_SINGLE: "auth_event.delete.single"; AUTH_EVENT_DELETE_BULK: "auth_event.delete.bulk"; AUTH_EVENT_RESTORE_SINGLE: "auth_event.restore.single"; AUTH_EVENT_RESTORE_BULK: "auth_event.restore.bulk"; AUTH_EVENT_DUPLICATE_SINGLE: "auth_event.duplicate.single"; AUTH_EVENT_DUPLICATE_BULK: "auth_event.duplicate.bulk"; CLIENT_REGISTRY_READ_ALL: "client_registry.read.all"; CLIENT_REGISTRY_READ_SINGLE: "client_registry.read.single"; CLIENT_REGISTRY_READ_AUDIT: "client_registry.read.audit"; CLIENT_REGISTRY_EXPORT: "client_registry.export"; CLIENT_REGISTRY_CREATE_SINGLE: "client_registry.create.single"; CLIENT_REGISTRY_CREATE_BULK: "client_registry.create.bulk"; CLIENT_REGISTRY_UPDATE_SINGLE: "client_registry.update.single"; CLIENT_REGISTRY_UPDATE_BULK: "client_registry.update.bulk"; CLIENT_REGISTRY_DELETE_SINGLE: "client_registry.delete.single"; CLIENT_REGISTRY_DELETE_BULK: "client_registry.delete.bulk"; CLIENT_REGISTRY_RESTORE_SINGLE: "client_registry.restore.single"; CLIENT_REGISTRY_RESTORE_BULK: "client_registry.restore.bulk"; CLIENT_REGISTRY_DUPLICATE_SINGLE: "client_registry.duplicate.single"; CLIENT_REGISTRY_DUPLICATE_BULK: "client_registry.duplicate.bulk"; CUSTOMER_READ_ALL: "customer.read.all"; CUSTOMER_READ_SINGLE: "customer.read.single"; CUSTOMER_READ_AUDIT: "customer.read.audit"; CUSTOMER_EXPORT: "customer.export"; CUSTOMER_CREATE_SINGLE: "customer.create.single"; CUSTOMER_CREATE_BULK: "customer.create.bulk"; CUSTOMER_UPDATE_SINGLE: "customer.update.single"; CUSTOMER_UPDATE_BULK: "customer.update.bulk"; CUSTOMER_DELETE_SINGLE: "customer.delete.single"; CUSTOMER_DELETE_BULK: "customer.delete.bulk"; CUSTOMER_RESTORE_SINGLE: "customer.restore.single"; CUSTOMER_RESTORE_BULK: "customer.restore.bulk"; CUSTOMER_DUPLICATE_SINGLE: "customer.duplicate.single"; CUSTOMER_DUPLICATE_BULK: "customer.duplicate.bulk"; ORGANIZATION_READ_ALL: "organization.read.all"; ORGANIZATION_READ_SINGLE: "organization.read.single"; ORGANIZATION_READ_AUDIT: "organization.read.audit"; ORGANIZATION_EXPORT: "organization.export"; ORGANIZATION_CREATE_SINGLE: "organization.create.single"; ORGANIZATION_CREATE_BULK: "organization.create.bulk"; ORGANIZATION_UPDATE_SINGLE: "organization.update.single"; ORGANIZATION_UPDATE_BULK: "organization.update.bulk"; ORGANIZATION_DELETE_SINGLE: "organization.delete.single"; ORGANIZATION_DELETE_BULK: "organization.delete.bulk"; ORGANIZATION_RESTORE_SINGLE: "organization.restore.single"; ORGANIZATION_RESTORE_BULK: "organization.restore.bulk"; ORGANIZATION_DUPLICATE_SINGLE: "organization.duplicate.single"; ORGANIZATION_DUPLICATE_BULK: "organization.duplicate.bulk"; ROLE_MAPPING_READ_ALL: "role_mapping.read.all"; ROLE_MAPPING_READ_SINGLE: "role_mapping.read.single"; ROLE_MAPPING_READ_AUDIT: "role_mapping.read.audit"; ROLE_MAPPING_EXPORT: "role_mapping.export"; ROLE_MAPPING_CREATE_SINGLE: "role_mapping.create.single"; ROLE_MAPPING_CREATE_BULK: "role_mapping.create.bulk"; ROLE_MAPPING_UPDATE_SINGLE: "role_mapping.update.single"; ROLE_MAPPING_UPDATE_BULK: "role_mapping.update.bulk"; ROLE_MAPPING_DELETE_SINGLE: "role_mapping.delete.single"; ROLE_MAPPING_DELETE_BULK: "role_mapping.delete.bulk"; ROLE_MAPPING_RESTORE_SINGLE: "role_mapping.restore.single"; ROLE_MAPPING_RESTORE_BULK: "role_mapping.restore.bulk"; ROLE_MAPPING_DUPLICATE_SINGLE: "role_mapping.duplicate.single"; ROLE_MAPPING_DUPLICATE_BULK: "role_mapping.duplicate.bulk"; TRANSLATION_READ_ALL: "translation.read.all"; TRANSLATION_READ_SINGLE: "translation.read.single"; TRANSLATION_READ_AUDIT: "translation.read.audit"; TRANSLATION_EXPORT: "translation.export"; TRANSLATION_CREATE_SINGLE: "translation.create.single"; TRANSLATION_CREATE_BULK: "translation.create.bulk"; TRANSLATION_UPDATE_SINGLE: "translation.update.single"; TRANSLATION_UPDATE_BULK: "translation.update.bulk"; TRANSLATION_DELETE_SINGLE: "translation.delete.single"; TRANSLATION_DELETE_BULK: "translation.delete.bulk"; TRANSLATION_RESTORE_SINGLE: "translation.restore.single"; TRANSLATION_RESTORE_BULK: "translation.restore.bulk"; TRANSLATION_DUPLICATE_SINGLE: "translation.duplicate.single"; TRANSLATION_DUPLICATE_BULK: "translation.duplicate.bulk"; USER_PROFILE_READ_ALL: "user_profile.read.all"; USER_PROFILE_READ_SINGLE: "user_profile.read.single"; USER_PROFILE_READ_AUDIT: "user_profile.read.audit"; USER_PROFILE_EXPORT: "user_profile.export"; USER_PROFILE_CREATE_SINGLE: "user_profile.create.single"; USER_PROFILE_CREATE_BULK: "user_profile.create.bulk"; USER_PROFILE_UPDATE_SINGLE: "user_profile.update.single"; USER_PROFILE_UPDATE_BULK: "user_profile.update.bulk"; USER_PROFILE_DELETE_SINGLE: "user_profile.delete.single"; USER_PROFILE_DELETE_BULK: "user_profile.delete.bulk"; USER_PROFILE_RESTORE_SINGLE: "user_profile.restore.single"; USER_PROFILE_RESTORE_BULK: "user_profile.restore.bulk"; USER_PROFILE_DUPLICATE_SINGLE: "user_profile.duplicate.single"; USER_PROFILE_DUPLICATE_BULK: "user_profile.duplicate.bulk"; EMAILSENDER_PROVIDER_READ_ALL: "emailsender.provider.read.all"; EMAILSENDER_PROVIDER_READ_SINGLE: "emailsender.provider.read.single"; EMAILSENDER_PROVIDER_READ_AUDIT: "emailsender.provider.read.audit"; EMAILSENDER_PROVIDER_EXPORT: "emailsender.provider.export"; EMAILSENDER_PROVIDER_CREATE_SINGLE: "emailsender.provider.create.single"; EMAILSENDER_PROVIDER_CREATE_BULK: "emailsender.provider.create.bulk"; EMAILSENDER_PROVIDER_UPDATE_SINGLE: "emailsender.provider.update.single"; EMAILSENDER_PROVIDER_UPDATE_BULK: "emailsender.provider.update.bulk"; EMAILSENDER_PROVIDER_DELETE_SINGLE: "emailsender.provider.delete.single"; EMAILSENDER_PROVIDER_DELETE_BULK: "emailsender.provider.delete.bulk"; EMAILSENDER_PROVIDER_RESTORE_SINGLE: "emailsender.provider.restore.single"; EMAILSENDER_PROVIDER_RESTORE_BULK: "emailsender.provider.restore.bulk"; EMAILSENDER_PROVIDER_DUPLICATE_SINGLE: "emailsender.provider.duplicate.single"; EMAILSENDER_PROVIDER_DUPLICATE_BULK: "emailsender.provider.duplicate.bulk"; EMAILSENDER_TEMPLATE_READ_ALL: "emailsender.email_template.read.all"; EMAILSENDER_TEMPLATE_READ_SINGLE: "emailsender.email_template.read.single"; EMAILSENDER_TEMPLATE_READ_AUDIT: "emailsender.email_template.read.audit"; EMAILSENDER_TEMPLATE_EXPORT: "emailsender.email_template.export"; EMAILSENDER_TEMPLATE_CREATE_SINGLE: "emailsender.email_template.create.single"; EMAILSENDER_TEMPLATE_CREATE_BULK: "emailsender.email_template.create.bulk"; EMAILSENDER_TEMPLATE_UPDATE_SINGLE: "emailsender.email_template.update.single"; EMAILSENDER_TEMPLATE_UPDATE_BULK: "emailsender.email_template.update.bulk"; EMAILSENDER_TEMPLATE_DELETE_SINGLE: "emailsender.email_template.delete.single"; EMAILSENDER_TEMPLATE_DELETE_BULK: "emailsender.email_template.delete.bulk"; EMAILSENDER_TEMPLATE_RESTORE_SINGLE: "emailsender.email_template.restore.single"; EMAILSENDER_TEMPLATE_RESTORE_BULK: "emailsender.email_template.restore.bulk"; EMAILSENDER_TEMPLATE_DUPLICATE_SINGLE: "emailsender.email_template.duplicate.single"; EMAILSENDER_TEMPLATE_DUPLICATE_BULK: "emailsender.email_template.duplicate.bulk"; EMAILSENDER_SEND: "emailsender.send"; EMAILSENDER_LOG_CREATE_SINGLE: "emailsender.log.create.single" }

FieldValue
PUBLIC"_public"
AUTHENTICATED_USER"_authenticated_user"
AUTHENTICATED_ADMIN"_authenticated_admin"
MODULES_READ_ALL"modules.read.all"
MODULES_READ_SINGLE"modules.read.single"
MODULES_UPDATE_SINGLE"modules.update.single"
MODULES_DELETE_SINGLE"modules.delete.single"
MODULES_CONFIG_READ_SINGLE"modules.config.read.single"
MODULES_CONFIG_UPDATE_SINGLE"modules.config.update.single"
AUTH_EVENT_READ_ALL"auth_event.read.all"
AUTH_EVENT_READ_SINGLE"auth_event.read.single"
AUTH_EVENT_READ_AUDIT"auth_event.read.audit"
AUTH_EVENT_EXPORT"auth_event.export"
AUTH_EVENT_CREATE_SINGLE"auth_event.create.single"
AUTH_EVENT_CREATE_BULK"auth_event.create.bulk"
AUTH_EVENT_UPDATE_SINGLE"auth_event.update.single"
AUTH_EVENT_UPDATE_BULK"auth_event.update.bulk"
AUTH_EVENT_DELETE_SINGLE"auth_event.delete.single"
AUTH_EVENT_DELETE_BULK"auth_event.delete.bulk"
AUTH_EVENT_RESTORE_SINGLE"auth_event.restore.single"
AUTH_EVENT_RESTORE_BULK"auth_event.restore.bulk"
AUTH_EVENT_DUPLICATE_SINGLE"auth_event.duplicate.single"
AUTH_EVENT_DUPLICATE_BULK"auth_event.duplicate.bulk"
CLIENT_REGISTRY_READ_ALL"client_registry.read.all"
CLIENT_REGISTRY_READ_SINGLE"client_registry.read.single"
CLIENT_REGISTRY_READ_AUDIT"client_registry.read.audit"
CLIENT_REGISTRY_EXPORT"client_registry.export"
CLIENT_REGISTRY_CREATE_SINGLE"client_registry.create.single"
CLIENT_REGISTRY_CREATE_BULK"client_registry.create.bulk"
CLIENT_REGISTRY_UPDATE_SINGLE"client_registry.update.single"
CLIENT_REGISTRY_UPDATE_BULK"client_registry.update.bulk"
CLIENT_REGISTRY_DELETE_SINGLE"client_registry.delete.single"
CLIENT_REGISTRY_DELETE_BULK"client_registry.delete.bulk"
CLIENT_REGISTRY_RESTORE_SINGLE"client_registry.restore.single"
CLIENT_REGISTRY_RESTORE_BULK"client_registry.restore.bulk"
CLIENT_REGISTRY_DUPLICATE_SINGLE"client_registry.duplicate.single"
CLIENT_REGISTRY_DUPLICATE_BULK"client_registry.duplicate.bulk"
CUSTOMER_READ_ALL"customer.read.all"
CUSTOMER_READ_SINGLE"customer.read.single"
CUSTOMER_READ_AUDIT"customer.read.audit"
CUSTOMER_EXPORT"customer.export"
CUSTOMER_CREATE_SINGLE"customer.create.single"
CUSTOMER_CREATE_BULK"customer.create.bulk"
CUSTOMER_UPDATE_SINGLE"customer.update.single"
CUSTOMER_UPDATE_BULK"customer.update.bulk"
CUSTOMER_DELETE_SINGLE"customer.delete.single"
CUSTOMER_DELETE_BULK"customer.delete.bulk"
CUSTOMER_RESTORE_SINGLE"customer.restore.single"
CUSTOMER_RESTORE_BULK"customer.restore.bulk"
CUSTOMER_DUPLICATE_SINGLE"customer.duplicate.single"
CUSTOMER_DUPLICATE_BULK"customer.duplicate.bulk"
ORGANIZATION_READ_ALL"organization.read.all"
ORGANIZATION_READ_SINGLE"organization.read.single"
ORGANIZATION_READ_AUDIT"organization.read.audit"
ORGANIZATION_EXPORT"organization.export"
ORGANIZATION_CREATE_SINGLE"organization.create.single"
ORGANIZATION_CREATE_BULK"organization.create.bulk"
ORGANIZATION_UPDATE_SINGLE"organization.update.single"
ORGANIZATION_UPDATE_BULK"organization.update.bulk"
ORGANIZATION_DELETE_SINGLE"organization.delete.single"
ORGANIZATION_DELETE_BULK"organization.delete.bulk"
ORGANIZATION_RESTORE_SINGLE"organization.restore.single"
ORGANIZATION_RESTORE_BULK"organization.restore.bulk"
ORGANIZATION_DUPLICATE_SINGLE"organization.duplicate.single"
ORGANIZATION_DUPLICATE_BULK"organization.duplicate.bulk"
ROLE_MAPPING_READ_ALL"role_mapping.read.all"
ROLE_MAPPING_READ_SINGLE"role_mapping.read.single"
ROLE_MAPPING_READ_AUDIT"role_mapping.read.audit"
ROLE_MAPPING_EXPORT"role_mapping.export"
ROLE_MAPPING_CREATE_SINGLE"role_mapping.create.single"
ROLE_MAPPING_CREATE_BULK"role_mapping.create.bulk"
ROLE_MAPPING_UPDATE_SINGLE"role_mapping.update.single"
ROLE_MAPPING_UPDATE_BULK"role_mapping.update.bulk"
ROLE_MAPPING_DELETE_SINGLE"role_mapping.delete.single"
ROLE_MAPPING_DELETE_BULK"role_mapping.delete.bulk"
ROLE_MAPPING_RESTORE_SINGLE"role_mapping.restore.single"
ROLE_MAPPING_RESTORE_BULK"role_mapping.restore.bulk"
ROLE_MAPPING_DUPLICATE_SINGLE"role_mapping.duplicate.single"
ROLE_MAPPING_DUPLICATE_BULK"role_mapping.duplicate.bulk"
TRANSLATION_READ_ALL"translation.read.all"
TRANSLATION_READ_SINGLE"translation.read.single"
TRANSLATION_READ_AUDIT"translation.read.audit"
TRANSLATION_EXPORT"translation.export"
TRANSLATION_CREATE_SINGLE"translation.create.single"
TRANSLATION_CREATE_BULK"translation.create.bulk"
TRANSLATION_UPDATE_SINGLE"translation.update.single"
TRANSLATION_UPDATE_BULK"translation.update.bulk"
TRANSLATION_DELETE_SINGLE"translation.delete.single"
TRANSLATION_DELETE_BULK"translation.delete.bulk"
TRANSLATION_RESTORE_SINGLE"translation.restore.single"
TRANSLATION_RESTORE_BULK"translation.restore.bulk"
TRANSLATION_DUPLICATE_SINGLE"translation.duplicate.single"
TRANSLATION_DUPLICATE_BULK"translation.duplicate.bulk"
USER_PROFILE_READ_ALL"user_profile.read.all"
USER_PROFILE_READ_SINGLE"user_profile.read.single"
USER_PROFILE_READ_AUDIT"user_profile.read.audit"
USER_PROFILE_EXPORT"user_profile.export"
USER_PROFILE_CREATE_SINGLE"user_profile.create.single"
USER_PROFILE_CREATE_BULK"user_profile.create.bulk"
USER_PROFILE_UPDATE_SINGLE"user_profile.update.single"
USER_PROFILE_UPDATE_BULK"user_profile.update.bulk"
USER_PROFILE_DELETE_SINGLE"user_profile.delete.single"
USER_PROFILE_DELETE_BULK"user_profile.delete.bulk"
USER_PROFILE_RESTORE_SINGLE"user_profile.restore.single"
USER_PROFILE_RESTORE_BULK"user_profile.restore.bulk"
USER_PROFILE_DUPLICATE_SINGLE"user_profile.duplicate.single"
USER_PROFILE_DUPLICATE_BULK"user_profile.duplicate.bulk"
EMAILSENDER_PROVIDER_READ_ALL"emailsender.provider.read.all"
EMAILSENDER_PROVIDER_READ_SINGLE"emailsender.provider.read.single"
EMAILSENDER_PROVIDER_READ_AUDIT"emailsender.provider.read.audit"
EMAILSENDER_PROVIDER_EXPORT"emailsender.provider.export"
EMAILSENDER_PROVIDER_CREATE_SINGLE"emailsender.provider.create.single"
EMAILSENDER_PROVIDER_CREATE_BULK"emailsender.provider.create.bulk"
EMAILSENDER_PROVIDER_UPDATE_SINGLE"emailsender.provider.update.single"
EMAILSENDER_PROVIDER_UPDATE_BULK"emailsender.provider.update.bulk"
EMAILSENDER_PROVIDER_DELETE_SINGLE"emailsender.provider.delete.single"
EMAILSENDER_PROVIDER_DELETE_BULK"emailsender.provider.delete.bulk"
EMAILSENDER_PROVIDER_RESTORE_SINGLE"emailsender.provider.restore.single"
EMAILSENDER_PROVIDER_RESTORE_BULK"emailsender.provider.restore.bulk"
EMAILSENDER_PROVIDER_DUPLICATE_SINGLE"emailsender.provider.duplicate.single"
EMAILSENDER_PROVIDER_DUPLICATE_BULK"emailsender.provider.duplicate.bulk"
EMAILSENDER_TEMPLATE_READ_ALL"emailsender.email_template.read.all"
EMAILSENDER_TEMPLATE_READ_SINGLE"emailsender.email_template.read.single"
EMAILSENDER_TEMPLATE_READ_AUDIT"emailsender.email_template.read.audit"
EMAILSENDER_TEMPLATE_EXPORT"emailsender.email_template.export"
EMAILSENDER_TEMPLATE_CREATE_SINGLE"emailsender.email_template.create.single"
EMAILSENDER_TEMPLATE_CREATE_BULK"emailsender.email_template.create.bulk"
EMAILSENDER_TEMPLATE_UPDATE_SINGLE"emailsender.email_template.update.single"
EMAILSENDER_TEMPLATE_UPDATE_BULK"emailsender.email_template.update.bulk"
EMAILSENDER_TEMPLATE_DELETE_SINGLE"emailsender.email_template.delete.single"
EMAILSENDER_TEMPLATE_DELETE_BULK"emailsender.email_template.delete.bulk"
EMAILSENDER_TEMPLATE_RESTORE_SINGLE"emailsender.email_template.restore.single"
EMAILSENDER_TEMPLATE_RESTORE_BULK"emailsender.email_template.restore.bulk"
EMAILSENDER_TEMPLATE_DUPLICATE_SINGLE"emailsender.email_template.duplicate.single"
EMAILSENDER_TEMPLATE_DUPLICATE_BULK"emailsender.email_template.duplicate.bulk"
EMAILSENDER_SEND"emailsender.send"
EMAILSENDER_LOG_CREATE_SINGLE"emailsender.log.create.single"

recordCarrier

TextMapCarrier over plain Record<string,string> (e.g. fetch headers).

Type: { get: ; set: ; keys: }

FieldValue
get""
set""
keys""

resetAuthConfigForTest

Test helper alias (backward compat).

Type: () => void

SECRET_DEFAULTS

Rotation cadence + how long previous stays acceptable.

Type: { rotationMs: number; graceMs: number }

FieldValue
rotationMs""
graceMs""

SECRET_SUBJECTS

Type: { GET: "secret.get"; ROTATED: "secret.rotated" }

FieldValue
GET"secret.get"
ROTATED"secret.rotated"

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

The BE also publishes service.stale internally (from the stale-detection job) when a service's heartbeat is older than the stale threshold. This is NOT published by microservices — it is a BE-internal event.

Type: { REGISTER: "service.register"; HEARTBEAT: "service.heartbeat"; UNREGISTER: "service.unregister"; STALE: "service.stale"; GATEWAY_ONLINE: "service.gateway_online" }

FieldValue
REGISTER"service.register"
HEARTBEAT"service.heartbeat"
UNREGISTER"service.unregister"
STALE"service.stale"
GATEWAY_ONLINE"service.gateway_online"

SHARED_CONFIG_SUBJECT

NATS subject for the shared config request/reply.

Type: "config.get"

SINGULAR_NAV_ALLOWLIST

Leaf nav segments allowed to stay singular when they resolve to no declared route — kept deliberately tiny; extending it is an explicit diff, never an accident.

Type: Set<string>

SSE_HEADERS

SSE response headers per the Primebrick SSE standard.

  • Cache-Control: no-cache, no-transform — prevent proxy buffering/transform
  • X-Accel-Buffering: no — disable nginx response buffering
  • Connection: keep-alive — persistent connection
  • X-Content-Type-Options: nosniff — security header

Type: Record<string, string>

STANDARD_OPS

The canonical vocabulary of standard entity ops. Every op is always present in meta.actions — ops whose route is not registered are emitted with enabled: false (explicitly unavailable) instead of being omitted.

Type: typeOperator

STATIC_SERVICE_CONFIG_KEYS

Static-identity config keys — values the service treats as part of its deployed identity/discovery coordinates, NOT runtime-tunable settings.

  • service_code — resolved statically (package.json / pkg-name derivation); a config row survives only as a drift tripwire checked at boot.
  • service_base_url — the discovery coordinate published in service_registry (/ws/<code> proxy target). Provisioned by deployment, not edited at runtime: a mid-flight change re-registers the service at a new URL and orphans the old one.

Any entity-CRUD route that exposes config_entry writes MUST reject updates to these keys — call assertWritableConfigKey in the update handler (see emailsender's config-route for the canonical wiring).

Type: ReadonlySet<string>

STRING_DERIVED_TYPES

String-derived types — min/max mean LENGTH, regex rules apply. Mirrors the isStringDerived check in config-validator.ts.

Type: typeOperator

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"

TEN

Type: Decimal

TYPE_CAPABILITIES

Capability matrix keyed by ConfigType. Frozen at module init — treat as immutable. Example: TYPE_CAPABILITIES.string.validation.min === "length".

Type: Record<ConfigType, TypeCapabilities>

VENDOR_BLOCKED_PACKAGES

Packages that MUST NOT be imported raw inside a remote chunk — they are already served through a sanctioned vendor specifier (e.g. transformers is vendored behind @primebrick/ui/vendor/transformers). A raw import would bundle a second runtime copy (split caches, split env config) — reject it.

Type: typeOperator

VENDOR_SPECIFIERS

Closed allowlist of bare specifiers remote chunks may import. A package's specifiers normally union into ONE bundle file so the browser dedupes by URL — a single runtime instance is guaranteed. Exception: svelte's public subpaths (events/reactivity/store/attachments) get their own files — their export names collide with the internal signal API (get/derived exist in both), and a flat union module cannot hold both. Shared internals still dedupe through the bundler's shared chunks.

Type: typeOperator

ZERO

Type: Decimal

Functions

abs(d): Decimal

ParameterTypeDescription
dDecimalInput

accessLeafKey(ns, relPath): string

The access key of a leaf route — create and [param] segments are sub-states of the owning leaf, never leaves of their own.

/system/job/job-schedules/create under ns access.system.job → access.system.job.job-schedules; /crm/customers under access.crm → access.crm.customers.

relPath = the route path with the module prefix already stripped (leading / tolerated).

ParameterTypeDescription
nsstring
relPathstring

accessNamespace(code, partOf): string

A module's access-axis namespace — access.{app}.{module} with the part_of === code collapse (same rule as the URL prefix). Declared access keys on the nav declaration MUST live under it: job can only gate access.system.job.*, never access.system.settings.*.

ParameterTypeDescription
codestring
partOfstring

accessSheetKey(ns, code, sheetKey): string

The access key of a module sheet — sheet keys are <code>.<name>; strip the code prefix and attach the name to the module namespace: job.runDetail under access.system.job → access.system.job.runDetail.

ParameterTypeDescription
nsstring
codestring
sheetKeystring

add(a, b): Decimal

ParameterTypeDescription
aDecimalInput
bDecimalInput

allowedKeyNamespaces(moduleCode): ReadonlySet<string>

Namespaces a module's meta/nav declarations may REFERENCE — the module's own namespace plus the CORE vocabularies (app, system).

ParameterTypeDescription
moduleCodestring

apiKeyCacheKey(hash): string

ParameterTypeDescription
hashstring

appendListFilterParams(params, opts, startIdx): number

ParameterTypeDescription
paramsURLSearchParams
optsListFilterParamsOptions
startIdxnumber

assertDbEnv(cwd): DbEnv

Validate the DB env contract and return the resolved { url, schemas }. Throws with the full violation list on any failure — services MUST crash.

ParameterTypeDescription
cwdstring

assertMfaStepUp(req, spec, user, consume): Promise<void>

Assert the request carries a valid, consumable action authorization for spec, issued for user. Throws MfaStepUpRequiredError (deny → FE dialog) or MfaConsumeUnavailableError (transport failure → 503).

ParameterTypeDescription
reqIncomingMessage
specMfaStepUpSpec
userAuthUser | undefined
consumeMfaConsumeFn

assertWritableConfigKey(key): void

Throws when key is a static-identity config key. Designed for config_entry update handlers — a static key is edited by changing the artifact/deployment, never through the admin config UI.

ParameterTypeDescription
keystring

asSnakeCaseSingular(value): SnakeCaseSingular

Branded constructor for SnakeCaseSingular. Throws if the value contains uppercase letters (camelCase/PascalCase/UPPER_CASE).

ParameterTypeDescription
valuestring

brandRouteHandler(h): RouteHandler

ParameterTypeDescription
hRouteHandler

bridgeNatsToSse(nats, bus, mappings): Promise<() => void>

Subscribe to NATS subjects and bridge their messages to an SSE event bus.

Returns a cleanup function that unsubscribes all NATS subscriptions. Call it on graceful shutdown or when the bridge is no longer needed.

ParameterTypeDescription
natstypeof NatsClient
busSseEventBus
mappingsNatsSseBridgeMapping[]

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

buildDocsPack(opts): Promise<DocsPackManifest>

ParameterTypeDescription
optsBuildDocsPackOptions

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

buildOpenApiSpec(input): Record<string, unknown>

ParameterTypeDescription
inputOpenApiSpecInput

buildUserAgent(identity, capabilities): string

Build the Primebrick User-Agent string, e.g. primebrick-emailsender/1.4.0 (emailsender) Node/24.14.1. capabilities is the service code / role tag — free-form but should be stable; it is what the allowlist prefix matches against along with name.

ParameterTypeDescription
identityPackageIdentity
capabilitiesstring

Cached(ttl): ClassDecorator

Mark an entity as cacheable. Reads through the injected CachePort on miss; writes invalidate the entity's cache prefix. The wrapper (withCache) is best-effort — if the cache is unavailable, the Repository's real methods are called directly.

ParameterTypeDescription
ttlnumberTime-to-live in milliseconds. Omit for no expiry — use this ONLY for
genuinely immutable data (the cached value can never change). For mutable data, pick a
TTL that bounds the staleness window if Redis is intermittently unavailable during
invalidation. Recommended starting point for mutable data: 300_000 (5 minutes).
There is NO implicit default — @Cached() with no arg means "no TTL, immutable".

CacheKey(): PropertyDecorator

Mark the property used as the cache key source. Optional — if absent, CacheKeyBuilder falls back to row.uuid (JS property convention), then to the @Key() column (read via Reflect.getMetadata("primebrick:keyColumn", ctor)). If none of these exist, CacheKeyBuilder.forRowFromMeta throws and the dev must add @CacheKey().

Use this when the entity has no uuid property, or when you want the cache key to use a different field (e.g. a natural key like idp_code).

callNats(subject, data, opts): Promise<TRes>

Caller side of the correlation-reply pattern — the governed replacement for NatsClient.request(). Publishes on subject with a private reply inbox and resolves with the responder's payload (or rejects on {error}/timeout). Auth headers are the caller's responsibility (BE uses buildNatsAuthHeaders to forward the resolved user).

ParameterTypeDescription
subjectstring
dataunknown
opts{ timeoutMs: number; headers: Record<string, string>; connection: NatsConnection; natsClient: typeof NatsClient }

cancelJobSchedule(scheduleSubject, cancelSubject): Promise<bigint>

Disarm a previously armed schedule. The cancellation MUST go to a different subject than scheduleSubject (a same-subject publish would just replace the declaration).

ParameterTypeDescription
scheduleSubjectstring
cancelSubjectstring

ceil(d): Decimal

Smallest integer >= d.

ParameterTypeDescription
dDecimalInput

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)

clientKeyHash(key): string

sha256 hex of a client key — the only form that ever leaves the service (sent in service.register → stored in system.client_shield_registry) and the only form compared at verification time.

ParameterTypeDescription
keystring

closeRedisClient(): Promise<void>

Graceful shutdown — disconnect the Redis client. Safe to call multiple times. Registered with the SDK's GracefulShutdown by the BE (or US) at bootstrap.

cmp(a, b): -1 \| 0 \| 1

Numeric comparison ignoring scale: cmp(1.0, 1.00) === 0. Returns -1 | 0 | 1.

ParameterTypeDescription
aDecimalInput
bDecimalInput

coerceConfigValue(type, value, type_config): string \| number \| bigint \| null

Coerce a raw string config value to its native JS type. Returns the typed value for BE→FE response shaping.

  • bigint → native bigint
  • number → native number
  • money → native number (amount only; currency is in type_config, not in the value)
  • all others → string as-is
ParameterTypeDescription
typeConfigType
valuestring | null
type_configstring | null

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

composeRouteHandlers(handlers): RouteHandler

Compose multiple factory-produced routers — same chaining contract. Throws at construction time if any handler is not factory-produced: composition is an extension of the closed set, not a smuggling hatch.

ParameterTypeDescription
handlersRouteHandler[]

computeETag(value): string

Compute a strong ETag from any serializable value. Uses SHA-256 (first 16 hex chars) of the ext-JSON representation.

BigInt values are serialized correctly via extJsonStringify.

ParameterTypeDescription
valueunknown

computeVersionETag(version): string

Compute a cheap ETag from an entity version field. Faster than hashing — use when the entity has version: number.

ParameterTypeDescription
versionstring | number

configureInternalClient(p): void

Register the identity provider once at boot:

  • microservices: wired automatically by createMicroservice
  • the BE: configureInternalClient(backendIdentityHeaders) after initBackendIdentity().
ParameterTypeDescription
pInternalIdentityProvider

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

createMicroservice(options): Promise<MicroserviceContext>

Bootstrap a Primebrick microservice with minimal boilerplate.

Orchestrates: env validation → DAL init → config load → auth config → NATS connect → service register → HTTP server → graceful shutdown → onReady.

Returns a MicroserviceContext with all wired components for the consumer to use in service-specific logic (NATS subscriptions, custom endpoints, etc.).

ParameterTypeDescription
optionsMicroserviceOptions

createRedisClient(url): Promise<RedisClientType>

Create (or return the existing) Redis client. Singleton — subsequent calls with a different URL return the existing client (the URL is only used on first call).

ParameterTypeDescription
urlstringRedis URL, e.g. redis://localhost:6379 or rediss://host:6380 (TLS).

createRedisHealthCheck(getCachePort, getVersion): () => Promise<HealthCheckResult>

Create a Redis health check function from a CachePort getter.

ParameterTypeDescription
getCachePort() => CachePort | nullA function that returns the current CachePort (or null).
Using a getter instead of a direct reference allows the health check to
pick up a cache port that is initialized after the health check is created
(e.g. during startup retry).
getVersion() => string | undefinedOptional function that returns the cached Redis version
(avoids re-querying INFO on every health probe).

createSseEventBus(): SseEventBus

Create a new in-process SseEventBus.

Typically created once as a singleton per BE process and shared across all SSE endpoints that need the same event stream.

createSseWriter(res): SseWriter

Create an SseWriter backed by a Node.js HTTP ServerResponse.

Works with both Express responses (Express Response extends http.ServerResponse) and raw http.ServerResponse from framework-agnostic microservices (e.g. the AI microservice's createMicroservice route handler).

Immediately writes the SSE headers and flushes them. The caller is responsible for sending an initial snapshot event and setting up keep-alive + cleanup.

ParameterTypeDescription
resServerResponse

currencyDecimals(code): number

Get the number of decimal places for a currency code (e.g. "EUR" → 2, "JPY" → 0). Falls back to 2.

ParameterTypeDescription
codestring

currencySymbol(code): string

Get the currency symbol for a currency code (e.g. "EUR" → "€"). Falls back to the code.

ParameterTypeDescription
codestring

currentClientShieldSecret(fallback): string \| undefined

The value a publisher must stamp on outbound calls.

ParameterTypeDescription
fallbackstring

dec(input): Decimal

Decimal — pass-through. string — parsed (PG text form or "…d" wire form). number — via shortest round-trip toString so 0.1 stays "0.1". bigint — scale 0.

ParameterTypeDescription
inputDecimalInput

defaultCurrencyForLang(lang): string

Derive the default currency code from a locale string. Uses the region suffix: "it-IT" → "IT" → getCountryData("IT").currency → "EUR". Falls back to "EUR" for unknown locales.

ParameterTypeDescription
langstring

defaultSearchGate(trimmed): boolean

search-gate — canonical "should this search query hit the server" predicate.

Mirrors the backend list-search gate: ≥3 "real" characters, or an escaped wildcard (\* / \?) with ≥1 real char. Prevents per-keystroke queries while still allowing single-char wildcard searches.

Canonical home for both the host FE and remote module pages — the gate must match the server-side rule byte-for-byte.

ParameterTypeDescription
trimmedstringthe search string, already .trim()ed by the caller

defaultVisibleColumnKeys(columns): string[]

Keys visible by default — from column flags.

ParameterTypeDescription
columnsMetaColumn[]

deriveClientKey(derivationSecret, uaPrefix, generation): string

Deterministic per-service client key (B14): HMAC-SHA256(secret, uaPrefix:generation) rendered base64url. Services configured with service_key_derivation_secret never store a key — they re-derive it from the derivation secret (provisioned by the installer/secret engine) plus the generation the BE registry row currently advertises. Rotating a key = bumping the generation on the BE.

ParameterTypeDescription
derivationSecretstring
uaPrefixstring
generationnumber

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

detectPackageIdentity(startDir): PackageIdentity

ParameterTypeDescription
startDirstring

diffVendorRequirements(requires, manifest): VendorMismatch[]

Vendor compatibility check — exact-match policy. A remote chunk compiled against a different version of a vendored package is incompatible: svelte/internal/* is not a stable API and each union bundle's export set may change between versions. The remedy is rebuilding the remote chunk — a semver-tolerant handshake would only defer an opaque import failure.

ParameterTypeDescription
requiresRecord<string, string>
manifestVendorManifest

div(a, b, opts): Decimal

a / b. Without scale, the result uses the PostgreSQL numeric division rule (pgDivScale — e.g. 1/3 → scale 20, 10/4 → 16), so Decimal↔DB computations compare equal digit-for-digit. Rounding defaults to HALF_UP. When the true quotient does not terminate at the target scale the result is flagged periodic (isPeriodic(d) → true).

div(1, 3) // 0.33333333333333333333 — periodic div(10, 4) // 2.5000000000000000 div(1, 3, 2) // 0.33 div(a, b, { scale: 6, rounding: "HALF_EVEN" })

ParameterTypeDescription
aDecimalInput
bDecimalInput
optsnumber | { scale: number; rounding: RoundingMode }

docsPackRouteHandler(packDir): RouteHandler

ParameterTypeDescription
packDirstringabsolute path of the built pack (e.g. "dist/docs-pack").
Must contain manifest.json + docs/.

dueLaneVerify(lane, permissions): (msg: { subject: string; headers: MsgHdrs }) => Promise<AuthUser \| null \| undefined>

Lane-aware verify for NatsClient.jetstreamSubscribe on jobs.due.*.

  • "system" lane → L1 client shield only (verifyNatsPublisher). The fired message carries the DECLARING service's static identity — verified verbatim. No user context exists by design; the handler receives no authUser.
  • "user" lane → L1 + full L2 chain: gateway-secret + serialized AuthUser
    • RBAC permissions. An expired rotating secret or missing permission returns null → the consumer term()s the message (terminal denial — recorded as a failed run by the result tap).
ParameterTypeDescription
lane"system" | "user"
permissionstypeOperator

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"

ensureJobSchedule(opts): Promise<bigint>

Arm a JetStream schedule. Publishes the declaration message — the NATS server itself republishes payload/source to target per schedule.

Callers do NOT publish on jobs.schedule.* directly in production: the governed entry point is the jobs.schedule.request route on primebrick-job, which validates the caller's namespace and records the declaration in job.job_schedules. This helper is what THAT service calls (and is also usable by tests/tooling).

ParameterTypeDescription
optsEnsureJobScheduleOpts

entityChangedSubject(entityType, entityUuid): string

Build the NATS subject for entity-changed markers. Format: entity.{entityType}.{entityUuid}.changed

ParameterTypeDescription
entityTypestring
entityUuidstring

entityCrudSpec(entity, opts): Record<string, unknown>

Emit the canonical /api/v1/entities/:entity/... path block. ops is the exact set the service implements — the spec lists only real routes. All operations are tagged [tag] (defaults to the plural-ish <entity>s convention used by existing specs) and secured with bearerAuth+apiKey.

ParameterTypeDescription
entitystring
opts{ ops: EntityCrudOp[]; tag: string; label: string; listResponse: JsonSchema }

entityPaths(prefix, listLeaf, detailParam): { list: string; create: string; detail: string }

The ONLY constructor of entity route paths — modules declare routes through this builder instead of writing string literals, so the /{list} / /{list}/create / /{list}/[param] shape can never diverge by hand.

ParameterTypeDescription
prefixstring
listLeafstring
detailParamstring

eq(a, b): boolean

Numeric equality ignoring scale — eq(1.0, 1.00) is true.

ParameterTypeDescription
aDecimalInput
bDecimalInput

etagMatches(requestETag, cachedETag): boolean

Check if a request ETag matches a cached ETag. Handles weak ETags (W/"...") and strong ETags ("..."). The W/ prefix is stripped before comparison — a weak ETag matches a strong ETag with the same hash (per RFC 7232 weak comparison).

ParameterTypeDescription
requestETagstring | null
cachedETagstring

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

extJsonBodyParser(opts): (req: Request, _res: Response, next: NextFunction) => Promise<void>

Express middleware that parses request bodies with Ext-JSON.

This replaces the default express.json() body parser for routes that need to receive BigInt values without precision loss. Native JSON.parse() converts large integers to number, losing precision above Number.MAX_SAFE_INTEGER. This parser uses extJsonParse which forces all integers to native bigint.

Install once in the Express app, BEFORE routes, typically replacing or alongside express.json(). Only parses requests with Content-Type: application/json.

Example: app.use(express.json({ limit: "1mb" })); // fallback for non-JSON content types app.use(extJsonBodyParser()); // ext-json for application/json

Or as a per-route middleware for only the routes that need BigInt: router.put("/config/", extJsonBodyParser(), validateBody(schema), handler);

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

ParameterTypeDescription
opts{ limit: string | number }

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 — the useNativeBigInt option throws on float tokens, 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

extractNatsContext(hdrs): Context

Extract parent context from NATS message headers.

ParameterTypeDescription
hdrsMsgHdrs | undefined

extractTraceContext(hdrs): Context

Extract parent context from a plain header record (HTTP inbound).

ParameterTypeDescription
hdrsRecord<string, string | string[] | undefined>

fetchDocsPack(serviceBaseUrl): Promise<DocsPackPayload>

ParameterTypeDescription
serviceBaseUrlstring

fetchSharedConfig(nats): Promise<SharedConfig>

Microservice side: fetch the shared config from the BE via NATS request/reply.

Best-effort: returns an empty object if the BE doesn't respond or times out. The caller should check individual fields (e.g. result.redis_url) rather than assuming the whole object is populated.

ParameterTypeDescription
natstypeof NatsClientThe NatsClient class (uses the singleton connection)

fetchTraced(input, init): Promise<Response>

fetch with automatic W3C traceparent injection — for service-to-service HTTP calls under runtimes where undici instrumentation is unavailable (Bun). On Node, auto-instrumentation already injects; injecting again is harmless (same trace context).

ParameterTypeDescription
inputstring | URL | Request
initRequestInit

floor(d): Decimal

Largest integer <= d.

ParameterTypeDescription
dDecimalInput

flushLogsSync(): void

Best-effort synchronous flush — called from graceful shutdown.

formatMoney(amount, lang, currency): string

Format a numeric amount as a localized currency string. Uses Intl.NumberFormat with the currency's native decimal precision. Returns empty string for null/undefined values.

ParameterTypeDescription
amountnumber | null | undefined
langstring
currencystring

forwardAuthHeaders(msg): Record<string, string>

Copy the auth headers off an inbound message for verbatim forwarding — used when a service re-publishes a trigger event (or arms a schedule) and the originating user's context must ride along. Only the auth header names configured on this service's AuthConfig are copied.

ParameterTypeDescription
msg{ headers: MsgHdrs }

fromBigInt(v, scale): Decimal

ParameterTypeDescription
vbigint
scalenumber

fromNumber(n): Decimal

ParameterTypeDescription
nnumber

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).

getAllCurrencies(): CurrencyInfo[]

Get the full list of ISO 4217 currencies (for currency selector dropdowns).

getAuthConfig(): AuthConfig

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

getCacheKeyProperty(ctor): string \| undefined

Returns the property name marked with @CacheKey(), or undefined if not set. Used by CacheKeyBuilder.forRowFromMeta as the first resolution step.

ParameterTypeDescription
ctor(args: any[]) => any

getClientShieldSecretSlot(): ClientShieldSecretSlot \| null

getCurrencyInfo(code): CurrencyInfo \| null

Get ISO 4217 metadata for a currency code. Returns null for unknown codes.

ParameterTypeDescription
codestring

getEntityCacheTtl(ctor): number \| undefined

Returns the cache TTL in milliseconds, or undefined if no TTL (immutable data). Returns undefined for entities not marked with @Cached().

ParameterTypeDescription
ctor(args: any[]) => any

getLogOptions(): Readonly<LoggerState>

getNatsShieldRegistry(): ShieldRegistryLike \| null

getOperatorsForColumnType(columnType): FilterOperator[]

Operators available for a given column UI type.

ParameterTypeDescription
columnTypestring

getRedisClient(): RedisClientType \| null

Existing singleton client, or null if never created.

getRedisInfo(redis): Promise<RedisInfo \| null>

Query Redis INFO and parse the server version.

The INFO command returns a text block with \r\n line separators:

Code
# Server\r\n redis_version:7.4.0\r\n redis_git_sha1:00000000\r\n ...
ParameterTypeDescription
redisRedisClientType

getSdkCachePort(): CachePort \| null

Returns the CachePort, or null if cache is disabled.

getSdkRedisInfo(): RedisInfo \| null

Returns the Redis info (version), or null if not connected.

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).

getSharedConfig(): SharedConfig

getTelemetryConfig(): TelemetryConfig

hashApiKey(key): string

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

ParameterTypeDescription
keystring

humanizeMs(ms): string

"3600000" → "1 hour", "27000" → "27 seconds", "300000" → "5 minutes".

ParameterTypeDescription
msnumber

i18nCacheKey(schema, language): string

Cache key format: translations:i18n:{schema}:{language}

ParameterTypeDescription
schemastring
languagestring

identityHeaders(identity, capabilities, clientKey): Record<string, string>

Identity headers to attach to every outbound internal call. clientKey comes from the service's own config (env/config entry) — it is the secret paired with the UA in system.client_shield_registry.

ParameterTypeDescription
identityPackageIdentity
capabilitiesstring
clientKeystring

initAuthConfig(p): void

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

ParameterTypeDescription
pAuthConfigPort

initCacheFromSharedConfig(nats, logger, prefetched): Promise<CacheBootstrapResult>

Initialize the Redis cache for a microservice by discovering redis_url from the BE via NATS config.get.

Must be called AFTER NatsClient.getConnection() has succeeded.

ParameterTypeDescription
natstypeof NatsClientThe NatsClient class (uses the singleton connection)
loggerCacheLoggerConsole or custom logger with info/warn methods
prefetchedSharedConfig

initTelemetry(cfg, serviceName, serviceVersion): Promise<void>

Initialize telemetry. Registers globals once, builds the pipeline if enabled, registers Node auto-instrumentations. Safe to call once per process — subsequent config changes go through restartTelemetry.

ParameterTypeDescription
cfgTelemetryConfig
serviceNamestring
serviceVersionstring

injectTraceHeaders(hdrs): Record<string, string>

Inject W3C traceparent/tracestate into a plain header record.

ParameterTypeDescription
hdrsRecord<string, string>

installConsoleBridge(): void

Rebind console.log/info/warn/error/debug to the async logger. Idempotent. Skipped under vitest (VITEST env) so test output is unaffected.

internalFetch(url, init): Promise<Response>

fetch with the caller's service-identity headers force-injected.

ParameterTypeDescription
urlstring | URL
initRequestInit

invalidateAuthConfig(): void

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

isAccessGranted(userPermissions, required): boolean

Evaluate an access requirement against the user's grants.

  • required absent → true (undeclared = open).
  • required not an access key → false (defensive: never a silent grant).
  • No is_admin short-circuit — admin sees only what its grants cover.
ParameterTypeDescription
userPermissionstypeOperator | Set<string>
requiredstring | null

isAccessGrantLegal(grant, leafKeys): boolean

Grant-legality grammar for access.* permission strings — the role_mappings write-path guard (rejection, not silent non-coverage):

access → ILLEGAL — the axis root is not a grant access.* → ILLEGAL — would cover the whole axis access.<path>.* → LEGAL wildcard (any non-empty path; * only as the trailing whole segment) access.<leaf> → LEGAL exact — only when the key is a REAL leaf: bare namespaces (access.system.job) and typos are ILLEGAL, not dead grants. Requires leafKeys (the inventory of derived leaf keys); absent = structural check only.

ParameterTypeDescription
grantstring
leafKeysReadonlySet<string>

isAccessKey(value): value is string

ParameterTypeDescription
valueunknown

isDecimal(v): v is Decimal

ParameterTypeDescription
vunknown

isEntityCached(ctor): boolean

Returns true if the entity class is marked with @Cached().

ParameterTypeDescription
ctor(args: any[]) => any

isEntityOpEnabled(actions, op): boolean

Pure enablement check — true when op exists in the served actions array and carries enabled: true. FAIL-CLOSED: when actions is absent (meta without actions — contract violation) the op is not enabled.

This deliberately does NOT evaluate user permissions — that step lives in the consumer (e.g. FE isEntityOpAllowed composes this with the session permission check).

ParameterTypeDescription
actionstypeOperator | undefined
opstring

isFactoryRouteHandler(h): h is RouteHandler

True when h was produced by an approved factory — makeRpcRouter, composeRouteHandlers, or makeOpenApiHandler. createMicroservice refuses to mount anything else at startup.

ParameterTypeDescription
hunknown

isPeriodic(d): boolean

true when the value was produced by a truncated non-terminating division.

ParameterTypeDescription
dDecimalInput

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 / AUTHENTICATED_ADMIN) handled directly by the rbac middleware rather than by role expansion.

ParameterTypeDescription
pstring

isSnakeCaseSingular(value): value is SnakeCaseSingular

Runtime validator for SnakeCaseSingular — rejects strings with uppercase letters. Use in meta-loading code to catch BE meta that violates the convention.

ParameterTypeDescription
valuestring

keyNamespaceOf(key): string \| null

First dotted segment of a translation key — job.entities.x → job.

ParameterTypeDescription
keystring

listNonSentinelPermissions(): string[]

Returns all non-sentinel permission strings (i.e. the real RBAC permissions, excluding PUBLIC / AUTHENTICATED_USER / AUTHENTICATED_ADMIN). Used by the BE to build the permissions catalog for the FE role-management UI.

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.

logModuleStartup(name, version, url): void

Log a successful infrastructure module connection.

ParameterTypeDescription
namestringModule name (e.g. "PostgreSQL", "Redis", "NATS", "Casdoor")
versionstring | null | undefinedServer version (e.g. "18.0", "8.8.0"), or "unknown" if not available
urlstringConnection URL or address (e.g. "127.0.0.1:5432")

logServiceStartup(url): void

Log the service's own startup — name/version are already carried by the logger's mandatory [service#version] tag, so only the URL is needed.

ParameterTypeDescription
urlstringBase URL the service is listening on

makeDbMigrationRouteHandler(boot, state): RouteHandler

ParameterTypeDescription
bootDbMigrationBootOptions
stateMigrationBootState

makeEntityRouter(config): RouteHandler

ParameterTypeDescription
configEntityRouterConfig<TEntity>

makeNatsRequestRoute(routes, deps): Promise<Subscription[]>

Register a governed request/reply-style endpoint over pure pub/sub: the caller publishes with reply: <private inbox>; the response is published back there. Auth + RBAC enforced exactly like makeNatsRoutes; denials/failures reply with { error: { code, message } }.

ParameterTypeDescription
routesNatsRequestRoute<TReq, TRes>[]
depsNatsRouterDeps

makeNatsRoutes(routes, deps): Promise<Subscription[]>

Register governed pub/sub subscribers. Errors are logged, never thrown into the message loop — fire-and-forget has no caller to answer to.

ParameterTypeDescription
routesNatsRoute<unknown>[]
depsNatsRouterDeps

makeOpenApiHandler(spec): RouteHandler

RouteHandler serving GET /api/v1/openapi.json with the given spec.

ParameterTypeDescription
specRecord<string, unknown>

makeRpcRouter(routes, deps): RouteHandler

Build a routeHandler for createHttpServer/createMicroservice from a list of governed route declarations. Returns true when a route handled the request, false when no path matched (chaining, same contract as the legacy composite handlers).

Fails fast at construction time on invalid declarations — an endpoint without permissions that isn't explicitly auth: "public" never exists.

ParameterTypeDescription
routesRpcRoute<unknown, Record<string, string>>[]
depsRpcRouterDeps

makeServiceRoutes(input): ServiceRoutesHandler

Build a service's full route surface from one declaration: entity CRUD routers, RPC routers, and the OpenAPI spec handler — spec paths derived from the same route metadata, so a route that exists but isn't documented (or vice versa) is impossible.

ParameterTypeDescription
inputServiceRoutesInput

mapDalError(err, instance): MappedDalError \| null

Map a DAL error to an RFC 7807 response {status, body}. Returns null for errors the DAL does not own (caller falls back to its own error path — ApiError, database-unavailable, generic 500…).

ParameterTypeDescription
errunknown
instancestring

matchesWildcard(pattern, permission): boolean

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

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

max(a, b): Decimal

ParameterTypeDescription
aDecimalInput
bDecimalInput

min(a, b): Decimal

ParameterTypeDescription
aDecimalInput
bDecimalInput

mod(a, b): Decimal

a mod b — remainder of truncated division at the common scale.

ParameterTypeDescription
aDecimalInput
bDecimalInput

msgHeader(hdrs, name): string \| null

Case-insensitive lookup over a case-sensitive MsgHdrs. Tries the name as given first, then the canonical MIME form (each dash segment capitalized). Returns null when absent.

ParameterTypeDescription
hdrsMsgHdrs | undefined
namestring

mul(a, b): Decimal

ParameterTypeDescription
aDecimalInput
bDecimalInput

neg(d): Decimal

ParameterTypeDescription
dDecimalInput

normalize(d): Decimal

Strip trailing fractional zeros — 1.230 → 1.23, 12.000 → 12.

ParameterTypeDescription
dDecimalInput

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

operationIdFor(method, path): string

operationId from method + path, snake_case: GET /api/v1/entities/provider/list → get_provider_list POST /api/v1/entities/provider → create_provider GET /api/v1/ai/conversations/ → get_ai_conversations_uuid

ParameterTypeDescription
methodstring
pathstring

orderedColumns(columns): MetaColumn[]

Columns in display order: sticky group → normal group → audited group, each sorted by order.

ParameterTypeDescription
columnstypeOperator | null | undefined

parseBracketQuery(params): Record<string, unknown>

ParameterTypeDescription
paramsURLSearchParams

parseDecimal(input): Decimal

Parse a decimal string into unscaled form. Accepts: "12", "-0.5", ".5", "5.", "1.5e3", "1E-3", "+4". Rejects: NaN, Infinity, empty strings, hex, non-numeric garbage. Scale is preserved as written ("1.230" → scale 3).

ParameterTypeDescription
inputstring

parseDocsPackPayload(raw): DocsPackPayload

ParameterTypeDescription
rawunknown

pgDivScale(a, b): number

PostgreSQL-faithful result scale for x / y (mirrors select_div_scale): max(16 - 4·qweight, x.scale, y.scale) — 16 significant digits minimum. Verified live: 1/3→20, 10/4→16, 1.00/8→20, 2/1→16, 100/3→16.

ParameterTypeDescription
aDecimalInput
bDecimalInput

pgServerBanner(probe, databaseUrl): Promise<{ name: string; version: string \| null; url: string }>

PostgreSQL startup banner probe for createMicroservice's dbBanner option. Returns the same {name, version, url} the BE logs at boot: PostgreSQL 18.4 connected (postgres://user@host:5432/db). The connection URL is printed with the password stripped.

ParameterTypeDescription
probeSqlVersionProbe
databaseUrlstring

pow10(n): bigint

ParameterTypeDescription
nnumber

presenceSubject(entityType, entityUuid): string

Build the NATS subject for presence deltas on an entity. Format: presence.{entityType}.{entityUuid}

ParameterTypeDescription
entityTypestring
entityUuidstring

publishEntityChanged(nc, marker): Promise<void>

Publish an entity-changed marker on the NATS entity.{entityType}.{entityUuid}.changed subject. The BE calls this from the audit-port-adapter hook after a successful save.

nc is the NatsClient class (publish is a static method).

ParameterTypeDescription
nctypeof NatsClient
markerEntityChangedMarker

publishPresence(nc, entityType, entityUuid, delta): Promise<void>

Publish a presence delta on the NATS presence.{entityType}.{entityUuid} subject. The BE calls this after updating Redis via PresencePort.

nc is the NatsClient class (publish is a static method). Pass the class itself: publishPresence(NatsClient, entityType, entityUuid, delta).

ParameterTypeDescription
nctypeof NatsClient
entityTypestring
entityUuidstring
deltaPresenceDelta

readServiceVersion(): string

Read service version from the deployed package.json — resolved by walking up from the entry file (detectPackageIdentity), so it is cwd-independent and always the same package as the service identity.

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

rescale(d, newScale, mode): Decimal

Re-express d at newScale. Growing is exact; shrinking applies mode (default HALF_UP, matching PostgreSQL round()).

ParameterTypeDescription
dDecimalInput
newScalenumber
modeRoundingMode

resetInternalClient(): void

Test hook — clears the configured provider.

resetOidcRuntimeForTest(): void

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

resetSdkCachePort(): void

Clear the singleton (for tests or graceful shutdown).

resolvePresetConditions(presets, selections): FilterCondition[]

Flatten the WHERE conditions of every preset's ACTIVE option — the conditions AND-ed into every /list call. selections maps preset.id → option id; invalid/absent ids resolve per resolvePresetSelection.

ParameterTypeDescription
presetstypeOperator | undefined
selectionsRecord<string, string>

resolvePresetSelection(preset, selected): string

Resolve a preset's active option id — the caller-provided selection when it names a declared option, otherwise default_option (itself falling back to the first option when misconfigured, keeping the always-one invariant rather than failing closed).

ParameterTypeDescription
presetFilterPreset
selectedstring | undefined

resolveServiceCode(identity, configuredCode): string

Canonical service code — STATIC identity resolved once at boot from the deployed artifact, never from runtime-editable config:

  1. package.json "service_code" (explicit override)
  2. derived from package name (primebrick-<code> → <code>)

Everything downstream — service_registry.code, /ws/<code> proxy routes, the i18n namespace (<code>.*), the NATS/schema ownership claim — is generated from this value. A DB service_code config row may still exist (legacy seeds): it is treated as a drift tripwire — when present and different from the static identity, boot fails loudly instead of silently re-identifing the service under a mutated code.

ParameterTypeDescription
identityPackageIdentity
configuredCodestring | null

resolveVendorSpecifier(specifier): VendorResolution

How a remote chunk's bundler must treat a bare import specifier. Both the host vendor build and every module chunk build derive behavior from this single function so the plane stays deterministic.

ParameterTypeDescription
specifierstring

restartTelemetry(cfg, serviceName, serviceVersion): Promise<void>

Rebuild the telemetry pipeline with new config — no process restart. Serialized through a promise queue: concurrent restarts collapse into sequential apply. Old processors shut down (flushing pending batches to the OLD endpoint) before the delegate swap.

ParameterTypeDescription
cfgTelemetryConfig
serviceNamestring
serviceVersionstring

round(d, scale, mode): Decimal

Round to scale fractional digits (default HALF_UP).

ParameterTypeDescription
dDecimalInput
scalenumber
modeRoundingMode

rpcSpec(routes): Record<string, unknown>

Convert declared routes into an OpenAPI paths fragment. Routes without an openapi field are documented with path+method+security only — enough for discovery; summary upgrades them to full documentation.

ParameterTypeDescription
routesOpenApiRouteDoc[]

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

runDbMigrationStep(opts): Promise<MigrationBootState>

ParameterTypeDescription
optsDbMigrationBootOptions

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

sanitizeVisibleKeys(visibleKeys, columns): string[]

Drop unknown keys and fall back to defaults when nothing left.

ParameterTypeDescription
visibleKeysstring[]
columnsMetaColumn[]

searchableTextColumns(columns): MetaColumn[]

Text-physical searchable columns — the scope selector + default "all fields" search set.

ParameterTypeDescription
columnsMetaColumn[]

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

serializeConfigValue(type, value): string

Serialize a typed value back to string for DB storage.

  • bigint → String(value)
  • number → String(value)
  • string → value
ParameterTypeDescription
typeConfigType
valuestring | number | bigint

serviceApiBase(listBasePath): string

Service API base of an entity list basePath — strips the trailing /entities/<entity> segment so service-relative endpoints declared in meta (e.g. sparkline endpoint: "/actions/job-run-stats") resolve to /ws/<svc>/api/v1/actions/… or /api/v1/actions/… for BE entities.

ParameterTypeDescription
listBasePathstring

serviceShieldVerify(msg): Promise<null \| undefined>

NatsRouteVerify adapter — use as verify on service-identity routes (no AuthUser): verify: serviceShieldVerify.

ParameterTypeDescription
msgMsg

setClientShieldSecretSlot(s): void

ParameterTypeDescription
sClientShieldSecretSlot | null

setLogOptions(opts): void

Hot-swap logging options (from config reload / config.changed).

ParameterTypeDescription
opts{ level: LogLevel; format: LogFormat; service: string; version: string }

setNatsShieldRegistry(r): void

ParameterTypeDescription
rShieldRegistryLike | null

setOtelLogSink(sink): void

ParameterTypeDescription
sinkLogSink | null

setSdkCachePort(port, info): void

Store the cache port and Redis info (called once after Redis connects).

ParameterTypeDescription
portCachePort | null
infoRedisInfo | null

setSharedConfig(config): void

ParameterTypeDescription
configSharedConfig

shutdownTelemetry(): Promise<void>

Flush pending batches + shutdown current providers (graceful shutdown).

signum(d): -1 \| 0 \| 1

ParameterTypeDescription
dDecimalInput

specifierTopPackage(specifier): string

Top-level npm package of a specifier ('svelte/internal/client' → 'svelte').

ParameterTypeDescription
specifierstring

startSecretProvider(provider): Promise<void>

Wire the consumer: subscribe to secret.rotated (shielded broadcast), refetch and re-cache. Call once per service after NATS + shield registry are up, then provider.get(ref) for each consumed secret.

ParameterTypeDescription
providerSecretProvider

strictEq(a, b): boolean

Exact structural equality — value AND scale must match (strictEq(1.0, 1.00) is false).

ParameterTypeDescription
aDecimalInput
bDecimalInput

sub(a, b): Decimal

ParameterTypeDescription
aDecimalInput
bDecimalInput

subscribeConfigChanged(nats, handler): Promise<void>

Microservice side: subscribe to config.changed broadcasts. The handler fires on every notification — it should re-fetch the shared config and apply changes (telemetry restart, logger options, etc.).

ParameterTypeDescription
natstypeof NatsClient
handler(payload: { keys: string[] }) => Promise<void>

subscribeSharedConfig(nats, getConfig, verify): Promise<void>

BE side: subscribe to config.get on NATS and respond with the shared config object.

The getConfig function is called on each request — the BE passes a function that reads from its auth config (already loaded in memory via getAuthConfig()).

ParameterTypeDescription
natstypeof NatsClientThe NatsClient class (uses the singleton connection)
getConfig() => SharedConfigFunction that returns the current SharedConfig

Example: await subscribeSharedConfig(NatsClient, () => { const cfg = getAuthConfig(); return { redis_url: cfg.redis_url }; }); | | verify | NatsRouteVerify | |

subscribeShielded(subject, handler, opts): Promise<void>

Shield-gated fire-and-forget subscription for broadcast subjects (config.changed, service.gateway_online, lifecycle events): the handler runs only after the publisher's client identity is verified. Use this — never raw NatsClient.subscribe — for subjects published by other services.

ParameterTypeDescription
subjectstring
handler(payload: T, msg: Msg) => Promise<unknown>
opts{ queue: string }

systemAuthUser(initiator): AuthUser

Synthetic is_system AuthUser for service-initiated outbound calls — the governed path for operations with no acting user (welcome emails on self-registration, scheduled jobs, lifecycle work). Same shape the is_system API-key path produces in verify-api-key.ts: RBAC bypasses via isSystem, so the call must ALWAYS be additionally gated by the client shield (it is — verifyNatsPublisher runs first on every governed route).

initiator records WHO triggered the send (the real session actor when one exists, else the service code) — it is audit metadata, not identity.

ParameterTypeDescription
initiatorstring

toPG(d): string

PostgreSQL parameter form — plain canonical decimal string, no d suffix.

ParameterTypeDescription
dDecimalInput

toWire(d): string

Wire form — "…d" suffixed string.

ParameterTypeDescription
dDecimalInput

translationKeyNamespace(schema): string

Owning PG schema → the only translation-key namespace writable there.

ParameterTypeDescription
schemastring

trunc(d): Decimal

Integer part, toward zero.

ParameterTypeDescription
dDecimalInput

typeCapabilities(type): TypeCapabilities

Accessor — returns the capability record for a type.

ParameterTypeDescription
typeConfigType

validateConfigValue(type, type_config, value, config_key): void

Validate a config value against its type and type_config.validation rules.

ParameterTypeDescription
typeConfigTypeConfigType of the row (string, integer, url, secret, etc.)
type_configstring | null | undefinedRaw type_config JSON string from the DB row
valuestringThe raw string value to validate
config_keystringThe config key (for error context)

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

validateNavDeclaration(code, decl, partOf): string \| null

Validate a service's self-declared endpoints.nav contract — called at service.register time so a malformed declaration is REJECTED at enrollment and can never poison /modules or the routes census. Returns the first error string, or null when valid.

partOf is the module's declared ROOT LEVEL — its primary route prefix is derived, never free-chosen: /{partOf}/{code}. When the module IS its own root (partOf === code, e.g. crm) the segment collapses to /{code} — /crm/customers, not /crm/crm/customers. route_prefixes must CONTAIN that primary prefix and may add EXTRA root memberships in the /{root}/{code} shape only — a module with a foot in two apps keeps its code as the URL's second segment (/system/crm/pipeline = a crm page inside the System app), so longest-prefix ownership keeps resolving module and breadcrumb. Single-segment extras and foreign-code segments are rejected. Every nav href / routes entry must live under SOME declared prefix.

ParameterTypeDescription
codestring
declunknown
partOfstring

validClientShieldSecrets(graceMs, fallback): string[]

All values a subscriber may accept right now (current + in-grace previous).

ParameterTypeDescription
graceMsnumber
fallbackstring

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

verifyClientIdentity(req, opts): Promise<ClientIdentityResult>

Verify an inbound internal call's identity headers.

  • ua missing → 401 UNIDENTIFIED_CLIENT
  • ua not starting with an allowed prefix → 403 UNKNOWN_CLIENT
  • verifyKey(key) false → 401 INVALID_CLIENT_SHIELD_KEY

allowedPrefixes is loaded from system.client_shield_registry (cached by the caller and refreshed on system.client_shield_registry.changed events). verifyKey compares the presented key against the registry entry for the matched prefix (hash comparison — inject the hasher).

ParameterTypeDescription
req{ headers: { get: } }
opts{ allowedPrefixes: typeOperator; verifyKey: (uaPrefix: string, presentedKey: string) => boolean | Promise<boolean> }

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

verifyNatsPublisher(msg): Promise<boolean>

Verify the publisher's client-shield identity. Returns true when the caller is a registered client (or the registry is not configured — test contexts only). Denials are logged with the presented UA.

ParameterTypeDescription
msg{ subject: string; headers: MsgHdrs }

verifyRowKey(row, presentedKey): boolean

Verify a presented key against a registry row: current hash always wins; during the post-rotation grace window (previous_valid_until in the future) the previous hash is still accepted so in-flight callers are not cut off mid-rotation.

ParameterTypeDescription
rowClientKeyMaterial | undefined
presentedKeystring

withCache(repo, port, logger): R

Wrap a Repository with best-effort Redis caching. Returns the same Repository instance with findById, findByUUID, find, and all write methods overridden.

If port throws on any call, the wrapper logs a warn and falls through to the underlying Repository method. The cache is a feature, not a requirement.

Only entities marked @Cached() are cached. Other entities pass through untouched.

ParameterTypeDescription
repoRA DAL Repository (or any object structurally compatible with CacheableRepository)
portCachePortCachePort implementation (e.g. RedisCachePort)
loggerCacheLoggerOptional logger — if absent, warnings are swallowed

wrapCacheEntry(data, etag): CacheEntry<T>

Wrap a value + ETag into a CacheEntry for Redis storage. If etag is omitted, it is computed via computeETag.

ParameterTypeDescription
dataT
etagstring
Last modified on October 28, 2026
Telemetry & LoggingGetting started
On this page
  • Classes
    • AuthError
    • CacheKeyBuilder
    • CachingApiKeyPort
    • ClientShieldRegistry
    • ConfigLoader
    • ConfigValidationError
    • Decimal
    • DecimalError
    • GracefulShutdown
    • HealthCheck
    • HttpHeaderProvider
    • MfaConsumeUnavailableError
    • MfaStepUpRequiredError
    • NatsApiKeyPort
    • NatsClient
    • NatsHeaderProvider
    • RbacDeniedError
    • RedisCachePort
    • RedisPresencePort
    • RotatingSecretEngine
    • SecretProvider
    • ServiceRegistrar
    • TranslationsCache
    • TranslationsService
    • ValidationError
  • Interfaces
    • AdvancedFilter
    • ApiKeyPort
    • ApiKeyRecord
    • AuthConfig
    • AuthConfigPort
    • AuthPorts
    • BuildDocsPackOptions
    • CacheableRepository
    • CacheEntry
    • CacheLogger
    • CachePort
    • CacheResponseMeta
    • ClientKeyMaterial
    • ClientShieldRegistryRow
    • ClientShieldSecretSlot
    • CollaborationMeta
    • ConfigRepositoryPort
    • ConfigTypeMoneyConfig
    • ConfigTypePhoneConfig
    • ConfigTypeUrlConfig
    • ConfigValidation
    • ConfigValidationRules
    • CurrencyInfo
    • DbMigrationBootOptions
    • DbMigrationEnginePort
    • DocsPackContentFile
    • DocsPackFile
    • DocsPackManifest
    • DocsPackPayload
    • EnsureJobScheduleOpts
    • EntityAction
    • EntityActionEndpoint
    • EntityActionMeta
    • EntityChangedMarker
    • EntityMeta
    • EntityRouteDecl
    • EntityRouterConfig
    • EntityServiceLike
    • EnvSchema
    • EnvValidationResult
    • GatewayConfig
    • GeneratedDoc
    • GenerateDocsContext
    • HeaderProvider
    • HealthCheckPort
    • HealthCheckResult
    • HealthResponse
    • HttpServerOptions
    • IConfigEntity
    • IServiceRegistry
    • ListFilterParamsOptions
    • ListResponse
    • LogMeta
    • MappedDalError
    • MfaStepUpSpec
    • MicroserviceContext
    • MicroserviceOptions
    • MigrationBootState
    • MigrationEngineOptsLike
    • MigrationRunResultLike
    • NatsRequestRoute
    • NatsRoute
    • NatsRouteContext
    • NatsRouterDeps
    • NatsSseBridgeMapping
    • NormalizedIdpUser
    • OidcConfig
    • OpenApiSpecInput
    • PackageIdentity
    • PresenceDelta
    • PresenceEntry
    • PresencePort
    • PresenceSignal
    • PresenceSnapshot
    • RbacResult
    • ResolveInput
    • Rfc7807Body
    • RoleMappingEntry
    • RoleMappingPort
    • RotatingSecretRef
    • RowCustomAction
    • RpcRoute
    • RpcRouteContext
    • RpcRouteOpenApi
    • RpcRouterDeps
    • SecretGetRequest
    • SecretGetResponse
    • ServiceHealthCheck
    • ServiceHeartbeatPayload
    • ServiceRegisterPayload
    • ServiceRegistrarConfig
    • ServiceRegistryPort
    • ServiceRoutesInput
    • ServiceStalePayload
    • ServiceUnregisterPayload
    • Session
    • SharedConfig
    • ShieldRegistryLike
    • SqlVersionProbe
    • SseEvent
    • SseEventBus
    • SseEventBusSubscription
    • SseWriter
    • TableMeta
    • TelemetryConfig
    • TelemetrySharedConfig
    • TranslationRow
    • TranslationsRepository
    • TranslationsServiceOptions
    • TypeCapabilities
    • UserResolverPort
    • ValidationCapabilities
    • ValidationRuleEmail
    • ValidationRuleMax
    • ValidationRuleMin
    • ValidationRuleRegex
    • ValidationRuleUrl
  • Types & Enums
    • ActionsOverrides
    • ApiKeyCacheSource
    • AuthMode
    • AuthUser
    • BoundsKind
    • CacheBootstrapResult
    • CacheScope
    • CleanupFn
    • ClientIdentityResult
    • ConfigEntry
    • ConfigEntryType
    • ConfigType
    • DbEnv
    • DecimalInput
    • DeletionFilterMode
    • EntityActionSentinel
    • EntityCrudOp
    • EntityMetaResponse
    • FilterCondition
    • FilterOperator
    • FilterPreset
    • FilterPresetOption
    • GenerateDocsFn
    • HealthCheckFn
    • HostApiFn
    • HostMountHandle
    • I18nDict
    • InternalIdentityProvider
    • JsonSchema
    • JwtClaims
    • ListConnector
    • LogFormat
    • LogLevel
    • MetaColumn
    • MetaColumnType
    • MfaConsumeFn
    • MfaConsumeResult
    • MfaStepUpFn
    • ModuleHostBase
    • ModuleHostComponents
    • ModuleNavDeclaration
    • ModuleNavLink
    • ModuleNotifyInput
    • ModulePageHost
    • ModuleSheetDecl
    • ModuleSheetDefaults
    • ModuleSheetHost
    • ModuleSheetSide
    • MountPageFn
    • MountSectionFn
    • MountSheetFn
    • NatsRouteVerify
    • OpenApiRouteDoc
    • OpenSheetFn
    • PageLayoutDecl
    • PageRouteComponentFormat
    • PageRouteDeclaration
    • PageRouteKind
    • PageRouteProps
    • PageSectionDecl
    • PageSectionSource
    • Permission
    • PresenceAction
    • PresenceStatus
    • Queryable
    • RedisInfo
    • RemoteDepsManifest
    • RoundingMode
    • RouteHandler
    • ServiceRoutesHandler
    • SnakeCaseSingular
    • SortDir
    • StandardOp
    • TooltipPriority
    • VendorManifest
    • VendorMismatch
    • VendorResolution
    • VendorSpecifier
    • ViewName
    • WidgetCapability
  • Constants
    • API_KEY_CACHE_TTL_MS
    • AUTH_MFA_CONSUME_SUBJECT
    • AuthMode
    • CACHE_CONTROL_CACHED
    • CACHE_HEADERS
    • CLIENT_SHIELD_KEY_HEADER
    • CLIENT_SHIELD_REGISTRY_SUBJECTS
    • CLIENT_SHIELD_SECRET_REF
    • CONFIG_CHANGED_SUBJECT
    • consumeMfaActionAuthorization
    • DOCS_PACK_CAPABILITY
    • DOCS_PACK_ROUTE
    • DOCS_PACK_SCHEMA_VERSION
    • DOCS_PACK_SHIELD_HEADER
    • fromPG
    • fromString
    • fromWire
    • JOB_SCHEDULE_HEADERS
    • JOB_SCHEDULE_REQUEST_SUBJECT
    • JOB_STREAM_SUBJECTS
    • logger
    • MFA_ACTION_AUTH_HEADER
    • MIGRATION_ROUTE_PREFIX
    • natsCarrier
    • NUMERIC_MIN_SIG_DIGITS
    • NUMERIC_TYPES
    • ONE
    • PAGE_ROUTE_KINDS
    • Permission
    • recordCarrier
    • resetAuthConfigForTest
    • SECRET_DEFAULTS
    • SECRET_SUBJECTS
    • SERVICE_SUBJECTS
    • SHARED_CONFIG_SUBJECT
    • SINGULAR_NAV_ALLOWLIST
    • SSE_HEADERS
    • STANDARD_OPS
    • STATIC_SERVICE_CONFIG_KEYS
    • STRING_DERIVED_TYPES
    • SYSTEM_ACTOR
    • TEN
    • TYPE_CAPABILITIES
    • VENDOR_BLOCKED_PACKAGES
    • VENDOR_SPECIFIERS
    • ZERO
  • Functions
    • abs(d): Decimal
    • accessLeafKey(ns, relPath): string
    • accessNamespace(code, partOf): string
    • accessSheetKey(ns, code, sheetKey): string
    • add(a, b): Decimal
    • allowedKeyNamespaces(moduleCode): ReadonlySet<string>
    • apiKeyCacheKey(hash): string
    • appendListFilterParams(params, opts, startIdx): number
    • assertDbEnv(cwd): DbEnv
    • assertMfaStepUp(req, spec, user, consume): Promise<void>
    • assertWritableConfigKey(key): void
    • asSnakeCaseSingular(value): SnakeCaseSingular
    • brandRouteHandler(h): RouteHandler
    • bridgeNatsToSse(nats, bus, mappings): Promise<() => void>
    • buildAuthUser(internalUuid, normalized, permissions, isAdmin): AuthUser
    • buildDocsPack(opts): Promise<DocsPackManifest>
    • buildNatsAuthHeaders(user, config): Record<string, string>
    • buildOpenApiSpec(input): Record<string, unknown>
    • buildUserAgent(identity, capabilities): string
    • Cached(ttl): ClassDecorator
    • CacheKey(): PropertyDecorator
    • callNats(subject, data, opts): Promise<TRes>
    • cancelJobSchedule(scheduleSubject, cancelSubject): Promise<bigint>
    • ceil(d): Decimal
    • checkRbac(user, requiredPermissions, mode): RbacResult
    • clientKeyHash(key): string
    • closeRedisClient(): Promise<void>
    • cmp(a, b): -1 \| 0 \| 1
    • coerceConfigValue(type, value, type_config): string \| number \| bigint \| null
    • coerceRoles(raw): string[]
    • composeRouteHandlers(handlers): RouteHandler
    • computeETag(value): string
    • computeVersionETag(version): string
    • configureInternalClient(p): void
    • createHttpServer(options): Promise<Server<typeof IncomingMessage, typeof ServerResponse>>
    • createMicroservice(options): Promise<MicroserviceContext>
    • createRedisClient(url): Promise<RedisClientType>
    • createRedisHealthCheck(getCachePort, getVersion): () => Promise<HealthCheckResult>
    • createSseEventBus(): SseEventBus
    • createSseWriter(res): SseWriter
    • currencyDecimals(code): number
    • currencySymbol(code): string
    • currentClientShieldSecret(fallback): string \| undefined
    • dec(input): Decimal
    • defaultCurrencyForLang(lang): string
    • defaultSearchGate(trimmed): boolean
    • defaultVisibleColumnKeys(columns): string[]
    • deriveClientKey(derivationSecret, uaPrefix, generation): string
    • deserializeAuthUserFromHeaders(headers, config): AuthUser
    • detectPackageIdentity(startDir): PackageIdentity
    • diffVendorRequirements(requires, manifest): VendorMismatch[]
    • div(a, b, opts): Decimal
    • docsPackRouteHandler(packDir): RouteHandler
    • dueLaneVerify(lane, permissions): (msg: { subject: string; headers: MsgHdrs }) => Promise<AuthUser \| null \| undefined>
    • enforceHttpRbac(user, requiredPermissions, mode): void
    • enforceNatsRbac(user, requiredPermissions, mode): void
    • ensureJobSchedule(opts): Promise<bigint>
    • entityChangedSubject(entityType, entityUuid): string
    • entityCrudSpec(entity, opts): Record<string, unknown>
    • entityPaths(prefix, listLeaf, detailParam): { list: string; create: string; detail: string }
    • eq(a, b): boolean
    • etagMatches(requestETag, cachedETag): boolean
    • expandPermissions(roles, getRoleMappingFn): Promise<{ patterns: string[]; isAdmin: boolean }>
    • extJsonBodyParser(opts): (req: Request, _res: Response, next: NextFunction) => Promise<void>
    • extJsonMiddleware(): (req: Request, res: Response, next: NextFunction) => void
    • extJsonParse(text): T
    • extJsonStringify(data): string
    • extractNatsContext(hdrs): Context
    • extractTraceContext(hdrs): Context
    • fetchDocsPack(serviceBaseUrl): Promise<DocsPackPayload>
    • fetchSharedConfig(nats): Promise<SharedConfig>
    • fetchTraced(input, init): Promise<Response>
    • floor(d): Decimal
    • flushLogsSync(): void
    • formatMoney(amount, lang, currency): string
    • forwardAuthHeaders(msg): Record<string, string>
    • fromBigInt(v, scale): Decimal
    • fromNumber(n): Decimal
    • generateApiKey(): { key: string; prefix: string }
    • getAllCurrencies(): CurrencyInfo[]
    • getAuthConfig(): AuthConfig
    • getCacheKeyProperty(ctor): string \| undefined
    • getClientShieldSecretSlot(): ClientShieldSecretSlot \| null
    • getCurrencyInfo(code): CurrencyInfo \| null
    • getEntityCacheTtl(ctor): number \| undefined
    • getLogOptions(): Readonly<LoggerState>
    • getNatsShieldRegistry(): ShieldRegistryLike \| null
    • getOperatorsForColumnType(columnType): FilterOperator[]
    • getRedisClient(): RedisClientType \| null
    • getRedisInfo(redis): Promise<RedisInfo \| null>
    • getSdkCachePort(): CachePort \| null
    • getSdkRedisInfo(): RedisInfo \| null
    • getSession(): Session \| undefined
    • getSharedConfig(): SharedConfig
    • getTelemetryConfig(): TelemetryConfig
    • hashApiKey(key): string
    • humanizeMs(ms): string
    • i18nCacheKey(schema, language): string
    • identityHeaders(identity, capabilities, clientKey): Record<string, string>
    • initAuthConfig(p): void
    • initCacheFromSharedConfig(nats, logger, prefetched): Promise<CacheBootstrapResult>
    • initTelemetry(cfg, serviceName, serviceVersion): Promise<void>
    • injectTraceHeaders(hdrs): Record<string, string>
    • installConsoleBridge(): void
    • internalFetch(url, init): Promise<Response>
    • invalidateAuthConfig(): void
    • isAccessGranted(userPermissions, required): boolean
    • isAccessGrantLegal(grant, leafKeys): boolean
    • isAccessKey(value): value is string
    • isDecimal(v): v is Decimal
    • isEntityCached(ctor): boolean
    • isEntityOpEnabled(actions, op): boolean
    • isFactoryRouteHandler(h): h is RouteHandler
    • isPeriodic(d): boolean
    • isPermissionGranted(userPermissions, requiredPermission): boolean
    • isPermissionSentinel(p): boolean
    • isSnakeCaseSingular(value): value is SnakeCaseSingular
    • keyNamespaceOf(key): string \| null
    • listNonSentinelPermissions(): string[]
    • loadAuthConfig(): Promise<AuthConfig>
    • logModuleStartup(name, version, url): void
    • logServiceStartup(url): void
    • makeDbMigrationRouteHandler(boot, state): RouteHandler
    • makeEntityRouter(config): RouteHandler
    • makeNatsRequestRoute(routes, deps): Promise<Subscription[]>
    • makeNatsRoutes(routes, deps): Promise<Subscription[]>
    • makeOpenApiHandler(spec): RouteHandler
    • makeRpcRouter(routes, deps): RouteHandler
    • makeServiceRoutes(input): ServiceRoutesHandler
    • mapDalError(err, instance): MappedDalError \| null
    • matchesWildcard(pattern, permission): boolean
    • max(a, b): Decimal
    • min(a, b): Decimal
    • mod(a, b): Decimal
    • msgHeader(hdrs, name): string \| null
    • mul(a, b): Decimal
    • neg(d): Decimal
    • normalize(d): Decimal
    • normalizeIdpToken(payload, rolesPath): NormalizedIdpUser
    • operationIdFor(method, path): string
    • orderedColumns(columns): MetaColumn[]
    • parseBracketQuery(params): Record<string, unknown>
    • parseDecimal(input): Decimal
    • parseDocsPackPayload(raw): DocsPackPayload
    • pgDivScale(a, b): number
    • pgServerBanner(probe, databaseUrl): Promise<{ name: string; version: string \| null; url: string }>
    • pow10(n): bigint
    • presenceSubject(entityType, entityUuid): string
    • publishEntityChanged(nc, marker): Promise<void>
    • publishPresence(nc, entityType, entityUuid, delta): Promise<void>
    • readServiceVersion(): string
    • requireActor(): string
    • requireEnv(schema): Record<string, string \| undefined>
    • rescale(d, newScale, mode): Decimal
    • resetInternalClient(): void
    • resetOidcRuntimeForTest(): void
    • resetSdkCachePort(): void
    • resolvePresetConditions(presets, selections): FilterCondition[]
    • resolvePresetSelection(preset, selected): string
    • resolveServiceCode(identity, configuredCode): string
    • resolveVendorSpecifier(specifier): VendorResolution
    • restartTelemetry(cfg, serviceName, serviceVersion): Promise<void>
    • round(d, scale, mode): Decimal
    • rpcSpec(routes): Record<string, unknown>
    • runAsSystem(fn): T
    • runDbMigrationStep(opts): Promise<MigrationBootState>
    • runWithSession(session, fn): T
    • sanitizeVisibleKeys(visibleKeys, columns): string[]
    • searchableTextColumns(columns): MetaColumn[]
    • serializeAuthUserToHeaders(user, config): Record<string, string>
    • serializeConfigValue(type, value): string
    • serviceApiBase(listBasePath): string
    • serviceShieldVerify(msg): Promise<null \| undefined>
    • setClientShieldSecretSlot(s): void
    • setLogOptions(opts): void
    • setNatsShieldRegistry(r): void
    • setOtelLogSink(sink): void
    • setSdkCachePort(port, info): void
    • setSharedConfig(config): void
    • shutdownTelemetry(): Promise<void>
    • signum(d): -1 \| 0 \| 1
    • specifierTopPackage(specifier): string
    • startSecretProvider(provider): Promise<void>
    • strictEq(a, b): boolean
    • sub(a, b): Decimal
    • subscribeConfigChanged(nats, handler): Promise<void>
    • subscribeSharedConfig(nats, getConfig, verify): Promise<void>
    • subscribeShielded(subject, handler, opts): Promise<void>
    • systemAuthUser(initiator): AuthUser
    • toPG(d): string
    • toWire(d): string
    • translationKeyNamespace(schema): string
    • trunc(d): Decimal
    • typeCapabilities(type): TypeCapabilities
    • validateConfigValue(type, type_config, value, config_key): void
    • validateEnv(schema): EnvValidationResult
    • validateNavDeclaration(code, decl, partOf): string \| null
    • validClientShieldSecrets(graceMs, fallback): string[]
    • verifyAccessToken(token, oidc): Promise<JwtClaims>
    • verifyApiKey(headers, apiKeyPort): Promise<AuthUser>
    • verifyAuth(headers, config, ports): Promise<AuthUser>
    • verifyAuthGatewayResolved(headers, config): Promise<AuthUser>
    • verifyClientIdentity(req, opts): Promise<ClientIdentityResult>
    • verifyHttpRequest(req, config, ports): Promise<AuthUser>
    • verifyNatsMessage(msg, config): Promise<AuthUser>
    • verifyNatsPublisher(msg): Promise<boolean>
    • verifyRowKey(row, presentedKey): boolean
    • withCache(repo, port, logger): R
    • wrapCacheEntry(data, etag): CacheEntry<T>
JSON
TypeScript