@stratum-hq/lib 0.7.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +109 -0
  2. package/dist/index.d.ts +6 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +36 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/migrations/020_usage_events.sql +55 -0
  7. package/dist/migrations/021_tenant_status_suspended.sql +12 -0
  8. package/dist/scoped-job.d.ts +57 -0
  9. package/dist/scoped-job.d.ts.map +1 -0
  10. package/dist/scoped-job.js +56 -0
  11. package/dist/scoped-job.js.map +1 -0
  12. package/dist/services/api-key-service.d.ts.map +1 -1
  13. package/dist/services/api-key-service.js +4 -1
  14. package/dist/services/api-key-service.js.map +1 -1
  15. package/dist/services/audit-service.d.ts +9 -1
  16. package/dist/services/audit-service.d.ts.map +1 -1
  17. package/dist/services/audit-service.js +18 -0
  18. package/dist/services/audit-service.js.map +1 -1
  19. package/dist/services/config-service.js +2 -2
  20. package/dist/services/config-service.js.map +1 -1
  21. package/dist/services/event-service.d.ts +1 -1
  22. package/dist/services/event-service.d.ts.map +1 -1
  23. package/dist/services/event-service.js +7 -6
  24. package/dist/services/event-service.js.map +1 -1
  25. package/dist/services/key-rotation-service.js +1 -1
  26. package/dist/services/key-rotation-service.js.map +1 -1
  27. package/dist/services/rate-limit-service.d.ts +135 -0
  28. package/dist/services/rate-limit-service.d.ts.map +1 -0
  29. package/dist/services/rate-limit-service.js +113 -0
  30. package/dist/services/rate-limit-service.js.map +1 -0
  31. package/dist/services/role-service.d.ts +13 -3
  32. package/dist/services/role-service.d.ts.map +1 -1
  33. package/dist/services/role-service.js +18 -5
  34. package/dist/services/role-service.js.map +1 -1
  35. package/dist/services/tenant-service.d.ts +34 -0
  36. package/dist/services/tenant-service.d.ts.map +1 -1
  37. package/dist/services/tenant-service.js +159 -11
  38. package/dist/services/tenant-service.js.map +1 -1
  39. package/dist/services/usage-service.d.ts +18 -0
  40. package/dist/services/usage-service.d.ts.map +1 -0
  41. package/dist/services/usage-service.js +80 -0
  42. package/dist/services/usage-service.js.map +1 -0
  43. package/dist/services/webhook-service.d.ts +9 -1
  44. package/dist/services/webhook-service.d.ts.map +1 -1
  45. package/dist/services/webhook-service.js +51 -0
  46. package/dist/services/webhook-service.js.map +1 -1
  47. package/dist/stratum.d.ts +51 -4
  48. package/dist/stratum.d.ts.map +1 -1
  49. package/dist/stratum.js +95 -1
  50. package/dist/stratum.js.map +1 -1
  51. package/package.json +9 -1
  52. package/dist/__tests__/crypto.test.d.ts +0 -2
  53. package/dist/__tests__/crypto.test.d.ts.map +0 -1
  54. package/dist/__tests__/crypto.test.js +0 -41
  55. package/dist/__tests__/crypto.test.js.map +0 -1
  56. package/dist/__tests__/migrate-schemas.test.d.ts +0 -2
  57. package/dist/__tests__/migrate-schemas.test.d.ts.map +0 -1
  58. package/dist/__tests__/migrate-schemas.test.js +0 -225
  59. package/dist/__tests__/migrate-schemas.test.js.map +0 -1
  60. package/dist/__tests__/stratum-als.test.d.ts +0 -2
  61. package/dist/__tests__/stratum-als.test.d.ts.map +0 -1
  62. package/dist/__tests__/stratum-als.test.js +0 -49
  63. package/dist/__tests__/stratum-als.test.js.map +0 -1
  64. package/dist/__tests__/webhook-signature.test.d.ts +0 -2
  65. package/dist/__tests__/webhook-signature.test.d.ts.map +0 -1
  66. package/dist/__tests__/webhook-signature.test.js +0 -103
  67. package/dist/__tests__/webhook-signature.test.js.map +0 -1
  68. package/dist/services/__tests__/abac-service.test.d.ts +0 -2
  69. package/dist/services/__tests__/abac-service.test.d.ts.map +0 -1
  70. package/dist/services/__tests__/abac-service.test.js +0 -108
  71. package/dist/services/__tests__/abac-service.test.js.map +0 -1
  72. package/dist/services/__tests__/api-key-service.test.d.ts +0 -2
  73. package/dist/services/__tests__/api-key-service.test.d.ts.map +0 -1
  74. package/dist/services/__tests__/api-key-service.test.js +0 -76
  75. package/dist/services/__tests__/api-key-service.test.js.map +0 -1
  76. package/dist/services/__tests__/audit-service.test.d.ts +0 -2
  77. package/dist/services/__tests__/audit-service.test.d.ts.map +0 -1
  78. package/dist/services/__tests__/audit-service.test.js +0 -230
  79. package/dist/services/__tests__/audit-service.test.js.map +0 -1
  80. package/dist/services/__tests__/config-service.test.d.ts +0 -2
  81. package/dist/services/__tests__/config-service.test.d.ts.map +0 -1
  82. package/dist/services/__tests__/config-service.test.js +0 -565
  83. package/dist/services/__tests__/config-service.test.js.map +0 -1
  84. package/dist/services/__tests__/consent-service.test.d.ts +0 -2
  85. package/dist/services/__tests__/consent-service.test.d.ts.map +0 -1
  86. package/dist/services/__tests__/consent-service.test.js +0 -169
  87. package/dist/services/__tests__/consent-service.test.js.map +0 -1
  88. package/dist/services/__tests__/event-service.test.d.ts +0 -2
  89. package/dist/services/__tests__/event-service.test.d.ts.map +0 -1
  90. package/dist/services/__tests__/event-service.test.js +0 -193
  91. package/dist/services/__tests__/event-service.test.js.map +0 -1
  92. package/dist/services/__tests__/key-rotation-service.test.d.ts +0 -2
  93. package/dist/services/__tests__/key-rotation-service.test.d.ts.map +0 -1
  94. package/dist/services/__tests__/key-rotation-service.test.js +0 -132
  95. package/dist/services/__tests__/key-rotation-service.test.js.map +0 -1
  96. package/dist/services/__tests__/permission-service.test.d.ts +0 -2
  97. package/dist/services/__tests__/permission-service.test.d.ts.map +0 -1
  98. package/dist/services/__tests__/permission-service.test.js +0 -480
  99. package/dist/services/__tests__/permission-service.test.js.map +0 -1
  100. package/dist/services/__tests__/retention-service.test.d.ts +0 -2
  101. package/dist/services/__tests__/retention-service.test.d.ts.map +0 -1
  102. package/dist/services/__tests__/retention-service.test.js +0 -152
  103. package/dist/services/__tests__/retention-service.test.js.map +0 -1
  104. package/dist/services/__tests__/role-service.test.d.ts +0 -2
  105. package/dist/services/__tests__/role-service.test.d.ts.map +0 -1
  106. package/dist/services/__tests__/role-service.test.js +0 -134
  107. package/dist/services/__tests__/role-service.test.js.map +0 -1
  108. package/dist/services/__tests__/tenant-service.test.d.ts +0 -2
  109. package/dist/services/__tests__/tenant-service.test.d.ts.map +0 -1
  110. package/dist/services/__tests__/tenant-service.test.js +0 -548
  111. package/dist/services/__tests__/tenant-service.test.js.map +0 -1
  112. package/dist/services/__tests__/test-helpers.d.ts +0 -9
  113. package/dist/services/__tests__/test-helpers.d.ts.map +0 -1
  114. package/dist/services/__tests__/test-helpers.js +0 -19
  115. package/dist/services/__tests__/test-helpers.js.map +0 -1
  116. package/dist/services/__tests__/webhook-service.test.d.ts +0 -2
  117. package/dist/services/__tests__/webhook-service.test.d.ts.map +0 -1
  118. package/dist/services/__tests__/webhook-service.test.js +0 -184
  119. package/dist/services/__tests__/webhook-service.test.js.map +0 -1
