pipework 0.8.20 → 0.10.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 (109) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/async/jobs/abortable-delay.d.ts +3 -0
  3. package/dist/async/jobs/abortable-delay.d.ts.map +1 -0
  4. package/dist/async/jobs/abortable-delay.js +19 -0
  5. package/dist/async/jobs/abortable-delay.js.map +1 -0
  6. package/dist/async/jobs/exclusion.d.ts +25 -0
  7. package/dist/async/jobs/exclusion.d.ts.map +1 -0
  8. package/dist/async/jobs/exclusion.js +80 -0
  9. package/dist/async/jobs/exclusion.js.map +1 -0
  10. package/dist/async/jobs/index.d.ts +2 -1
  11. package/dist/async/jobs/index.d.ts.map +1 -1
  12. package/dist/async/jobs/index.js.map +1 -1
  13. package/dist/async/jobs/queue.d.ts +26 -2
  14. package/dist/async/jobs/queue.d.ts.map +1 -1
  15. package/dist/async/jobs/queue.js +129 -36
  16. package/dist/async/jobs/queue.js.map +1 -1
  17. package/dist/async/jobs/table.d.ts +14 -0
  18. package/dist/async/jobs/table.d.ts.map +1 -0
  19. package/dist/async/jobs/table.js +115 -0
  20. package/dist/async/jobs/table.js.map +1 -0
  21. package/dist/auth/tenant/index.d.ts +1 -1
  22. package/dist/auth/tenant/index.d.ts.map +1 -1
  23. package/dist/auth/tenant/index.js +1 -1
  24. package/dist/auth/tenant/index.js.map +1 -1
  25. package/dist/auth/tenant/namespace.d.ts +3 -3
  26. package/dist/auth/tenant/namespace.d.ts.map +1 -1
  27. package/dist/auth/tenant/namespace.js +3 -3
  28. package/dist/auth/tenant/namespace.js.map +1 -1
  29. package/dist/auth/tenant/rls.d.ts +24 -3
  30. package/dist/auth/tenant/rls.d.ts.map +1 -1
  31. package/dist/auth/tenant/rls.js +59 -19
  32. package/dist/auth/tenant/rls.js.map +1 -1
  33. package/dist/cli/commands/db.d.ts +8 -0
  34. package/dist/cli/commands/db.d.ts.map +1 -1
  35. package/dist/cli/commands/db.js +129 -72
  36. package/dist/cli/commands/db.js.map +1 -1
  37. package/dist/core/config/load.d.ts +2 -0
  38. package/dist/core/config/load.d.ts.map +1 -1
  39. package/dist/core/config/load.js +7 -3
  40. package/dist/core/config/load.js.map +1 -1
  41. package/dist/core/config/namespace.d.ts +3 -0
  42. package/dist/core/config/namespace.d.ts.map +1 -1
  43. package/dist/core/config/schema.d.ts +6 -0
  44. package/dist/core/config/schema.d.ts.map +1 -1
  45. package/dist/core/config/schema.js +3 -0
  46. package/dist/core/config/schema.js.map +1 -1
  47. package/dist/core/pipework.d.ts +3 -3
  48. package/dist/core/pipework.d.ts.map +1 -1
  49. package/dist/core/pipework.js +13 -5
  50. package/dist/core/pipework.js.map +1 -1
  51. package/dist/core/surface/worker.d.ts.map +1 -1
  52. package/dist/core/surface/worker.js +26 -9
  53. package/dist/core/surface/worker.js.map +1 -1
  54. package/dist/data/db/expressions.d.ts +17 -5
  55. package/dist/data/db/expressions.d.ts.map +1 -1
  56. package/dist/data/db/expressions.js +30 -5
  57. package/dist/data/db/expressions.js.map +1 -1
  58. package/dist/data/db/filter.d.ts +9 -1
  59. package/dist/data/db/filter.d.ts.map +1 -1
  60. package/dist/data/db/filter.js +9 -1
  61. package/dist/data/db/filter.js.map +1 -1
  62. package/dist/data/db/namespace.d.ts +8 -0
  63. package/dist/data/db/namespace.d.ts.map +1 -1
  64. package/dist/data/db/pool.d.ts +2 -0
  65. package/dist/data/db/pool.d.ts.map +1 -1
  66. package/dist/data/db/pool.js.map +1 -1
  67. package/dist/data/db/serializable.d.ts +2 -0
  68. package/dist/data/db/serializable.d.ts.map +1 -1
  69. package/dist/data/db/serializable.js +2 -1
  70. package/dist/data/db/serializable.js.map +1 -1
  71. package/dist/data/domain/build-factory.js +2 -0
  72. package/dist/data/domain/build-factory.js.map +1 -1
  73. package/dist/data/domain/build-schema.js +7 -0
  74. package/dist/data/domain/build-schema.js.map +1 -1
  75. package/dist/data/domain/build-table.d.ts.map +1 -1
  76. package/dist/data/domain/build-table.js +9 -1
  77. package/dist/data/domain/build-table.js.map +1 -1
  78. package/dist/data/domain/field.d.ts +2 -0
  79. package/dist/data/domain/field.d.ts.map +1 -1
  80. package/dist/data/domain/field.js +4 -0
  81. package/dist/data/domain/field.js.map +1 -1
  82. package/dist/data/domain/types.d.ts +1 -1
  83. package/dist/data/domain/types.d.ts.map +1 -1
  84. package/dist/data/domain/types.js.map +1 -1
  85. package/dist/data/migrate/diff.js +7 -0
  86. package/dist/data/migrate/diff.js.map +1 -1
  87. package/dist/data/migrate/generate.js +1 -1
  88. package/dist/data/migrate/generate.js.map +1 -1
  89. package/dist/data/migrate/internal-definitions.d.ts.map +1 -1
  90. package/dist/data/migrate/internal-definitions.js +9 -0
  91. package/dist/data/migrate/internal-definitions.js.map +1 -1
  92. package/dist/data/migrate/rls-generate.d.ts +1 -1
  93. package/dist/data/migrate/rls-generate.d.ts.map +1 -1
  94. package/dist/data/migrate/rls-generate.js +20 -4
  95. package/dist/data/migrate/rls-generate.js.map +1 -1
  96. package/dist/data/migrate/snapshot.js +7 -5
  97. package/dist/data/migrate/snapshot.js.map +1 -1
  98. package/dist/data/migrate/sql-emit.js +4 -1
  99. package/dist/data/migrate/sql-emit.js.map +1 -1
  100. package/dist/request/context/create.d.ts +6 -0
  101. package/dist/request/context/create.d.ts.map +1 -1
  102. package/dist/request/context/create.js +6 -0
  103. package/dist/request/context/create.js.map +1 -1
  104. package/dist/request/context/run-in-transaction.d.ts.map +1 -1
  105. package/dist/request/context/run-in-transaction.js +8 -2
  106. package/dist/request/context/run-in-transaction.js.map +1 -1
  107. package/dist/request/context/types.d.ts +4 -0
  108. package/dist/request/context/types.d.ts.map +1 -1
  109. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ // The queue table as a pipe.define() definition, built from queue config.