package/README.md CHANGED
@@ -46,6 +46,7 @@ The `Stratum` instance covers the full tenant lifecycle:
46
46
  - **API keys & roles** — `createApiKey`, `validateApiKey`, `rotateApiKey`, `createRole`, `assignRoleToKey`
47
47
  - **Webhooks & audit** — `createWebhook`, `testWebhook`, `queryAuditLogs`, `listFailedDeliveries`
48
48
  - **GDPR & regions** — `exportTenantData`, `purgeTenant`, `grantConsent`, `createRegion`, `migrateRegion`
49
+ - **Usage metering** — `recordUsage`, `aggregateUsage` (see [docs/usage-metering.md](../../docs/usage-metering.md))
49
50
 
50
51
  Low-level pool helpers are also exported:
51
52
 
@@ -57,6 +58,114 @@ await withTransaction(pool, async (client) => {
57
58
  });
58
59
  ```
59
60
 
61
+ ## Background jobs
62
+
63
+ `runScopedJob` runs a unit of background work bound to a single tenant. It
64
+ establishes the tenant context in two layers for the whole duration of the job
65
+ and tears both down when the job settles:
66
+
67
+ - **AsyncLocalStorage** — the tenant is placed in the SDK's ALS store, so code
68
+ inside the job reads it through `Stratum.currentTenantId()` /
69
+ `Stratum.currentTenantContext()` exactly as a request handler would. Each job
70
+ gets its own store, so concurrent jobs cannot observe each other's tenant.
71
+ - **Postgres row-level security** — the job runs through the data-plane
72
+ `withTenantContext` (`@stratum-hq/db-adapters`), which opens a transaction and
73
+ issues `SET LOCAL app.current_tenant_id`. Every query the job makes on the
74
+ provided client is confined to that tenant by RLS, so it cannot read or write
75
+ another tenant's rows even with a missing `WHERE tenant_id` filter. `SET LOCAL`
76
+ resets at COMMIT / ROLLBACK, so the context cannot leak onto the next job that
77
+ reuses the pooled connection.
78
+
79
+ ```typescript
80
+ import { runScopedJob } from "@stratum-hq/lib";
81
+
82
+ // `pool` must connect as a NON-superuser, NON-BYPASSRLS role, or RLS is a no-op.
83
+ await runScopedJob(pool, tenantId, async (client) => {
84
+ Stratum.currentTenantId(); // === tenantId
85
+ // Confined to `tenantId` by RLS — no app-layer WHERE filter needed.
86
+ await client.query("SELECT * FROM invoices WHERE status = 'pending'");
87
+ });
88
+ ```
89
+
90
+ Unlike `withClient` / `withTransaction` above (the control-plane path, which runs
91
+ under the audited `app.bypass_rls`), `runScopedJob` keeps the job **subject to**
92
+ tenant isolation. Use it for any tenant-scoped background work. Pass
93
+ `{ resolve }` to populate the full `ResolvedTenantContext` in the ALS store; by
94
+ default a minimal placeholder carrying only the tenant id is used. See
95
+ ADR 0001 (`docs/adr/0001-postgres-rls-defense-in-depth.md`).
96
+
97
+ ## Rate Limiting
98
+
99
+ `RateLimiter` is a standalone, storage-agnostic per-tenant rate-limiting primitive. It is not part of the `Stratum` facade (which is bound to a single `pg.Pool`) — the point is that its storage is pluggable. It ships with a process-local in-memory store and a documented `RateLimitStore` contract you can implement over Redis, Postgres, or anything else.
100
+
101
+ > This is distinct from the HTTP-layer rate limiting in `@stratum-hq/control-plane`. Use this to embed per-tenant limits directly in an application.
102
+
103
+ ```typescript
104
+ import { RateLimiter } from "@stratum-hq/lib";
105
+
106
+ const limiter = new RateLimiter({
107
+ defaultLimit: { limit: 100, windowMs: 60_000 }, // 100 requests / minute
108
+ limits: {
109
+ "tenant-vip": { limit: 1000, windowMs: 60_000 }, // static per-tenant override
110
+ },
111
+ });
112
+
113
+ // `key` sub-scopes the limit within a tenant (per-endpoint, per-user, ...).
114
+ // Omit it for one tenant-wide bucket. Counters are namespaced by tenant, so
115
+ // one tenant's usage never affects another's.
116
+ const res = await limiter.checkLimit(tenantId, "api");
117
+ if (!res.allowed) {
118
+ throw new Error(`rate limited — retry in ${res.retryAfter}s`);
119
+ }
120
+ // res: { allowed, limit, remaining, resetAt, retryAfter }
121
+ ```
122
+
123
+ It uses a fixed-window algorithm: at most `limit` hits per `windowMs`, resetting when the window elapses.
124
+
125
+ ### Effective limit resolution
126
+
127
+ For each tenant the limit is resolved in order:
128
+
129
+ 1. `resolveLimit(tenantId)` if provided and it returns a value,
130
+ 2. the static `limits[tenantId]` override,
131
+ 3. `defaultLimit`.
132
+
133
+ `resolveLimit` is the seam for **config inheritance** — back it with `stratum.resolveConfig` to drive limits from the tenant config tree:
134
+
135
+ ```typescript
136
+ const limiter = new RateLimiter({
137
+ defaultLimit: { limit: 100, windowMs: 60_000 },
138
+ async resolveLimit(tenantId) {
139
+ const cfg = await stratum.resolveConfig(tenantId);
140
+ const limit = cfg.rate_limit?.value as { limit: number; windowMs: number } | undefined;
141
+ return limit; // undefined falls through to defaultLimit
142
+ },
143
+ });
144
+ ```
145
+
146
+ ### Storage backend contract
147
+
148
+ Pass any `RateLimitStore` as `store` (defaults to `MemoryRateLimitStore`). A store provides one atomic-per-key operation:
149
+
150
+ ```typescript
151
+ interface RateLimitStore {
152
+ // Atomically increment `key`'s counter and return the new count and the
153
+ // window's reset time. If no live window exists (never seen, or the previous
154
+ // window has elapsed), begin a fresh window: count = 1, resetAt = now + windowMs.
155
+ // Otherwise increment and return the current window's unchanged resetAt.
156
+ // The read-modify-write MUST be atomic per key.
157
+ increment(key: string, windowMs: number): Promise<{ count: number; resetAt: number }>;
158
+ // Clear `key`'s counter so the next increment starts a new window.
159
+ reset(key: string): Promise<void>;
160
+ }
161
+ ```
162
+
163
+ Implementation notes:
164
+
165
+ - **Redis** — `INCR key`, then on a reply of `1`, `PEXPIRE key windowMs`; derive `resetAt` from `now + PTTL`. A short Lua script keeps it atomic and returns count + TTL in one round trip.
166
+ - **Postgres** — `INSERT ... ON CONFLICT (key) DO UPDATE` returning the new count, resetting the row when `reset_at` has passed.
167
+ - **In-memory** — `MemoryRateLimitStore`; single-process only, holds one entry per distinct `tenantId:key` pair. For high key cardinality or multi-process deployments, use a store with native TTL eviction.
168
+
60
169
  ## Error Handling
61
170
 
62
171
  All errors come from `@stratum-hq/core` and extend `StratumError`:
package/dist/index.d.ts CHANGED
@@ -2,15 +2,20 @@ export { Stratum, type StratumOptions } from "./stratum.js";
2
2
  export { migrate, type MigrateOptions } from "./migrate.js";
3
3
  export { migrateAllSchemas, type MigrateSchemasOptions, type MigrateSchemasResult, } from "./migrate-schemas.js";
4
4
  export { withClient, withTransaction } from "./pool-helpers.js";
5
+ export { runScopedJob, type ScopedJobFn, type RunScopedJobOptions, } from "./scoped-job.js";
5
6
  export { verifyWebhookSignature, signWebhookPayload, DEFAULT_WEBHOOK_TOLERANCE_SECONDS, type VerifyWebhookOptions, } from "./webhook-signature.js";
6
7
  export { traced, getTracer, isTracingEnabled } from "./telemetry.js";
8
+ export { ErrorCode, StratumError, TenantNotFoundError, TenantAlreadyExistsError, TenantHasChildrenError, TenantCycleDetectedError, TenantArchivedError, TenantSuspendedError, InvalidTenantStateError, TenantContextNotFoundError, IsolationViolationError, IsolationStrategyUnsupportedError, PermissionLockedError, PermissionNotFoundError, PermissionRevocationDeniedError, ConfigLockedError, ConfigNotFoundError, ValidationError, UnauthorizedError, ForbiddenError, WebhookNotFoundError, WebhookDeliveryError, WebhookUrlValidationError, AbacPolicyNotFoundError, InvalidAbacOperatorError, AbacPolicyLockedError, } from "@stratum-hq/core";
7
9
  export type { TenantNode, TenantContext, CreateTenantInput, UpdateTenantInput, MoveTenantInput, PaginationInput, PaginatedResult, ConfigEntry, SetConfigInput, ResolvedConfigEntry, ResolvedConfig, PermissionPolicy, CreatePermissionInput, UpdatePermissionInput, ResolvedPermission, } from "@stratum-hq/core";
10
+ export { RateLimiter, MemoryRateLimitStore } from "./services/rate-limit-service.js";
11
+ export type { RateLimit, RateLimitResult, RateLimitState, RateLimitStore, RateLimitResolver, RateLimiterOptions, } from "./services/rate-limit-service.js";
8
12
  export type { ApiKeyRecord, CreatedApiKey, ValidatedApiKey, CreateApiKeyOptions } from "./services/api-key-service.js";
9
13
  export type { BatchCreateResult } from "./services/tenant-service.js";
10
14
  export type { KeyRotationResult } from "./services/key-rotation-service.js";
11
15
  export type { DeliveryStats } from "./services/event-service.js";
12
16
  export type { Role, CreateRoleInput, UpdateRoleInput } from "./services/role-service.js";
13
- export type { AuditContext, AuditEntry, AuditLogQuery } from "@stratum-hq/core";
17
+ export type { AuditContext, AuditEntry, AuditLogQuery, RecordAuditEventInput } from "@stratum-hq/core";
18
+ export type { RecordUsageInput, UsageEvent, UsageAggregate, UsageAggregateQuery, } from "@stratum-hq/core";
14
19
  export { defaultLogger, noopLogger } from "./logger.js";
15
20
  export type { StratumLogger } from "./logger.js";
16
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,iCAAiC,EACjC,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGrE,YAAY,EACV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACvH,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGzF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,iCAAiC,EACjC,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKrE,OAAO,EACL,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,iCAAiC,EACjC,qBAAqB,EACrB,uBAAuB,EACvB,+BAA+B,EAC/B,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACrF,YAAY,EACV,SAAS,EACT,eAAe,EACf,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACvH,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGzF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAGvG,YAAY,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noopLogger = exports.defaultLogger = exports.isTracingEnabled = exports.getTracer = exports.traced = exports.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports.signWebhookPayload = exports.verifyWebhookSignature = exports.withTransaction = exports.withClient = exports.migrateAllSchemas = exports.migrate = exports.Stratum = void 0;
3
+ exports.noopLogger = exports.defaultLogger = exports.MemoryRateLimitStore = exports.RateLimiter = exports.AbacPolicyLockedError = exports.InvalidAbacOperatorError = exports.AbacPolicyNotFoundError = exports.WebhookUrlValidationError = exports.WebhookDeliveryError = exports.WebhookNotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.ValidationError = exports.ConfigNotFoundError = exports.ConfigLockedError = exports.PermissionRevocationDeniedError = exports.PermissionNotFoundError = exports.PermissionLockedError = exports.IsolationStrategyUnsupportedError = exports.IsolationViolationError = exports.TenantContextNotFoundError = exports.InvalidTenantStateError = exports.TenantSuspendedError = exports.TenantArchivedError = exports.TenantCycleDetectedError = exports.TenantHasChildrenError = exports.TenantAlreadyExistsError = exports.TenantNotFoundError = exports.StratumError = exports.ErrorCode = exports.isTracingEnabled = exports.getTracer = exports.traced = exports.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports.signWebhookPayload = exports.verifyWebhookSignature = exports.runScopedJob = exports.withTransaction = exports.withClient = exports.migrateAllSchemas = exports.migrate = exports.Stratum = void 0;
4
4
  var stratum_js_1 = require("./stratum.js");
5
5
  Object.defineProperty(exports, "Stratum", { enumerable: true, get: function () { return stratum_js_1.Stratum; } });
6
6
  var migrate_js_1 = require("./migrate.js");
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "migrateAllSchemas", { enumerable: true, get: fun
10
10
  var pool_helpers_js_1 = require("./pool-helpers.js");
11
11
  Object.defineProperty(exports, "withClient", { enumerable: true, get: function () { return pool_helpers_js_1.withClient; } });
12
12
  Object.defineProperty(exports, "withTransaction", { enumerable: true, get: function () { return pool_helpers_js_1.withTransaction; } });
13
+ var scoped_job_js_1 = require("./scoped-job.js");
14
+ Object.defineProperty(exports, "runScopedJob", { enumerable: true, get: function () { return scoped_job_js_1.runScopedJob; } });
13
15
  var webhook_signature_js_1 = require("./webhook-signature.js");
14
16
  Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return webhook_signature_js_1.verifyWebhookSignature; } });
15
17
  Object.defineProperty(exports, "signWebhookPayload", { enumerable: true, get: function () { return webhook_signature_js_1.signWebhookPayload; } });
@@ -18,6 +20,39 @@ var telemetry_js_1 = require("./telemetry.js");
18
20
  Object.defineProperty(exports, "traced", { enumerable: true, get: function () { return telemetry_js_1.traced; } });
19
21
  Object.defineProperty(exports, "getTracer", { enumerable: true, get: function () { return telemetry_js_1.getTracer; } });
20
22
  Object.defineProperty(exports, "isTracingEnabled", { enumerable: true, get: function () { return telemetry_js_1.isTracingEnabled; } });
23
+ // Re-export core error classes as runtime VALUES (not `export type`) so consumers
24
+ // can branch with `instanceof` instead of matching error-message substrings. A
25
+ // type-only re-export would erase these at compile time and break instanceof.
26
+ var core_1 = require("@stratum-hq/core");
27
+ Object.defineProperty(exports, "ErrorCode", { enumerable: true, get: function () { return core_1.ErrorCode; } });
28
+ Object.defineProperty(exports, "StratumError", { enumerable: true, get: function () { return core_1.StratumError; } });
29
+ Object.defineProperty(exports, "TenantNotFoundError", { enumerable: true, get: function () { return core_1.TenantNotFoundError; } });
30
+ Object.defineProperty(exports, "TenantAlreadyExistsError", { enumerable: true, get: function () { return core_1.TenantAlreadyExistsError; } });
31
+ Object.defineProperty(exports, "TenantHasChildrenError", { enumerable: true, get: function () { return core_1.TenantHasChildrenError; } });
32
+ Object.defineProperty(exports, "TenantCycleDetectedError", { enumerable: true, get: function () { return core_1.TenantCycleDetectedError; } });
33
+ Object.defineProperty(exports, "TenantArchivedError", { enumerable: true, get: function () { return core_1.TenantArchivedError; } });
34
+ Object.defineProperty(exports, "TenantSuspendedError", { enumerable: true, get: function () { return core_1.TenantSuspendedError; } });
35
+ Object.defineProperty(exports, "InvalidTenantStateError", { enumerable: true, get: function () { return core_1.InvalidTenantStateError; } });
36
+ Object.defineProperty(exports, "TenantContextNotFoundError", { enumerable: true, get: function () { return core_1.TenantContextNotFoundError; } });
37
+ Object.defineProperty(exports, "IsolationViolationError", { enumerable: true, get: function () { return core_1.IsolationViolationError; } });
38
+ Object.defineProperty(exports, "IsolationStrategyUnsupportedError", { enumerable: true, get: function () { return core_1.IsolationStrategyUnsupportedError; } });
39
+ Object.defineProperty(exports, "PermissionLockedError", { enumerable: true, get: function () { return core_1.PermissionLockedError; } });
40
+ Object.defineProperty(exports, "PermissionNotFoundError", { enumerable: true, get: function () { return core_1.PermissionNotFoundError; } });
41
+ Object.defineProperty(exports, "PermissionRevocationDeniedError", { enumerable: true, get: function () { return core_1.PermissionRevocationDeniedError; } });
42
+ Object.defineProperty(exports, "ConfigLockedError", { enumerable: true, get: function () { return core_1.ConfigLockedError; } });
43
+ Object.defineProperty(exports, "ConfigNotFoundError", { enumerable: true, get: function () { return core_1.ConfigNotFoundError; } });
44
+ Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return core_1.ValidationError; } });
45
+ Object.defineProperty(exports, "UnauthorizedError", { enumerable: true, get: function () { return core_1.UnauthorizedError; } });
46
+ Object.defineProperty(exports, "ForbiddenError", { enumerable: true, get: function () { return core_1.ForbiddenError; } });
47
+ Object.defineProperty(exports, "WebhookNotFoundError", { enumerable: true, get: function () { return core_1.WebhookNotFoundError; } });
48
+ Object.defineProperty(exports, "WebhookDeliveryError", { enumerable: true, get: function () { return core_1.WebhookDeliveryError; } });
49
+ Object.defineProperty(exports, "WebhookUrlValidationError", { enumerable: true, get: function () { return core_1.WebhookUrlValidationError; } });
50
+ Object.defineProperty(exports, "AbacPolicyNotFoundError", { enumerable: true, get: function () { return core_1.AbacPolicyNotFoundError; } });
51
+ Object.defineProperty(exports, "InvalidAbacOperatorError", { enumerable: true, get: function () { return core_1.InvalidAbacOperatorError; } });
52
+ Object.defineProperty(exports, "AbacPolicyLockedError", { enumerable: true, get: function () { return core_1.AbacPolicyLockedError; } });
53
+ var rate_limit_service_js_1 = require("./services/rate-limit-service.js");
54
+ Object.defineProperty(exports, "RateLimiter", { enumerable: true, get: function () { return rate_limit_service_js_1.RateLimiter; } });
55
+ Object.defineProperty(exports, "MemoryRateLimitStore", { enumerable: true, get: function () { return rate_limit_service_js_1.MemoryRateLimitStore; } });
21
56
  var logger_js_1 = require("./logger.js");
22
57
  Object.defineProperty(exports, "defaultLogger", { enumerable: true, get: function () { return logger_js_1.defaultLogger; } });
23
58
  Object.defineProperty(exports, "noopLogger", { enumerable: true, get: function () { return logger_js_1.noopLogger; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA4D;AAAnD,qGAAA,OAAO,OAAA;AAChB,2CAA4D;AAAnD,qGAAA,OAAO,OAAA;AAChB,2DAI8B;AAH5B,uHAAA,iBAAiB,OAAA;AAInB,qDAAgE;AAAvD,6GAAA,UAAU,OAAA;AAAE,kHAAA,eAAe,OAAA;AACpC,+DAKgC;AAJ9B,8HAAA,sBAAsB,OAAA;AACtB,0HAAA,kBAAkB,OAAA;AAClB,yIAAA,iCAAiC,OAAA;AAGnC,+CAAqE;AAA5D,sGAAA,MAAM,OAAA;AAAE,yGAAA,SAAS,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AA8B5C,yCAAwD;AAA/C,0GAAA,aAAa,OAAA;AAAE,uGAAA,UAAU,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA4D;AAAnD,qGAAA,OAAO,OAAA;AAChB,2CAA4D;AAAnD,qGAAA,OAAO,OAAA;AAChB,2DAI8B;AAH5B,uHAAA,iBAAiB,OAAA;AAInB,qDAAgE;AAAvD,6GAAA,UAAU,OAAA;AAAE,kHAAA,eAAe,OAAA;AACpC,iDAIyB;AAHvB,6GAAA,YAAY,OAAA;AAId,+DAKgC;AAJ9B,8HAAA,sBAAsB,OAAA;AACtB,0HAAA,kBAAkB,OAAA;AAClB,yIAAA,iCAAiC,OAAA;AAGnC,+CAAqE;AAA5D,sGAAA,MAAM,OAAA;AAAE,yGAAA,SAAS,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAE5C,kFAAkF;AAClF,+EAA+E;AAC/E,8EAA8E;AAC9E,yCA2B0B;AA1BxB,iGAAA,SAAS,OAAA;AACT,oGAAA,YAAY,OAAA;AACZ,2GAAA,mBAAmB,OAAA;AACnB,gHAAA,wBAAwB,OAAA;AACxB,8GAAA,sBAAsB,OAAA;AACtB,gHAAA,wBAAwB,OAAA;AACxB,2GAAA,mBAAmB,OAAA;AACnB,4GAAA,oBAAoB,OAAA;AACpB,+GAAA,uBAAuB,OAAA;AACvB,kHAAA,0BAA0B,OAAA;AAC1B,+GAAA,uBAAuB,OAAA;AACvB,yHAAA,iCAAiC,OAAA;AACjC,6GAAA,qBAAqB,OAAA;AACrB,+GAAA,uBAAuB,OAAA;AACvB,uHAAA,+BAA+B,OAAA;AAC/B,yGAAA,iBAAiB,OAAA;AACjB,2GAAA,mBAAmB,OAAA;AACnB,uGAAA,eAAe,OAAA;AACf,yGAAA,iBAAiB,OAAA;AACjB,sGAAA,cAAc,OAAA;AACd,4GAAA,oBAAoB,OAAA;AACpB,4GAAA,oBAAoB,OAAA;AACpB,iHAAA,yBAAyB,OAAA;AACzB,+GAAA,uBAAuB,OAAA;AACvB,gHAAA,wBAAwB,OAAA;AACxB,6GAAA,qBAAqB,OAAA;AAsBvB,0EAAqF;AAA5E,oHAAA,WAAW,OAAA;AAAE,6HAAA,oBAAoB,OAAA;AA2B1C,yCAAwD;AAA/C,0GAAA,aAAa,OAAA;AAAE,uGAAA,UAAU,OAAA"}
@@ -0,0 +1,55 @@
1
+ -- Migration 020: per-tenant usage metering (FR-58).
2
+ --
3
+ -- Countable usage events (a metric plus a quantity) per tenant, aggregated per
4
+ -- metric over a time window for billing / quota. Adjacent to audit_logs (005)
5
+ -- but append-heavy and numeric. `occurred_at` is the event time that
6
+ -- aggregation windows filter on (caller-supplied, so a back-dated import lands
7
+ -- in the right window); `recorded_at` is ingest time.
8
+ --
9
+ -- `idempotency_key` makes a record idempotent per (tenant_id, metric): a
10
+ -- retried write with the same key is a no-op, so an at-least-once producer
11
+ -- cannot double count. Enforced by a partial unique index; NULL keys are
12
+ -- unconstrained and can repeat freely.
13
+ --
14
+ -- The table is tenant-scoped, so it gets the same row-level-security
15
+ -- defense-in-depth as migration 019: ENABLE + FORCE + a fail-closed
16
+ -- tenant_isolation policy. See docs/adr/0001-postgres-rls-defense-in-depth.md.
17
+
18
+ CREATE TABLE IF NOT EXISTS usage_events (
19
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
20
+ tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
21
+ metric TEXT NOT NULL,
22
+ quantity BIGINT NOT NULL DEFAULT 1 CHECK (quantity >= 0),
23
+ idempotency_key TEXT,
24
+ metadata JSONB NOT NULL DEFAULT '{}',
25
+ occurred_at TIMESTAMPTZ NOT NULL DEFAULT now(),
26
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT now()
27
+ );
28
+
29
+ -- Aggregation is always WHERE tenant_id = ? [AND metric = ?] over an
30
+ -- occurred_at window, grouped by metric. This composite index serves that path.
31
+ CREATE INDEX IF NOT EXISTS idx_usage_tenant_metric_time
32
+ ON usage_events (tenant_id, metric, occurred_at);
33
+
34
+ -- Idempotency: at most one row per (tenant_id, metric, idempotency_key) when a
35
+ -- key is supplied. Partial so unkeyed events (idempotency_key IS NULL) do not
36
+ -- collide.
37
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_usage_idempotency
38
+ ON usage_events (tenant_id, metric, idempotency_key)
39
+ WHERE idempotency_key IS NOT NULL;
40
+
41
+ -- RLS: same fail-closed tenant_isolation shape as migration 019. With no
42
+ -- context set the predicate is NULL and zero rows are visible; a write with no
43
+ -- context is rejected.
44
+ ALTER TABLE usage_events ENABLE ROW LEVEL SECURITY;
45
+ ALTER TABLE usage_events FORCE ROW LEVEL SECURITY;
46
+ DROP POLICY IF EXISTS tenant_isolation ON usage_events;
47
+ CREATE POLICY tenant_isolation ON usage_events FOR ALL
48
+ USING (
49
+ current_setting('app.bypass_rls', true) = 'on'
50
+ OR tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid
51
+ )
52
+ WITH CHECK (
53
+ current_setting('app.bypass_rls', true) = 'on'
54
+ OR tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid
55
+ );
@@ -0,0 +1,12 @@
1
+ -- Tenant lifecycle: add the 'suspended' state (FR-58).
2
+ -- The original inline CHECK on tenants.status (001_init.sql) allowed only
3
+ -- 'active' and 'archived'. Relax it to also permit 'suspended', the reversible
4
+ -- "blocks access" state introduced by suspendTenant/resumeTenant.
5
+ --
6
+ -- The inline constraint from 001_init.sql is named tenants_status_check by
7
+ -- Postgres. Drop it if present and re-add the widened constraint under the same
8
+ -- name so the schema stays stable.
9
+ ALTER TABLE tenants DROP CONSTRAINT IF EXISTS tenants_status_check;
10
+ ALTER TABLE tenants
11
+ ADD CONSTRAINT tenants_status_check
12
+ CHECK (status IN ('active', 'suspended', 'archived'));
@@ -0,0 +1,57 @@
1
+ import type pg from "pg";
2
+ import type { ResolvedTenantContext } from "@stratum-hq/core";
3
+ /**
4
+ * The unit of work a background job runs. It receives the RLS-scoped pooled
5
+ * client bound to the job's tenant; every query issued on this client is
6
+ * confined to that tenant by Postgres row-level security.
7
+ */
8
+ export type ScopedJobFn<T> = (client: pg.PoolClient) => Promise<T>;
9
+ export interface RunScopedJobOptions {
10
+ /**
11
+ * Populate the full {@link ResolvedTenantContext} placed in the
12
+ * AsyncLocalStorage store for the job. Job code can then read it through
13
+ * `Stratum.currentTenantContext()` exactly as a request handler does.
14
+ *
15
+ * When omitted, a minimal placeholder context carrying only the tenant
16
+ * identity is used: `resolved_config` and `resolved_permissions` are empty.
17
+ * This mirrors the default in `@stratum-hq/hono`'s middleware. The isolation
18
+ * boundary (the RLS `app.current_tenant_id`) does NOT depend on this callback;
19
+ * it only enriches what in-job code can read from the ALS context.
20
+ */
21
+ resolve?: (tenantId: string) => ResolvedTenantContext | Promise<ResolvedTenantContext>;
22
+ }
23
+ /**
24
+ * Runs a background job bound to a single tenant.
25
+ *
26
+ * A job scoped with this helper carries its tenant context in two layers, both
27
+ * established before `fn` runs and both torn down when it settles:
28
+ *
29
+ * 1. AsyncLocalStorage: the tenant context is placed in the SDK's ALS store via
30
+ * `runWithTenantContext`, so code inside the job sees the same tenant through
31
+ * `Stratum.currentTenantId()` / `Stratum.currentTenantContext()` that a
32
+ * request handler would. Each job gets its own store; concurrent jobs cannot
33
+ * observe each other's tenant (async isolation), and the store is gone once
34
+ * the job settles.
35
+ * 2. Postgres row-level security: the work runs through the data-plane
36
+ * `withTenantContext` (packages/db-adapters), which opens a transaction and
37
+ * issues `SET LOCAL app.current_tenant_id`. Every query `fn` makes on the
38
+ * provided client is confined to that tenant by RLS, so a job cannot read or
39
+ * write another tenant's rows even if it forgets a `WHERE tenant_id` filter.
40
+ * `SET LOCAL` resets at COMMIT / ROLLBACK, so the context cannot leak onto the
41
+ * next job that reuses the pooled connection.
42
+ *
43
+ * This is deliberately NOT the control-plane bypass path (`withClient` /
44
+ * `withTransaction` in this package, which set `app.bypass_rls`). Background
45
+ * jobs are tenant-scoped data-plane work and must stay subject to isolation.
46
+ * See ADR 0001 (docs/adr/0001-postgres-rls-defense-in-depth.md).
47
+ *
48
+ * @param pool A pool whose connections use a NON-superuser, NON-BYPASSRLS
49
+ * role. A superuser connection bypasses RLS and the scoping is a
50
+ * no-op.
51
+ * @param tenantId The tenant the job is confined to.
52
+ * @param fn The job body; receives the tenant-scoped client.
53
+ * @param options Optionally resolve the full ALS context (see
54
+ * {@link RunScopedJobOptions.resolve}).
55
+ */
56
+ export declare function runScopedJob<T>(pool: pg.Pool, tenantId: string, fn: ScopedJobFn<T>, options?: RunScopedJobOptions): Promise<T>;
57
+ //# sourceMappingURL=scoped-job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-job.d.ts","sourceRoot":"","sources":["../src/scoped-job.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAIzB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,KACb,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC7D;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,EAClB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,CAAC,CAAC,CAOZ"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runScopedJob = runScopedJob;
4
+ const sdk_1 = require("@stratum-hq/sdk");
5
+ const db_adapters_1 = require("@stratum-hq/db-adapters");
6
+ const core_1 = require("@stratum-hq/core");
7
+ function placeholderContext(tenantId) {
8
+ return {
9
+ tenant_id: tenantId,
10
+ ancestry_path: tenantId,
11
+ depth: 0,
12
+ resolved_config: {},
13
+ resolved_permissions: {},
14
+ isolation_strategy: core_1.IsolationStrategy.SHARED_RLS,
15
+ };
16
+ }
17
+ /**
18
+ * Runs a background job bound to a single tenant.
19
+ *
20
+ * A job scoped with this helper carries its tenant context in two layers, both
21
+ * established before `fn` runs and both torn down when it settles:
22
+ *
23
+ * 1. AsyncLocalStorage: the tenant context is placed in the SDK's ALS store via
24
+ * `runWithTenantContext`, so code inside the job sees the same tenant through
25
+ * `Stratum.currentTenantId()` / `Stratum.currentTenantContext()` that a
26
+ * request handler would. Each job gets its own store; concurrent jobs cannot
27
+ * observe each other's tenant (async isolation), and the store is gone once
28
+ * the job settles.
29
+ * 2. Postgres row-level security: the work runs through the data-plane
30
+ * `withTenantContext` (packages/db-adapters), which opens a transaction and
31
+ * issues `SET LOCAL app.current_tenant_id`. Every query `fn` makes on the
32
+ * provided client is confined to that tenant by RLS, so a job cannot read or
33
+ * write another tenant's rows even if it forgets a `WHERE tenant_id` filter.
34
+ * `SET LOCAL` resets at COMMIT / ROLLBACK, so the context cannot leak onto the
35
+ * next job that reuses the pooled connection.
36
+ *
37
+ * This is deliberately NOT the control-plane bypass path (`withClient` /
38
+ * `withTransaction` in this package, which set `app.bypass_rls`). Background
39
+ * jobs are tenant-scoped data-plane work and must stay subject to isolation.
40
+ * See ADR 0001 (docs/adr/0001-postgres-rls-defense-in-depth.md).
41
+ *
42
+ * @param pool A pool whose connections use a NON-superuser, NON-BYPASSRLS
43
+ * role. A superuser connection bypasses RLS and the scoping is a
44
+ * no-op.
45
+ * @param tenantId The tenant the job is confined to.
46
+ * @param fn The job body; receives the tenant-scoped client.
47
+ * @param options Optionally resolve the full ALS context (see
48
+ * {@link RunScopedJobOptions.resolve}).
49
+ */
50
+ async function runScopedJob(pool, tenantId, fn, options = {}) {
51
+ const context = options.resolve
52
+ ? await options.resolve(tenantId)
53
+ : placeholderContext(tenantId);
54
+ return (0, sdk_1.runWithTenantContext)(context, () => (0, db_adapters_1.withTenantContext)(pool, tenantId, fn));
55
+ }
56
+ //# sourceMappingURL=scoped-job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-job.js","sourceRoot":"","sources":["../src/scoped-job.ts"],"names":[],"mappings":";;AA0EA,oCAYC;AArFD,yCAAuD;AACvD,yDAA4D;AAC5D,2CAAqD;AA2BrD,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,OAAO;QACL,SAAS,EAAE,QAAQ;QACnB,aAAa,EAAE,QAAQ;QACvB,KAAK,EAAE,CAAC;QACR,eAAe,EAAE,EAAE;QACnB,oBAAoB,EAAE,EAAE;QACxB,kBAAkB,EAAE,wBAAiB,CAAC,UAAU;KACjD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACI,KAAK,UAAU,YAAY,CAChC,IAAa,EACb,QAAgB,EAChB,EAAkB,EAClB,UAA+B,EAAE;IAEjC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;QAC7B,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;QACjC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACjC,OAAO,IAAA,0BAAoB,EAAC,OAAO,EAAE,GAAG,EAAE,CACxC,IAAA,+BAAiB,EAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CACtC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"api-key-service.d.ts","sourceRoot":"","sources":["../../src/services/api-key-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-key-service.d.ts","sourceRoot":"","sources":["../../src/services/api-key-service.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ exports.getApiKey = getApiKey;
13
13
  exports.listDormantKeys = listDormantKeys;
14
14
  const node_crypto_1 = __importDefault(require("node:crypto"));
15
15
  const pool_helpers_js_1 = require("../pool-helpers.js");
16
+ const role_service_js_1 = require("./role-service.js");
16
17
  // --- Hashing ---
17
18
  const HASH_V1_SHA256 = 1;
18
19
  const HASH_V2_HMAC = 2;
@@ -99,7 +100,9 @@ async function validateApiKey(pool, key) {
99
100
  return {
100
101
  tenant_id: row.tenant_id,
101
102
  key_id: row.id,
102
- scopes: row.scopes ?? ["read"],
103
+ // Resolve through the single source so an assigned role governs the
104
+ // key's scopes at the auth boundary, exactly as resolveKeyScopes does.
105
+ scopes: await (0, role_service_js_1.resolveEffectiveScopes)(pool, row.id),
103
106
  rate_limit_max: row.rate_limit_max,
104
107
  rate_limit_window: row.rate_limit_window,
105
108
  };
@@ -1 +1 @@
1
- {"version":3,"file":"api-key-service.js","sourceRoot":"","sources":["../../src/services/api-key-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-key-service.js","sourceRoot":"","sources":["../../src/services/api-key-service.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,14 @@
1
1
  import pg from "pg";
2
- import type { AuditContext, AuditEntry, AuditLogQuery } from "@stratum-hq/core";
2
+ import type { AuditContext, AuditEntry, AuditLogQuery, RecordAuditEventInput } from "@stratum-hq/core";
3
3
  export declare function createAuditEntry(pool: pg.Pool, context: AuditContext, action: string, resourceType: string, resourceId: string | null, tenantId: string | null, beforeState?: Record<string, unknown> | null, afterState?: Record<string, unknown> | null, metadata?: Record<string, unknown>): Promise<AuditEntry>;
4
+ /**
5
+ * Append a custom audit event through the public surface. Validates the
6
+ * consumer-facing input and maps it onto the same createAuditEntry write path
7
+ * the internal services use, so a recorded event is indistinguishable from one
8
+ * Stratum writes itself and is queryable via queryAuditLogs. The row is stamped
9
+ * for input.tenantId and no other tenant.
10
+ */
11
+ export declare function recordAuditEvent(pool: pg.Pool, input: RecordAuditEventInput): Promise<AuditEntry>;
4
12
  export declare function queryAuditLogs(pool: pg.Pool, query: AuditLogQuery): Promise<AuditEntry[]>;
5
13
  export declare function getAuditEntry(pool: pg.Pool, id: string): Promise<AuditEntry | null>;
6
14
  //# sourceMappingURL=audit-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit-service.d.ts","sourceRoot":"","sources":["../../src/services/audit-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEhF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC5C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,UAAU,CAAC,CAwBrB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAkDvB;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAY5B"}
1
+ {"version":3,"file":"audit-service.d.ts","sourceRoot":"","sources":["../../src/services/audit-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpB,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,qBAAqB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC5C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,UAAU,CAAC,CAwBrB;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAkBrB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAkDvB;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAY5B"}
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAuditEntry = createAuditEntry;
4
+ exports.recordAuditEvent = recordAuditEvent;
4
5
  exports.queryAuditLogs = queryAuditLogs;
5
6
  exports.getAuditEntry = getAuditEntry;
6
7
  const pool_helpers_js_1 = require("../pool-helpers.js");
8
+ const core_1 = require("@stratum-hq/core");
7
9
  async function createAuditEntry(pool, context, action, resourceType, resourceId, tenantId, beforeState, afterState, metadata) {
8
10
  return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
9
11
  const res = await client.query(`INSERT INTO audit_logs (actor_id, actor_type, action, resource_type, resource_id, tenant_id, source_ip, request_id, before_state, after_state, metadata)