2
+ //
3
+ // The table shape used to live in a hand-written schema.sql that no TS code
4
+ // referenced, so `pipework generate` could not emit it and a regenerated
5
+ // baseline silently dropped the queue migration. Expressing it as a
6
+ // DefinedTable puts it on the same generate path as every other table:
7
+ // createQueue() registers the definition here, and the migration generator
8
+ // merges this registry through its internalDefinitions hook.
9
+ import { define } from '../../data/domain/index.js';
10
+ import { field } from '../../data/domain/field.js';
11
+ import { schema } from '../../data/schema/namespace.js';
12
+ import { index } from '../../data/db/idx/index.js';
13
+ import { quoteIdentifier } from '../../data/db/identifiers.js';
14
+ import { sql } from '../../data/db/sql.js';
15
+ export const QUEUE_STATUSES = ['pending', 'claimed', 'running', 'completed', 'failed', 'cancelled', 'dead_letter'];
16
+ /** Column names every queue table owns — exclusion columns must not collide with these. */
17
+ export const QUEUE_BASE_COLUMNS = new Set([
18
+ 'id', 'job_type', 'priority', 'status', 'payload', 'output', 'error_payload',
19
+ 'created_lsn', 'queued_at', 'claimed_at', 'heartbeat_at', 'completed_at',
20
+ 'worker_id', 'attempt_count', 'max_attempts', 'next_retry_at',
21
+ ]);
22
+ function queueFields(exclusion) {
23
+ const fields = {
24
+ id: field.uuid().primaryKey().defaultRandom(),
25
+ jobType: field.text(),
26
+ priority: field.integer().default(0),
27
+ status: field.enum(QUEUE_STATUSES).default('pending'),
28
+ payload: field.jsonb(schema.check.json()),
29
+ output: field.jsonb(schema.check.json()).nullable(),
30
+ errorPayload: field.jsonb(schema.check.json()).nullable(),
31
+ createdLsn: field.pgLsn().default('currentWal'),
32
+ queuedAt: field.timestamp().default('now'),
33
+ claimedAt: field.timestamp().nullable(),
34
+ heartbeatAt: field.timestamp().nullable(),
35
+ completedAt: field.timestamp().nullable(),
36
+ workerId: field.text().nullable(),
37
+ attemptCount: field.integer().default(0),
38
+ maxAttempts: field.integer().default(1),
39
+ nextRetryAt: field.timestamp().nullable(),
40
+ };
41
+ if (exclusion?.tenantColumn !== undefined) {
42
+ fields[exclusion.tenantColumn] = field.uuid().tenant();
43
+ }
44
+ if (exclusion?.arrayColumn !== undefined) {
45
+ const column = exclusion.arrayElementType === 'text'
46
+ ? schema.col.text(exclusion.arrayColumn).array()
47
+ : schema.col.uuid(exclusion.arrayColumn).array();
48
+ fields[exclusion.arrayColumn] = field.custom(column, schema.check.array(schema.check.string())).nullable();
49
+ }
50
+ if (exclusion?.blocksAllColumn !== undefined) {
51
+ fields[exclusion.blocksAllColumn] = field.boolean().default(false);
52
+ }
53
+ return fields;
54
+ }
55
+ const queueTables = new Map();
56
+ /**
57
+ * Builds (or reuses) the DefinedTable for a queue. Cached per table name so
58
+ * repeated createQueue() calls share one definition; the same name with a
59
+ * different shape is a config error, not a silent overwrite.
60
+ */
61
+ export function queueDefinition(tableName, exclusion) {
62
+ const configKey = JSON.stringify(exclusion ?? null);
63
+ const existing = queueTables.get(tableName);
64
+ if (existing !== undefined) {
65
+ if (existing.configKey !== configKey) {
66
+ throw new Error(`[pipework] Queue table "${tableName}" is already defined with a different exclusion config.\n\n` +
67
+ ` Existing: ${existing.configKey}\n` +
68
+ ` New: ${configKey}\n` +
69
+ ' One table, one shape — give the second queue its own table name.\n');
70
+ }
71
+ return existing.definition;
72
+ }
73
+ const definition = define(tableName, queueFields(exclusion), {
74
+ indexes: (t) => ({
75
+ claimable: index(`idx_${tableName}_claimable`)
76
+ .on(t['priority'].desc(), t['queuedAt'].asc())
77
+ .where(sql.raw(`status = 'pending'`)),
78
+ stale: index(`idx_${tableName}_stale`)
79
+ .on(t['heartbeatAt'])
80
+ .where(sql.raw(`status IN ('claimed', 'running')`)),
81
+ retryable: index(`idx_${tableName}_retryable`)
82
+ .on(t['nextRetryAt'])
83
+ .where(sql.raw(`status = 'pending' AND next_retry_at IS NOT NULL`)),
84
+ // The claim query's exclusion checks run under SERIALIZABLE; without a
85
+ // matching index the overlap NOT EXISTS scans the whole claimed/running
86
+ // set and takes coarse predicate locks, so claimers with disjoint arrays
87
+ // SSI-conflict and starve. Partial GIN keeps the locks at the key level;
88
+ // fastupdate must be off — a GIN pending list forces relation-level
89
+ // predicate locks, voiding the granularity.
90
+ ...(exclusion?.arrayColumn !== undefined ? {
91
+ exclusionArray: index(`idx_${tableName}_excl_arr`)
92
+ .using('gin', t[exclusion.arrayColumn])
93
+ .with({ fastupdate: 'off' })
94
+ .where(sql.raw(`status IN ('claimed', 'running')`)),
95
+ } : {}),
96
+ // Same starvation mechanism for the blocks-all NOT EXISTS: without its
97
+ // own index that check scans every claimed/running row. This partial
98
+ // index contains only active blocks-all jobs (normally none), so
99
+ // ordinary claims neither read beyond it nor insert into it — only a
100
+ // genuine blocks-all claim conflicts, which is the intended semantics.
101
+ ...(exclusion?.blocksAllColumn !== undefined ? {
102
+ exclusionBlocks: index(`idx_${tableName}_excl_blocks`)
103
+ .on(t[exclusion.tenantColumn ?? exclusion.blocksAllColumn])
104
+ .where(sql.raw(`status IN ('claimed', 'running') AND ${quoteIdentifier(exclusion.blocksAllColumn, 'exclusion column')} = true`)),
105
+ } : {}),
106
+ }),
107
+ });
108
+ queueTables.set(tableName, { definition, configKey });
109
+ return definition;
110
+ }
111
+ /** Registered queue definitions — merged into `pipework generate` via internalDefinitions. */
112
+ export function queueDefinitions() {
113
+ return new Map([...queueTables].map(([name, entry]) => [name, entry.definition]));
114
+ }
115
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../../../src/async/jobs/table.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ export type { TenantConfig, TenantContext } from './types.js';
2
2
  export { validateTenantId, validateSessionVarName, validateSessionVarValue } from './validate.js';
3
3
  export { propagateTenantLocals } from './propagate.js';
4
4
  export { extractTenant, type TenantExtractionResult } from './extract.js';
5
- export { tenantIsolationPolicy, enableTenantRls, verifyTenantContext } from './rls.js';
5
+ export { tenantIsolationPolicy, enableTenantRls, verifyPrincipalContext } from './rls.js';
6
6
  export { scopeTable, getScopeConfig, isTenantScoped, type ScopeConfig } from './scope.js';
7
7
  export { createScopedDb, createTenantGuardDb } from './scoped-db.js';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACtF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -1,7 +1,7 @@
1
1
  export { validateTenantId, validateSessionVarName, validateSessionVarValue } from './validate.js';
2
2
  export { propagateTenantLocals } from './propagate.js';
3
3
  export { extractTenant } from './extract.js';
4
- export { tenantIsolationPolicy, enableTenantRls, verifyTenantContext } from './rls.js';
4
+ export { tenantIsolationPolicy, enableTenantRls, verifyPrincipalContext } from './rls.js';
5
5
  export { scopeTable, getScopeConfig, isTenantScoped } from './scope.js';
6
6
  export { createScopedDb, createTenantGuardDb } from './scoped-db.js';
7
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAA+B,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACtF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAA+B,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { validateTenantId } from './validate.js';
2
- import { enableTenantRls, tenantIsolationPolicy, verifyTenantContext } from './rls.js';
2
+ import { enableTenantRls, tenantIsolationPolicy, verifyPrincipalContext } from './rls.js';
3
3
  import { propagateTenantLocals } from './propagate.js';