@@ -26,6 +28,22 @@ async function createAuditEntry(pool, context, action, resourceType, resourceId,
26
28
  return res.rows[0];
27
29
  });
28
30
  }
31
+ /**
32
+ * Append a custom audit event through the public surface. Validates the
33
+ * consumer-facing input and maps it onto the same createAuditEntry write path
34
+ * the internal services use, so a recorded event is indistinguishable from one
35
+ * Stratum writes itself and is queryable via queryAuditLogs. The row is stamped
36
+ * for input.tenantId and no other tenant.
37
+ */
38
+ async function recordAuditEvent(pool, input) {
39
+ const parsed = core_1.RecordAuditEventInputSchema.parse(input);
40
+ const context = {
41
+ actor_id: parsed.actorId,
42
+ actor_type: parsed.actorType,
43
+ source_ip: parsed.sourceIp ?? undefined,
44
+ };
45
+ return createAuditEntry(pool, context, parsed.action, parsed.resourceType, parsed.resourceId, parsed.tenantId, parsed.before ?? null, parsed.after ?? null, parsed.metadata);
46
+ }
29
47
  async function queryAuditLogs(pool, query) {
30
48
  return (0, pool_helpers_js_1.withClient)(pool, async (client) => {
31
49
  const conditions = [];
@@ -1 +1 @@
1
- {"version":3,"file":"audit-service.js","sourceRoot":"","sources":["../../src/services/audit-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit-service.js","sourceRoot":"","sources":["../../src/services/audit-service.ts"],"names":[],"mappings":";;AAUA,4CAkCC;AASD,4CAqBC;AAED,wCAqDC;AAED,sCAeC;AAjJD,wDAAgD;AAChD,2CAA+D;AAQxD,KAAK,UAAU,gBAAgB,CACpC,IAAa,EACb,OAAqB,EACrB,MAAc,EACd,YAAoB,EACpB,UAAyB,EACzB,QAAuB,EACvB,WAA4C,EAC5C,UAA2C,EAC3C,QAAkC;IAElC,OAAO,IAAA,4BAAU,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B;;;;gDAI0C,EAC1C;YACE,OAAO,CAAC,QAAQ;YAChB,OAAO,CAAC,UAAU;YAClB,MAAM;YACN,YAAY;YACZ,UAAU;YACV,QAAQ;YACR,OAAO,CAAC,SAAS,IAAI,IAAI;YACzB,OAAO,CAAC,UAAU,IAAI,IAAI;YAC1B,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI;YAChD,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;YAC9C,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC;SAC/B,CACF,CAAC;QACF,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,gBAAgB,CACpC,IAAa,EACb,KAA4B;IAE5B,MAAM,MAAM,GAAG,kCAA2B,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,OAAO,GAAiB;QAC5B,QAAQ,EAAE,MAAM,CAAC,OAAO;QACxB,UAAU,EAAE,MAAM,CAAC,SAAS;QAC5B,SAAS,EAAE,MAAM,CAAC,QAAQ,IAAI,SAAS;KACxC,CAAC;IACF,OAAO,gBAAgB,CACrB,IAAI,EACJ,OAAO,EACP,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,UAAU,EACjB,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,MAAM,IAAI,IAAI,EACrB,MAAM,CAAC,KAAK,IAAI,IAAI,EACpB,MAAM,CAAC,QAAQ,CAChB,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,cAAc,CAClC,IAAa,EACb,KAAoB;IAEpB,OAAO,IAAA,4BAAU,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACvC,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,IAAI,GAAG,GAAG,CAAC,CAAC;QAEZ,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,UAAU,CAAC,IAAI,CAAC,gBAAgB,GAAG,EAAE,EAAE,CAAC,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,UAAU,CAAC,IAAI,CAAC,aAAa,GAAG,EAAE,EAAE,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACtC,UAAU,CAAC,IAAI,CAAC,oBAAoB,GAAG,EAAE,EAAE,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,UAAU,CAAC,IAAI,CAAC,eAAe,GAAG,EAAE,EAAE,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC,kBAAkB,GAAG,EAAE,EAAE,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC3B,UAAU,CAAC,IAAI,CAAC,kBAAkB,GAAG,EAAE,EAAE,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,UAAU,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;QAEhC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B;;;;SAIG,KAAK;;gBAEE,GAAG,EAAE,EACf,MAAM,CACP,CAAC;QACF,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAEM,KAAK,UAAU,aAAa,CACjC,IAAa,EACb,EAAU;IAEV,OAAO,IAAA,4BAAU,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B;;;;qBAIe,EACf,CAAC,EAAE,CAAC,CACL,CAAC;QACF,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -54,7 +54,7 @@ async function resolveConfig(pool, tenantId) {
54
54
  }
55
55
  const isCurrentTenant = currentTenantId === tenantId;
56
56
  const resolvedValue = entry.sensitive
57
- ? JSON.parse((0, crypto_js_1.decrypt)(JSON.parse(entry.value)))
57
+ ? JSON.parse((0, crypto_js_1.decrypt)(entry.value))
58
58
  : entry.value;
59
59
  resolved.set(entry.key, {
60
60
  key: entry.key,
@@ -229,7 +229,7 @@ async function getConfigWithInheritance(pool, tenantId) {
229
229
  }
230
230
  }
231
231
  const decryptEntryValue = (entry) => entry.sensitive
232
- ? JSON.parse((0, crypto_js_1.decrypt)(JSON.parse(entry.value)))
232
+ ? JSON.parse((0, crypto_js_1.decrypt)(entry.value))
233
233
  : entry.value;
234
234
  const result = {};
235
235
  for (const [key, rec] of byKey) {