4
4
  import { extractTenant } from './extract.js';
5
5
  import { isTenantScoped } from './scope.js';
@@ -11,8 +11,8 @@ export declare const tenant: {
11
11
  rls: typeof enableTenantRls;
12
12
  /** Returns the SQL string for a tenant isolation RLS policy — use in migrations. */
13
13
  policy: typeof tenantIsolationPolicy;
14
- /** Cross-checks ALS tenant against Postgres set_config('pipework.tenant_id') — defense-in-depth. */
15
- verify: typeof verifyTenantContext;
14
+ /** Cross-checks the ALS principal (tenant, user, asOf) against the Postgres session GUCs — defense-in-depth. */
15
+ verify: typeof verifyPrincipalContext;
16
16
  /** Sets pipework.tenant_id on the Postgres connection via set_config(). */
17
17
  propagate: typeof propagateTenantLocals;
18
18
  /** Extracts tenant ID from auth context using the configured extraction strategy. */
@@ -1 +1 @@
1
- {"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,sGAAsG;AACtG,eAAO,MAAM,MAAM;IACjB,wEAAwE;;IAExE,kFAAkF;;IAElF,oFAAoF;;IAEpF,oGAAoG;;IAEpG,2EAA2E;;IAE3E,qFAAqF;;IAErF,+FAA+F;;CAEhG,CAAA"}
1
+ {"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,sGAAsG;AACtG,eAAO,MAAM,MAAM;IACjB,wEAAwE;;IAExE,kFAAkF;;IAElF,oFAAoF;;IAEpF,gHAAgH;;IAEhH,2EAA2E;;IAE3E,qFAAqF;;IAErF,+FAA+F;;CAEhG,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { validateTenantId } from './validate.js';
2
- import { enableTenantRls, tenantIsolationPolicy, verifyTenantContext } from './rls.js';
2
+ import { enableTenantRls, tenantIsolationPolicy, verifyPrincipalContext } from './rls.js';
3
3
  import { propagateTenantLocals } from './propagate.js';
4
4
  import { extractTenant } from './extract.js';
5
5
  import { isTenantScoped } from './scope.js';
@@ -11,8 +11,8 @@ export const tenant = {
11
11
  rls: enableTenantRls,
12
12
  /** Returns the SQL string for a tenant isolation RLS policy — use in migrations. */
13
13
  policy: tenantIsolationPolicy,
14
- /** Cross-checks ALS tenant against Postgres set_config('pipework.tenant_id') — defense-in-depth. */
15
- verify: verifyTenantContext,
14
+ /** Cross-checks the ALS principal (tenant, user, asOf) against the Postgres session GUCs — defense-in-depth. */
15
+ verify: verifyPrincipalContext,
16
16
  /** Sets pipework.tenant_id on the Postgres connection via set_config(). */
17
17
  propagate: propagateTenantLocals,
18
18
  /** Extracts tenant ID from auth context using the configured extraction strategy. */
@@ -1 +1 @@
1
- {"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,sGAAsG;AACtG,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,wEAAwE;IACxE,QAAQ,EAAE,gBAAgB;IAC1B,kFAAkF;IAClF,GAAG,EAAE,eAAe;IACpB,oFAAoF;IACpF,MAAM,EAAE,qBAAqB;IAC7B,oGAAoG;IACpG,MAAM,EAAE,mBAAmB;IAC3B,2EAA2E;IAC3E,SAAS,EAAE,qBAAqB;IAChC,qFAAqF;IACrF,OAAO,EAAE,aAAa;IACtB,+FAA+F;IAC/F,cAAc;CACf,CAAA"}
1
+ {"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,sGAAsG;AACtG,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,wEAAwE;IACxE,QAAQ,EAAE,gBAAgB;IAC1B,kFAAkF;IAClF,GAAG,EAAE,eAAe;IACpB,oFAAoF;IACpF,MAAM,EAAE,qBAAqB;IAC7B,gHAAgH;IAChH,MAAM,EAAE,sBAAsB;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,qBAAqB;IAChC,qFAAqF;IACrF,OAAO,EAAE,aAAa;IACtB,+FAA+F;IAC/F,cAAc;CACf,CAAA"}
@@ -1,5 +1,26 @@
1
1
  import type { DB } from '../../data/db/index.js';
2
- export declare function tenantIsolationPolicy(table: string, column?: string, sessionVar?: string): string;
3
- export declare function enableTenantRls(db: DB, table: string, column?: string): Promise<void>;
4
- export declare function verifyTenantContext(db: DB, expectedTenant: string | null): Promise<void>;
2
+ /**
3
+ * SQL types a tenant discriminator column may be. The union members are
4
+ * literal Postgres type names, so they double as the cast target — a tenant
5
+ * policy compares the column against the session GUC cast to this type. A
6
+ * field whose kind is not one of these cannot be a tenant key; the generate
7
+ * layer rejects it before reaching here (see rls-generate.ts).
8
+ */
9
+ export type TenantKeyType = 'uuid' | 'text' | 'integer' | 'bigint';
10
+ export declare function tenantIsolationPolicy(table: string, column?: string, keyType?: TenantKeyType, sessionVar?: string, force?: boolean): string;
11
+ export declare function enableTenantRls(db: DB, table: string, column?: string, keyType?: TenantKeyType, force?: boolean): Promise<void>;
12
+ /** The principal axes carried on the request/job context and propagated as GUCs. */
13
+ export interface ExpectedPrincipal {
14
+ readonly tenant: string | null;
15
+ readonly user: string | null;
16
+ readonly asOf: string | null;
17
+ }
18
+ /**
19
+ * Cross-checks the application's principal (tenant, user, asOf) against the GUCs
20
+ * actually set on the Postgres session — defense-in-depth against an
21
+ * AsyncLocalStorage context swap propagating one principal's identity onto
22
+ * another's transaction. All three axes are checked together so a mismatch on
23
+ * any one aborts before a query can read the wrong rows.
24
+ */
25
+ export declare function verifyPrincipalContext(db: DB, expected: ExpectedPrincipal): Promise<void>;
5
26
  //# sourceMappingURL=rls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAIhD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAoB,EAAE,UAAU,GAAE,MAA6B,GAAG,MAAM,CAYpI;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,EAAE,EACN,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAIhD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;AAqBlE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAoB,EAC5B,OAAO,GAAE,aAAsB,EAC/B,UAAU,GAAE,MAA6B,EACzC,KAAK,GAAE,OAAc,GACpB,MAAM,CAUR;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAoB,EAC5B,OAAO,GAAE,aAAsB,EAC/B,KAAK,GAAE,OAAc,GACpB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,oFAAoF;AACpF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAoBf"}
@@ -1,35 +1,75 @@
1
1
  import { sql } from '../../data/db/sql.js';
2
2
  import { quoteIdentifier } from '../../data/db/identifiers.js';
3
3
  import { validateSessionVarName } from './validate.js';
4
- export function tenantIsolationPolicy(table, column = 'tenant_id', sessionVar = 'pipework.tenant_id') {
4
+ function tenantPolicyName(table) {
5
+ return `pipework_tenant_isolation_${table.replace(/[^a-zA-Z0-9_]/g, '_')}`;
6
+ }
7
+ /**
8
+ * The USING predicate of a tenant isolation policy.
9
+ *
10
+ * The cast (`::uuid` etc.) is required: current_setting returns text, and
11
+ * Postgres will not implicitly compare `uuid = text`. NULLIF(...,'') means an
12
+ * unset or explicitly-empty session GUC yields NULL — a deny-by-default match —
13
+ * instead of throwing on `''::uuid`. (current_setting(var, true) is itself NULL
14
+ * when the GUC was never set; the NULLIF only guards the empty-string case that
15
+ * set_config can produce.)
16
+ */
17
+ function tenantUsingClause(column, keyType, sessionVar) {
18
+ const c = quoteIdentifier(column, 'column');
19
+ return `${c} = NULLIF(current_setting('${sessionVar}', true), '')::${keyType}`;
20
+ }
21
+ export function tenantIsolationPolicy(table, column = 'tenant_id', keyType = 'uuid', sessionVar = 'pipework.tenant_id', force = true) {
5
22
  validateSessionVarName(sessionVar);
6
23
  const t = quoteIdentifier(table, 'table');
7
- const c = quoteIdentifier(column, 'column');
8
- const policyName = `pipework_tenant_isolation_${table.replace(/[^a-zA-Z0-9_]/g, '_')}`;
9
24
  return [
10
25
  `ALTER TABLE ${t} ENABLE ROW LEVEL SECURITY`,
11
- `ALTER TABLE ${t} FORCE ROW LEVEL SECURITY`,
12
- `CREATE POLICY ${quoteIdentifier(policyName, 'policy')} ON ${t} USING (${c} = current_setting('${sessionVar}', true))`,
26
+ ...(force ? [`ALTER TABLE ${t} FORCE ROW LEVEL SECURITY`] : []),
27
+ `CREATE POLICY ${quoteIdentifier(tenantPolicyName(table), 'policy')} ON ${t} USING (${tenantUsingClause(column, keyType, sessionVar)})`,
13
28
  ].join(';\n');
14
29
  }
15
- export async function enableTenantRls(db, table, column = 'tenant_id') {
30
+ export async function enableTenantRls(db, table, column = 'tenant_id', keyType = 'uuid', force = true) {
16
31
  const t = quoteIdentifier(table, 'table');
17
- const c = quoteIdentifier(column, 'column');
18
- const policyName = quoteIdentifier(`pipework_tenant_isolation_${table.replace(/[^a-zA-Z0-9_]/g, '_')}`, 'policy');
32
+ const policyName = quoteIdentifier(tenantPolicyName(table), 'policy');
19
33
  await db.execute(sql.raw(`ALTER TABLE ${t} ENABLE ROW LEVEL SECURITY`));
20
- await db.execute(sql.raw(`ALTER TABLE ${t} FORCE ROW LEVEL SECURITY`));
21
- await db.execute(sql.raw(`CREATE POLICY ${policyName} ON ${t} USING (${c} = current_setting('pipework.tenant_id', true))`));
34
+ if (force)
35
+ await db.execute(sql.raw(`ALTER TABLE ${t} FORCE ROW LEVEL SECURITY`));
36
+ await db.execute(sql.raw(`CREATE POLICY ${policyName} ON ${t} USING (${tenantUsingClause(column, keyType, 'pipework.tenant_id')})`));
22
37
  }
23
- export async function verifyTenantContext(db, expectedTenant) {
24
- const result = await db.execute(sql `SELECT current_setting('pipework.tenant_id', true) AS tenant_id`);
25
- const raw = result[0]?.tenant_id ?? null;
26
- const pgTenant = raw === '' ? null : raw;
27
- if (pgTenant !== expectedTenant) {
28
- throw new Error(`[pipework] Tenant context mismatch between application and database.\n\n` +
29
- ` Application context tenant: ${expectedTenant ?? '(null)'}\n` +
30
- ` PostgreSQL session tenant: ${pgTenant ?? '(null)'}\n\n` +
38
+ /**
39
+ * Cross-checks the application's principal (tenant, user, asOf) against the GUCs
40
+ * actually set on the Postgres session — defense-in-depth against an
41
+ * AsyncLocalStorage context swap propagating one principal's identity onto
42
+ * another's transaction. All three axes are checked together so a mismatch on
43
+ * any one aborts before a query can read the wrong rows.
44
+ */
45
+ export async function verifyPrincipalContext(db, expected) {
46
+ const result = await db.execute(sql `SELECT
47
+ current_setting('pipework.tenant_id', true) AS tenant_id,
48
+ current_setting('pipework.user_id', true) AS user_id,
49
+ current_setting('pipework.asof', true) AS asof`);
50
+ const row = result[0];
51
+ const mismatches = [];
52
+ if (normalizeGuc(row?.tenant_id) !== expected.tenant)
53
+ mismatches.push(formatAxis('tenant', expected.tenant, normalizeGuc(row?.tenant_id)));
54
+ if (normalizeGuc(row?.user_id) !== expected.user)
55
+ mismatches.push(formatAxis('user', expected.user, normalizeGuc(row?.user_id)));
56
+ if (normalizeGuc(row?.asof) !== expected.asOf)
57
+ mismatches.push(formatAxis('asOf', expected.asOf, normalizeGuc(row?.asof)));
58
+ if (mismatches.length > 0) {
59
+ throw new Error(`[pipework] Principal context mismatch between application and database.\n\n` +
60
+ mismatches.join('\n') + `\n\n` +
31
61
  ` This indicates an AsyncLocalStorage context integrity failure.\n` +
32
- ` The request has been aborted to prevent cross-tenant data access.\n`);
62
+ ` The request has been aborted to prevent cross-tenant or cross-user data access.\n`);
33
63
  }
34
64
  }
65
+ // current_setting(var, true) is NULL when the GUC was never set; set_config can
66
+ // also leave an empty string. Both collapse to null so they compare equal to an
67
+ // absent application-side axis.
68
+ function normalizeGuc(raw) {
69
+ const v = raw ?? null;
70
+ return v === '' ? null : v;
71
+ }
72
+ function formatAxis(axis, app, pg) {
73
+ return ` ${axis}: application context = ${app ?? '(null)'}, PostgreSQL session = ${pg ?? '(null)'}`;
74
+ }
35
75
  //# sourceMappingURL=rls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rls.js","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAEtD,MAAM,UAAU,qBAAqB,CAAC,KAAa,EAAE,SAAiB,WAAW,EAAE,aAAqB,oBAAoB;IAC1H,sBAAsB,CAAC,UAAU,CAAC,CAAA;IAElC,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACzC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,6BAA6B,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAA;IAEtF,OAAO;QACL,eAAe,CAAC,4BAA4B;QAC5C,eAAe,CAAC,2BAA2B;QAC3C,iBAAiB,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,uBAAuB,UAAU,WAAW;KACvH,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAM,EACN,KAAa,EACb,SAAiB,WAAW;IAE5B,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACzC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,eAAe,CAChC,6BAA6B,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,EACnE,QAAQ,CACT,CAAA;IAED,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,4BAA4B,CAAC,CAAC,CAAA;IACvE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,2BAA2B,CAAC,CAAC,CAAA;IACtE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CACtB,iBAAiB,UAAU,OAAO,CAAC,WAAW,CAAC,iDAAiD,CACjG,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAM,EACN,cAA6B;IAE7B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAA,iEAAiE,CAAC,CAAA;IACrG,MAAM,GAAG,GAAI,MAAyD,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,IAAI,CAAA;IAC5F,MAAM,QAAQ,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAA;IAExC,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,0EAA0E;YAC1E,iCAAiC,cAAc,IAAI,QAAQ,IAAI;YAC/D,iCAAiC,QAAQ,IAAI,QAAQ,MAAM;YAC3D,oEAAoE;YACpE,uEAAuE,CACxE,CAAA;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"rls.js","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"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"}
@@ -7,4 +7,12 @@ export interface DbOptions {
7
7
  readonly adminUrl?: string | undefined;
8
8
  }
9
9
  export declare function db(opts: DbOptions): Promise<void>;
10
+ /**
11
+ * The DML grants for the non-owner runtime role, applied at provision time on a
12
+ * connection authenticated as the owner. The two ALTER DEFAULT PRIVILEGES lines
13
+ * are the drift guard: every future table the owner creates via migrations is
14
+ * granted to the runtime role automatically, so an RLS-enabled table can never
15
+ * be left ungranted — which would blank the rows the app is entitled to.
16
+ */
17
+ export declare function buildRuntimeGrants(schema: string, ownerRole: string, runtimeRole: string): string[];
10
18
  //# sourceMappingURL=db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/db.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,wBAAsB,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvD"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/db.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,wBAAsB,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvD;AAyND;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAWnG"}