@opengeni/db 0.14.3 → 0.17.1

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 (67) hide show
  1. package/dist/{chunk-Y5WZZVQK.js → chunk-SK7YYHBI.js} +5 -2
  2. package/dist/chunk-SK7YYHBI.js.map +1 -0
  3. package/dist/{chunk-ELMUCYZF.js → chunk-T6RSJT6C.js} +202 -43
  4. package/dist/chunk-T6RSJT6C.js.map +1 -0
  5. package/dist/chunk-TLAC622R.js +4990 -0
  6. package/dist/chunk-TLAC622R.js.map +1 -0
  7. package/dist/codex-token-resolver.d.ts +64 -0
  8. package/dist/connection-token-resolver.d.ts +90 -0
  9. package/dist/environment-crypto.d.ts +11 -0
  10. package/dist/event-payload-sanitizer.d.ts +32 -0
  11. package/dist/index.d.ts +6374 -10
  12. package/dist/index.js +8075 -2776
  13. package/dist/index.js.map +1 -1
  14. package/dist/insights.d.ts +157 -0
  15. package/dist/memory-domain.d.ts +44 -0
  16. package/dist/migrate.d.ts +5 -7
  17. package/dist/migrate.js +1 -1
  18. package/dist/new-session-drafts.d.ts +49 -0
  19. package/dist/persistence-errors.d.ts +69 -0
  20. package/dist/preference-registry-schema.d.ts +1147 -0
  21. package/dist/preference-registry.d.ts +878 -0
  22. package/dist/provision-roles.d.ts +4 -6489
  23. package/dist/provision-roles.js +1 -1
  24. package/dist/role-relationships.d.ts +35 -0
  25. package/dist/runtime-posture-cli.d.ts +1 -0
  26. package/dist/runtime-posture.d.ts +103 -0
  27. package/dist/schema.d.ts +25330 -3
  28. package/dist/schema.js +31 -1
  29. package/dist/session-control.d.ts +331 -0
  30. package/dist/session-queue-commands.d.ts +186 -0
  31. package/dist/session-tool-call-settlement.d.ts +32 -0
  32. package/dist/turn-initiator.d.ts +28 -0
  33. package/dist/workspace-artifacts.d.ts +62 -0
  34. package/dist/workspace-instruction-policies-schema.d.ts +735 -0
  35. package/dist/workspace-instruction-policies.d.ts +64 -0
  36. package/drizzle/0136_unified_session_tool_policy.sql +1 -1
  37. package/drizzle/0137_preference_registry.sql +1347 -0
  38. package/drizzle/0138_sandbox_checkpoint_artifacts_and_deadlines.sql +1074 -0
  39. package/drizzle/0139_codex_provider_artifact_invalidations.sql +51 -0
  40. package/drizzle/0140_sandbox_restore_and_reaper_fences.sql +276 -0
  41. package/drizzle/0142_sandbox_archive_capture_gate.sql +80 -0
  42. package/drizzle/0143_session_codex_compaction_mode.sql +20 -0
  43. package/drizzle/0144_sandbox_viewer_force_drain_gate.sql +95 -0
  44. package/drizzle/0145_model_call_facts.sql +96 -0
  45. package/drizzle/0146_slack_bot_delete_idempotency.sql +105 -0
  46. package/drizzle/0147_draft_latency_mode.sql +12 -0
  47. package/drizzle/0148_session_turn_latency_mode.sql +12 -0
  48. package/drizzle/0149_workspace_artifacts.sql +348 -0
  49. package/drizzle/0150_slack_task_interactions.sql +344 -0
  50. package/package.json +6 -6
  51. package/src/index.ts +5766 -275
  52. package/src/insights.ts +837 -0
  53. package/src/migrate.ts +9 -1
  54. package/src/new-session-drafts.ts +4 -0
  55. package/src/preference-registry-schema.ts +170 -0
  56. package/src/preference-registry.ts +1220 -0
  57. package/src/provision-roles.ts +95 -22
  58. package/src/role-relationships.ts +132 -0
  59. package/src/runtime-posture.ts +47 -27
  60. package/src/schema.ts +875 -19
  61. package/src/session-queue-commands.ts +39 -6
  62. package/src/workspace-artifacts.ts +751 -0
  63. package/dist/chunk-DW24CKCT.js +0 -4046
  64. package/dist/chunk-DW24CKCT.js.map +0 -1
  65. package/dist/chunk-ELMUCYZF.js.map +0 -1
  66. package/dist/chunk-Y5WZZVQK.js.map +0 -1
  67. package/dist/schema-DhkRhcuQ.d.ts +0 -22666
@@ -99,7 +99,10 @@ async function executeMigrationFile(sql, file, sqlText) {
99
99
  }
100
100
  const concurrentIndex = parseConcurrentIndexMigration(file, sqlText);
101
101
  if (!concurrentIndex) {
102
- await sql.unsafe(sqlText);
102
+ await sql.unsafe(
103
+ `SELECT pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
104
+ ${sqlText}`
105
+ );
103
106
  return;
104
107
  }
105
108
  await sql`select set_config('lock_timeout', ${concurrentIndex.lockTimeout}, false)`;
@@ -200,4 +203,4 @@ export {
200
203
  migrate,
201
204
  runMigrations
202
205
  };
203
- //# sourceMappingURL=chunk-Y5WZZVQK.js.map
206
+ //# sourceMappingURL=chunk-SK7YYHBI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/migrate.ts"],"sourcesContent":["import { readdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport postgres from \"postgres\";\n\nconst DEFAULT_DATABASE_URL = \"postgres://opengeni:opengeni@127.0.0.1:5432/opengeni\";\nconst DEFAULT_MAX_NESTED_AGENT_DEPTH = 3;\nconst MAX_NESTED_AGENT_DEPTH = 2_147_483_647;\nconst batchedBackfillDirective =\n /^-- opengeni:batched-backfill batch-size=(\\d+) lock-timeout=(\\d+(?:ms|s|min)) statement-timeout=(\\d+(?:ms|s|min))$/;\nconst concurrentIndexDirective = /^-- opengeni:concurrent-index lock-timeout=(\\d+(?:ms|s|min))$/;\nconst concurrentIndexStatement =\n /^CREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+CONCURRENTLY\\s+(?:(IF\\s+NOT\\s+EXISTS)\\s+)?(?:\"((?:[^\"]|\"\")+)\"|([A-Za-z_][A-Za-z0-9_]*))\\s+ON\\b/is;\nconst governedLegacyConcurrentIndexMigrations = new Set([\n \"0066_session_interruption_attempt_lookup.sql\",\n \"0070_session_event_type_sequence_lookup.sql\",\n \"0071_session_event_monitoring_tail.sql\",\n \"0072_sessions_workspace_created_id_idx.sql\",\n \"0073_sessions_workspace_updated_id_idx.sql\",\n \"0075_sessions_workspace_activity_revision_idx.sql\",\n \"0077_session_attempt_latest_lookup.sql\",\n]);\n\nexport interface ConcurrentIndexMigration {\n indexName: string;\n lockTimeout: string;\n skipWhenValid: boolean;\n statement: string;\n}\n\nexport type MigrationRuntimeOptions = {\n maxNestedAgentDepth?: number;\n};\n\ntype DeploymentDepthPolicy = {\n maxNestedAgentDepth: number;\n source: \"deployment\" | \"default\";\n};\n\n/** A bare Postgres identifier (schema/role name) safe to interpolate into DDL. */\nfunction assertIdentifier(name: string, value: string): string {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {\n throw new Error(`${name} is not a valid Postgres identifier: ${value}`);\n }\n return value;\n}\n\n/**\n * Most migration files intentionally execute as one implicit transaction.\n * PostgreSQL forbids CREATE INDEX CONCURRENTLY there, so a migration may opt\n * into one narrowly validated transactionless statement with:\n *\n * -- opengeni:concurrent-index lock-timeout=5s\n * CREATE [UNIQUE] INDEX CONCURRENTLY IF NOT EXISTS ...;\n *\n * The directive is deliberately not a generic \"no transaction\" escape hatch:\n * only one idempotent concurrent-index statement is accepted, lock acquisition\n * is always bounded, and an invalid artifact left by a failed concurrent build\n * is removed before retry. Seven governed historical migrations predate the\n * IF NOT EXISTS rule; only those exact filenames may use their immutable bare\n * statements, and the runner makes their retries idempotent by skipping an\n * already-valid index. This keeps additive large-table indexes online without\n * rewriting shipped history or making arbitrary partially-applied migration\n * scripts possible.\n */\nexport function parseConcurrentIndexMigration(\n file: string,\n sqlText: string,\n): ConcurrentIndexMigration | null {\n const lines = sqlText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\");\n const firstLine = lines[0]?.trim() ?? \"\";\n const deploymentPrefixed = /^-- deployment-mode: (?:rolling|maintenance)$/.test(firstLine);\n const directiveIndex = deploymentPrefixed ? 1 : 0;\n const directiveLine = lines[directiveIndex]?.trim() ?? \"\";\n const directive = concurrentIndexDirective.exec(directiveLine);\n if (!directive) {\n if (directiveLine.startsWith(\"-- opengeni:\")) {\n throw new Error(`Unsupported OpenGeni migration directive in ${file}`);\n }\n return null;\n }\n\n const lockTimeout = directive[1]!;\n const statement = lines\n .slice(directiveIndex + 1)\n .join(\"\\n\")\n .trim();\n const withoutTrailingSemicolon = statement.endsWith(\";\")\n ? statement.slice(0, -1).trimEnd()\n : statement;\n const parsedStatement = concurrentIndexStatement.exec(withoutTrailingSemicolon);\n if (!parsedStatement || withoutTrailingSemicolon.includes(\";\")) {\n throw new Error(\n `${file}: opengeni:concurrent-index requires exactly one CREATE [UNIQUE] INDEX CONCURRENTLY IF NOT EXISTS statement with an unqualified index name`,\n );\n }\n const idempotentInSql = parsedStatement[1] !== undefined;\n if (!idempotentInSql && !governedLegacyConcurrentIndexMigrations.has(file)) {\n throw new Error(\n `${file}: opengeni:concurrent-index requires IF NOT EXISTS; bare statements are supported only for governed historical migrations`,\n );\n }\n return {\n indexName: (parsedStatement[2] ?? parsedStatement[3]!).replaceAll('\"\"', '\"'),\n lockTimeout,\n skipWhenValid: !idempotentInSql,\n statement,\n };\n}\n\nfunction parseBatchedBackfillMigration(\n file: string,\n sqlText: string,\n): { batchSize: number; lockTimeout: string; statementTimeout: string; statement: string } | null {\n const lines = sqlText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\");\n const directiveIndex = /^-- deployment-mode: (?:rolling|maintenance)$/.test(\n lines[0]?.trim() ?? \"\",\n )\n ? 1\n : 0;\n const directive = batchedBackfillDirective.exec(lines[directiveIndex]?.trim() ?? \"\");\n if (!directive) return null;\n const statement = lines\n .slice(directiveIndex + 1)\n .filter((line) => !line.trim().startsWith(\"--\"))\n .join(\"\\n\")\n .trim();\n const withoutTrailingSemicolon = statement.endsWith(\";\")\n ? statement.slice(0, -1).trimEnd()\n : statement;\n const batchSize = Number(directive[1]!);\n if (\n !Number.isSafeInteger(batchSize) ||\n batchSize < 1 ||\n batchSize > 10_000 ||\n !/^WITH\\b/is.test(withoutTrailingSemicolon) ||\n !/\\bUPDATE\\b/is.test(withoutTrailingSemicolon) ||\n !/\\bRETURNING\\b/is.test(withoutTrailingSemicolon) ||\n !new RegExp(`\\\\bLIMIT\\\\s+${batchSize}\\\\b`, \"i\").test(withoutTrailingSemicolon) ||\n withoutTrailingSemicolon.includes(\";\")\n ) {\n throw new Error(\n `${file}: opengeni:batched-backfill requires one bounded WITH ... UPDATE ... RETURNING statement whose LIMIT matches batch-size`,\n );\n }\n return {\n batchSize,\n lockTimeout: directive[2]!,\n statementTimeout: directive[3]!,\n statement,\n };\n}\n\nasync function executeMigrationFile(\n sql: postgres.Sql,\n file: string,\n sqlText: string,\n): Promise<void> {\n const batchedBackfill = parseBatchedBackfillMigration(file, sqlText);\n if (batchedBackfill) {\n await sql`select set_config('lock_timeout', ${batchedBackfill.lockTimeout}, false)`;\n await sql`select set_config('statement_timeout', ${batchedBackfill.statementTimeout}, false)`;\n try {\n for (;;) {\n const result = await sql.unsafe(batchedBackfill.statement);\n if (result.length === 0) break;\n }\n } finally {\n await sql`select set_config('statement_timeout', '0', false)`;\n await sql`select set_config('lock_timeout', '0', false)`;\n }\n return;\n }\n const concurrentIndex = parseConcurrentIndexMigration(file, sqlText);\n if (!concurrentIndex) {\n // Migration SQL is the only writer allowed to cross protocol generations.\n // Keep the authority transaction-local and inside the same PostgreSQL simple\n // query as the migration body: setting it in a prior statement would end\n // that implicit transaction and silently lose the LOCAL value. This also\n // makes a fresh database capable of applying maintenance migration 0138\n // without a process-global PGOPTIONS escape hatch.\n await sql.unsafe(\n `SELECT pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);\\n${sqlText}`,\n );\n return;\n }\n\n await sql`select set_config('lock_timeout', ${concurrentIndex.lockTimeout}, false)`;\n try {\n const [existing] = await sql<Array<{ valid: boolean; ready: boolean }>>`\n select i.indisvalid as valid, i.indisready as ready\n from pg_catalog.pg_class c\n join pg_catalog.pg_namespace n on n.oid = c.relnamespace\n join pg_catalog.pg_index i on i.indexrelid = c.oid\n where n.nspname = current_schema() and c.relname = ${concurrentIndex.indexName}\n `;\n if (existing && (!existing.valid || !existing.ready)) {\n const quotedIndexName = `\"${concurrentIndex.indexName.replaceAll('\"', '\"\"')}\"`;\n await sql.unsafe(`DROP INDEX CONCURRENTLY ${quotedIndexName}`);\n } else if (existing && concurrentIndex.skipWhenValid) {\n return;\n }\n await sql.unsafe(concurrentIndex.statement);\n } finally {\n await sql`select set_config('lock_timeout', '0', false)`;\n }\n}\n\nfunction deploymentDepthPolicy(\n options: MigrationRuntimeOptions | undefined,\n): DeploymentDepthPolicy {\n const raw =\n options === undefined\n ? process.env.OPENGENI_MAX_NESTED_AGENT_DEPTH?.trim() || undefined\n : options.maxNestedAgentDepth;\n if (raw === undefined) {\n return { maxNestedAgentDepth: DEFAULT_MAX_NESTED_AGENT_DEPTH, source: \"default\" };\n }\n const value = typeof raw === \"number\" ? raw : Number(raw);\n if (\n !Number.isSafeInteger(value) ||\n value < 0 ||\n value > MAX_NESTED_AGENT_DEPTH ||\n (typeof raw === \"string\" && !/^(0|[1-9][0-9]*)$/.test(raw))\n ) {\n throw new Error(\n `OPENGENI_MAX_NESTED_AGENT_DEPTH must be a non-negative 32-bit integer: ${raw}`,\n );\n }\n return { maxNestedAgentDepth: value, source: \"deployment\" };\n}\n\nasync function persistDeploymentDepthPolicy(\n sql: postgres.Sql,\n policy: DeploymentDepthPolicy,\n): Promise<void> {\n const [relation] = await sql<{ exists: boolean }[]>`\n select to_regclass('nested_agent_depth_configuration') is not null as exists\n `;\n if (!relation?.exists) return;\n await sql`\n insert into \"nested_agent_depth_configuration\" (\n \"singleton\", \"max_nested_agent_depth\", \"policy_source\", \"updated_at\"\n ) values (true, ${policy.maxNestedAgentDepth}, ${policy.source}, now())\n on conflict (\"singleton\") do update\n set \"max_nested_agent_depth\" = excluded.\"max_nested_agent_depth\",\n \"policy_source\" = excluded.\"policy_source\",\n \"updated_at\" = now()\n where \"nested_agent_depth_configuration\".\"max_nested_agent_depth\"\n is distinct from excluded.\"max_nested_agent_depth\"\n or \"nested_agent_depth_configuration\".\"policy_source\"\n is distinct from excluded.\"policy_source\"\n `;\n}\n\n/**\n * Apply the OpenGeni SQL migration chain.\n *\n * STANDALONE (default, unchanged): `migrate()` / `migrate(databaseUrl)` runs the\n * whole chain with NO search_path manipulation, so every unqualified\n * table/index/policy lands in the server default schema (`public`). This is the\n * byte-for-byte historical behavior — the migration test suite calls\n * `migrate(DB_URL)` and is unaffected.\n *\n * EMBEDDED SCHEMA MODE: pass a `schema` (or set\n * `OPENGENI_DB_SCHEMA`). The migrate session then `CREATE SCHEMA IF NOT EXISTS`\n * for both `<schema>` and `opengeni_private`, and sets\n * `search_path = \"<schema>\", \"opengeni_private\", \"public\"`, so EVERY unqualified\n * DDL statement lands in the dedicated schema with NO per-statement SQL rewrite\n * (the schema-isolation contract). Two things make this work and stay idempotent:\n * 1. The policy-existence guards in the migration SQL use `current_schema()`\n * (not a hardcoded `'public'`) — so a re-run finds the policy it already\n * created in `<schema>` and DROP/CREATEs idempotently instead of failing\n * with \"policy already exists\". (This guard substitution is the migrate-\n * time enabler for the runtime search_path approach; without it the SDK\n * entry point silently fails on re-run — the migration replay hazard.)\n * 2. `public` stays LAST on the path so `gen_random_uuid()` (pgcrypto) and the\n * `vector` type — both installed into `public` by 0000 — still resolve. The\n * `opengeni_private.*` helpers are always called with an absolute prefix.\n *\n * `OPENGENI_DB_SCHEMA` defaults UNSET → `public` → standalone, so the default\n * binding never regresses.\n */\nexport async function migrate(\n databaseUrl = process.env.OPENGENI_MIGRATIONS_DATABASE_URL ??\n process.env.OPENGENI_DATABASE_URL ??\n DEFAULT_DATABASE_URL,\n schema: string | undefined = process.env.OPENGENI_DB_SCHEMA?.trim() || undefined,\n runtimeOptions?: MigrationRuntimeOptions,\n): Promise<void> {\n const migrationsDir = join(dirname(fileURLToPath(import.meta.url)), \"../drizzle\");\n const files = (await readdir(migrationsDir)).filter((file) => file.endsWith(\".sql\")).sort();\n const depthPolicy = deploymentDepthPolicy(runtimeOptions);\n const sql = postgres(databaseUrl, { max: 1 });\n try {\n // Serialize concurrent migrate() runs; the session-level lock is released\n // when the connection closes.\n await sql`SELECT pg_advisory_lock(727458)`;\n if (schema) {\n assertIdentifier(\"OPENGENI_DB_SCHEMA\", schema);\n await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS \"${schema}\"`);\n // opengeni_private is also created by 0001 with an absolute prefix, but the\n // session search_path must already resolve it for the policy predicates\n // and the SECURITY DEFINER functions that inherit the caller's path.\n await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS \"opengeni_private\"`);\n await sql.unsafe(`SET search_path = \"${schema}\", \"opengeni_private\", \"public\"`);\n }\n await sql`select set_config('opengeni.max_nested_agent_depth', ${String(depthPolicy.maxNestedAgentDepth)}, false)`;\n await sql`select set_config('opengeni.nested_agent_depth_policy_source', ${depthPolicy.source}, false)`;\n await sql.unsafe(\n `CREATE TABLE IF NOT EXISTS \"schema_migrations\" (\"name\" text PRIMARY KEY, \"applied_at\" timestamptz NOT NULL DEFAULT now())`,\n );\n const appliedRows = await sql`SELECT \"name\" FROM \"schema_migrations\"`;\n const applied = new Set(appliedRows.map((row) => row.name as string));\n for (const file of files) {\n if (applied.has(file)) {\n continue;\n }\n const sqlText = await readFile(join(migrationsDir, file), \"utf8\");\n await executeMigrationFile(sql, file, sqlText);\n await sql`INSERT INTO \"schema_migrations\" (\"name\") VALUES (${file}) ON CONFLICT DO NOTHING`;\n }\n // Reconcile even when all migration names were already recorded. This is\n // the only supported way to change deployment policy in a live database.\n await persistDeploymentDepthPolicy(sql, depthPolicy);\n } finally {\n await sql.end();\n }\n}\n\n/**\n * SDK entry point (Step I): run the migration chain over a host-supplied admin\n * connection string against an explicit target schema. This is the embedded\n * topology's named entry — a host calls `runMigrations(adminConnection,\n * targetSchema)` from its own provisioning code instead of relying on env vars.\n * `targetSchema` undefined → `public` → standalone behavior. Thin wrapper over\n * `migrate` so there is one migration engine.\n */\nexport async function runMigrations(\n adminConnection: string,\n targetSchema?: string,\n runtimeOptions?: MigrationRuntimeOptions,\n): Promise<void> {\n await migrate(adminConnection, targetSchema, runtimeOptions);\n}\n\nif (import.meta.main) {\n await migrate();\n console.log(\"Applied Drizzle SQL migrations.\");\n}\n"],"mappings":"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","names":[]}
@@ -3,6 +3,92 @@ import postgres from "postgres";
3
3
 
4
4
  // src/runtime-posture.ts
5
5
  import { sql } from "drizzle-orm";
6
+
7
+ // src/role-relationships.ts
8
+ function roleRelationshipsCatalogQuery(targetRole) {
9
+ return `
10
+ with recursive target_role(oid, rolname) as (
11
+ select oid, rolname
12
+ from pg_roles
13
+ where rolname = ${targetRole}
14
+ ), inherited_roles(oid, rolname) as (
15
+ select parent.oid, parent.rolname
16
+ from pg_auth_members membership
17
+ join target_role target on target.oid = membership.member
18
+ join pg_roles parent on parent.oid = membership.roleid
19
+ union
20
+ select parent.oid, parent.rolname
21
+ from inherited_roles inherited
22
+ join pg_auth_members membership on membership.member = inherited.oid
23
+ join pg_roles parent on parent.oid = membership.roleid
24
+ ), relationships as (
25
+ select
26
+ ('inherits:' || inherited.rolname)::text as relationship,
27
+ 'inherits'::text as direction,
28
+ null::boolean as admin_option,
29
+ null::boolean as inherit_option,
30
+ null::boolean as set_option,
31
+ null::boolean as member_is_superuser,
32
+ null::boolean as member_can_create_role,
33
+ null::text as grantor_role,
34
+ null::boolean as grantor_is_superuser
35
+ from inherited_roles inherited
36
+ union all
37
+ select
38
+ ('member:' || member.rolname)::text as relationship,
39
+ 'member'::text as direction,
40
+ membership.admin_option,
41
+ case
42
+ when current_setting('server_version_num')::integer >= 160000
43
+ then (to_jsonb(membership) ->> 'inherit_option')::boolean
44
+ else null::boolean
45
+ end as inherit_option,
46
+ case
47
+ when current_setting('server_version_num')::integer >= 160000
48
+ then (to_jsonb(membership) ->> 'set_option')::boolean
49
+ else null::boolean
50
+ end as set_option,
51
+ member.rolsuper as member_is_superuser,
52
+ member.rolcreaterole as member_can_create_role,
53
+ grantor.rolname::text as grantor_role,
54
+ grantor.rolsuper as grantor_is_superuser
55
+ from pg_auth_members membership
56
+ join target_role target on target.oid = membership.roleid
57
+ join pg_roles member on member.oid = membership.member
58
+ left join pg_roles grantor on grantor.oid = membership.grantor
59
+ )
60
+ select
61
+ relationship,
62
+ direction,
63
+ current_setting('server_version_num')::integer as server_version_num,
64
+ admin_option,
65
+ inherit_option,
66
+ set_option,
67
+ member_is_superuser,
68
+ member_can_create_role,
69
+ grantor_role,
70
+ grantor_is_superuser
71
+ from relationships
72
+ order by relationship, direction, grantor_role nulls first
73
+ `;
74
+ }
75
+ function isManagementOnlyRoleRelationship(row) {
76
+ return row.direction === "member" && row.server_version_num >= 16e4 && row.admin_option === true && row.inherit_option === false && row.set_option === false && row.member_is_superuser === false && row.member_can_create_role === true && row.grantor_is_superuser === true;
77
+ }
78
+ function sortedUnique(values) {
79
+ return [...new Set(values)].sort((a, b) => a.localeCompare(b));
80
+ }
81
+ function classifyRoleRelationships(rows) {
82
+ const managementOnlyRows = rows.filter(isManagementOnlyRoleRelationship);
83
+ const unsafeRows = rows.filter((row) => !isManagementOnlyRoleRelationship(row));
84
+ return {
85
+ relationships: sortedUnique(rows.map((row) => row.relationship)),
86
+ managementOnlyRelationships: sortedUnique(managementOnlyRows.map((row) => row.relationship)),
87
+ unsafeRelationships: sortedUnique(unsafeRows.map((row) => row.relationship))
88
+ };
89
+ }
90
+
91
+ // src/runtime-posture.ts
6
92
  var FORCE_RLS_TABLES = [
7
93
  "agent_run_states",
8
94
  "api_keys",
@@ -37,11 +123,17 @@ var FORCE_RLS_TABLES = [
37
123
  "knowledge_memories",
38
124
  "machine_metrics_latest",
39
125
  "machine_metrics_series",
126
+ "model_call_facts",
40
127
  "new_session_drafts",
41
128
  "pack_installations",
129
+ "preference_registry_events",
130
+ "preference_registry_preferences",
131
+ "preference_registry_revisions",
132
+ "preference_registry_snapshots",
42
133
  "rig_changes",
43
134
  "rig_versions",
44
135
  "rigs",
136
+ "sandbox_checkpoint_artifacts",
45
137
  "sandbox_lease_holders",
46
138
  "sandbox_leases",
47
139
  "sandbox_pty_sessions",
@@ -70,10 +162,18 @@ var FORCE_RLS_TABLES = [
70
162
  "session_turns",
71
163
  "session_workflow_wake_outbox",
72
164
  "sessions",
165
+ "slack_bot_delete_operations",
73
166
  "slack_bot_post_operations",
167
+ "slack_bot_user_links",
168
+ "slack_interaction_inbox",
169
+ "slack_interaction_progress_deliveries",
170
+ "slack_interactions",
74
171
  "social_connections",
75
172
  "social_posts",
76
173
  "usage_events",
174
+ "workspace_artifact_events",
175
+ "workspace_artifact_versions",
176
+ "workspace_artifacts",
77
177
  "workspace_captures",
78
178
  "workspace_control_events",
79
179
  "workspace_inference_controls",
@@ -135,11 +235,13 @@ var RUNTIME_FULL_DML_TABLES = [
135
235
  "machine_metrics_latest",
136
236
  "machine_metrics_series",
137
237
  "managed_accounts",
238
+ "model_call_facts",
138
239
  "new_session_drafts",
139
240
  "pack_installations",
140
241
  "rig_changes",
141
242
  "rig_versions",
142
243
  "rigs",
244
+ "sandbox_checkpoint_artifacts",
143
245
  "sandbox_lease_holders",
144
246
  "sandbox_leases",
145
247
  "sandbox_pty_sessions",
@@ -167,11 +269,17 @@ var RUNTIME_FULL_DML_TABLES = [
167
269
  "session_turns",
168
270
  "session_workflow_wake_outbox",
169
271
  "sessions",
272
+ "slack_bot_delete_operations",
170
273
  "slack_bot_post_operations",
274
+ "slack_bot_user_links",
275
+ "slack_interaction_inbox",
276
+ "slack_interaction_progress_deliveries",
277
+ "slack_interactions",
171
278
  "social_connections",
172
279
  "social_posts",
173
280
  "stripe_webhook_events",
174
281
  "usage_events",
282
+ "workspace_artifacts",
175
283
  "workspace_captures",
176
284
  "workspace_control_events",
177
285
  "workspace_inference_controls",
@@ -184,9 +292,17 @@ var RUNTIME_FULL_DML_TABLES = [
184
292
  "workspace_variable_sets",
185
293
  "workspaces"
186
294
  ];
187
- var RUNTIME_READ_ONLY_TABLES = ["nested_agent_depth_configuration"];
295
+ var RUNTIME_READ_ONLY_TABLES = [
296
+ "nested_agent_depth_configuration",
297
+ "preference_registry_events",
298
+ "preference_registry_snapshots"
299
+ ];
188
300
  var RUNTIME_READ_INSERT_TABLES = [
301
+ "preference_registry_preferences",
302
+ "preference_registry_revisions",
189
303
  "session_spawn_denials",
304
+ "workspace_artifact_events",
305
+ "workspace_artifact_versions",
190
306
  "workspace_instruction_policy_activation_events",
191
307
  "workspace_instruction_policy_revisions"
192
308
  ];
@@ -287,30 +403,10 @@ async function inspectRuntimeDatabasePosture(db, options) {
287
403
  privateRoutines: []
288
404
  };
289
405
  }
290
- const memberships = resultRows(
291
- await tx.execute(sql`
292
- with recursive inherited_roles(oid, rolname) as (
293
- select parent.oid, parent.rolname
294
- from pg_auth_members membership
295
- join pg_roles member on member.oid = membership.member
296
- join pg_roles parent on parent.oid = membership.roleid
297
- where member.rolname = current_user
298
- union
299
- select parent.oid, parent.rolname
300
- from inherited_roles inherited
301
- join pg_auth_members membership on membership.member = inherited.oid
302
- join pg_roles parent on parent.oid = membership.roleid
303
- )
304
- select ('inherits:' || rolname)::text as relationship from inherited_roles
305
- union
306
- select ('member:' || member.rolname)::text as relationship
307
- from pg_auth_members membership
308
- join pg_roles parent on parent.oid = membership.roleid
309
- join pg_roles member on member.oid = membership.member
310
- where parent.rolname = current_user
311
- order by relationship
312
- `)
313
- ).map((row) => row.relationship);
406
+ const relationshipRows = resultRows(
407
+ await tx.execute(sql.raw(roleRelationshipsCatalogQuery("current_user")))
408
+ );
409
+ const memberships = classifyRoleRelationships(relationshipRows).unsafeRelationships;
314
410
  const schemas = resultRows(
315
411
  await tx.execute(sql`
316
412
  select
@@ -396,7 +492,11 @@ async function inspectRuntimeDatabasePosture(db, options) {
396
492
  and p.prokind in ('f', 'p')
397
493
  order by p.proname, pg_get_function_identity_arguments(p.oid)
398
494
  `)
399
- ).map((row) => ({ name: row.name, owner: row.owner, execute: row.can_execute }));
495
+ ).map((row) => ({
496
+ name: row.name,
497
+ owner: row.owner,
498
+ execute: row.can_execute
499
+ }));
400
500
  return {
401
501
  identity: mappedIdentity,
402
502
  memberships,
@@ -674,12 +774,23 @@ BEGIN
674
774
  ${literal(role)},
675
775
  ${literal(password)}
676
776
  );
677
- ELSE
777
+ ELSIF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = current_user AND rolsuper) THEN
678
778
  EXECUTE format(
679
779
  'ALTER ROLE %I WITH LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEROLE NOCREATEDB NOREPLICATION NOINHERIT PASSWORD %L',
680
780
  ${literal(role)},
681
781
  ${literal(password)}
682
782
  );
783
+ ELSE
784
+ -- PostgreSQL requires elevated attributes merely to name SUPERUSER,
785
+ -- BYPASSRLS, REPLICATION, or CREATEDB in ALTER ROLE, even when setting them
786
+ -- false. The preflight has already proved those protected attributes are
787
+ -- false, so a managed CREATEROLE administrator converges only the
788
+ -- attributes it may change.
789
+ EXECUTE format(
790
+ 'ALTER ROLE %I WITH LOGIN NOCREATEROLE NOINHERIT PASSWORD %L',
791
+ ${literal(role)},
792
+ ${literal(password)}
793
+ );
683
794
  END IF;
684
795
  END $$;
685
796
  `);
@@ -691,23 +802,38 @@ async function assertAppRoleSafeToNormalize(sql2, role) {
691
802
  if (!exists[0]?.exists) {
692
803
  return;
693
804
  }
694
- const memberships = await sql2`
695
- select ('inherits:' || parent.rolname)::text as relationship
696
- from pg_auth_members membership
697
- join pg_roles member on member.oid = membership.member
698
- join pg_roles parent on parent.oid = membership.roleid
699
- where member.rolname = ${role}
700
- union all
701
- select ('member:' || member.rolname)::text as relationship
702
- from pg_auth_members membership
703
- join pg_roles member on member.oid = membership.member
704
- join pg_roles parent on parent.oid = membership.roleid
705
- where parent.rolname = ${role}
706
- order by relationship
805
+ const [roleAttributes] = await sql2`
806
+ select
807
+ administrator.rolsuper as administrator_superuser,
808
+ app.rolsuper as app_superuser,
809
+ app.rolbypassrls as app_bypass_rls,
810
+ app.rolcreatedb as app_create_database,
811
+ app.rolreplication as app_replication
812
+ from pg_roles app
813
+ join pg_roles administrator on administrator.rolname = current_user
814
+ where app.rolname = ${role}
707
815
  `;
708
- if (memberships.length > 0) {
816
+ if (roleAttributes && !roleAttributes.administrator_superuser) {
817
+ const protectedAttributes = [
818
+ roleAttributes.app_superuser ? "SUPERUSER" : null,
819
+ roleAttributes.app_bypass_rls ? "BYPASSRLS" : null,
820
+ roleAttributes.app_create_database ? "CREATEDB" : null,
821
+ roleAttributes.app_replication ? "REPLICATION" : null
822
+ ].filter((attribute) => attribute !== null);
823
+ if (protectedAttributes.length > 0) {
824
+ throw new Error(
825
+ `Refusing to normalize app role ${role}: a non-superuser administrator cannot clear protected attributes (${protectedAttributes.join(", ")})`
826
+ );
827
+ }
828
+ }
829
+ const relationshipRows = await sql2.unsafe(
830
+ roleRelationshipsCatalogQuery("$1"),
831
+ [role]
832
+ );
833
+ const { unsafeRelationships } = classifyRoleRelationships(relationshipRows);
834
+ if (unsafeRelationships.length > 0) {
709
835
  throw new Error(
710
- `Refusing to normalize app role ${role}: remove role relationships first (${memberships.map((row) => row.relationship).join(", ")})`
836
+ `Refusing to normalize app role ${role}: remove privilege-bearing role relationships first (${unsafeRelationships.join(", ")})`
711
837
  );
712
838
  }
713
839
  const ownedObjects = await sql2`
@@ -827,6 +953,39 @@ BEGIN
827
953
  ${literal(role)}
828
954
  );
829
955
  END IF;
956
+ IF to_regprocedure(
957
+ format(
958
+ '%I.preference_registry_apply_lifecycle(text,uuid,integer,uuid,uuid,text,uuid,text,text)',
959
+ ${literal(schema)}
960
+ )
961
+ ) IS NOT NULL THEN
962
+ EXECUTE format(
963
+ 'GRANT EXECUTE ON FUNCTION %I.preference_registry_apply_lifecycle(text, uuid, integer, uuid, uuid, text, uuid, text, text) TO %I',
964
+ ${literal(schema)},
965
+ ${literal(role)}
966
+ );
967
+ END IF;
968
+ IF to_regprocedure(
969
+ format('%I.preference_registry_lock_heads(uuid[])', ${literal(schema)})
970
+ ) IS NOT NULL THEN
971
+ EXECUTE format(
972
+ 'GRANT EXECUTE ON FUNCTION %I.preference_registry_lock_heads(uuid[]) TO %I',
973
+ ${literal(schema)},
974
+ ${literal(role)}
975
+ );
976
+ END IF;
977
+ IF to_regprocedure(
978
+ format(
979
+ '%I.preference_registry_get_or_create_snapshot(uuid,uuid,uuid,uuid,uuid,integer)',
980
+ ${literal(schema)}
981
+ )
982
+ ) IS NOT NULL THEN
983
+ EXECUTE format(
984
+ 'GRANT EXECUTE ON FUNCTION %I.preference_registry_get_or_create_snapshot(uuid, uuid, uuid, uuid, uuid, integer) TO %I',
985
+ ${literal(schema)},
986
+ ${literal(role)}
987
+ );
988
+ END IF;
830
989
  EXECUTE format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});
831
990
  EXECUTE format(
832
991
  'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I REVOKE ALL PRIVILEGES ON TABLES FROM %I',
@@ -903,4 +1062,4 @@ export {
903
1062
  runtimeDatabaseReadyCheck,
904
1063
  provisionRoles
905
1064
  };
906
- //# sourceMappingURL=chunk-ELMUCYZF.js.map
1065
+ //# sourceMappingURL=chunk-T6RSJT6C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provision-roles.ts","../src/runtime-posture.ts","../src/role-relationships.ts"],"sourcesContent":["import postgres from \"postgres\";\nimport type { RlsStrategy } from \"./index\";\nimport {\n RUNTIME_FULL_DML_TABLES,\n RUNTIME_READ_INSERT_TABLES,\n RUNTIME_READ_ONLY_TABLES,\n} from \"./runtime-posture\";\nimport {\n classifyRoleRelationships,\n roleRelationshipsCatalogQuery,\n type RoleRelationshipCatalogRow,\n} from \"./role-relationships\";\n\nexport type ProvisionResult = {\n appRole: string | null;\n hostExportRole: string | null;\n temporalRole: string | null;\n temporalDatabases: string[];\n schema: string;\n rlsStrategy: RlsStrategy;\n};\n\nexport type ProvisionRolesOptions = {\n /**\n * The schema OpenGeni's tables live in. The app-role GRANTs target this\n * schema + `opengeni_private`. Defaults to `public` (standalone).\n */\n targetSchema?: string;\n /**\n * RLS posture (Step I). `\"force\"` (default) provisions the non-owner\n * `opengeni_app` login role and GRANTs it table DML in the target schema —\n * the role OpenGeni connects as under FORCE-RLS. `\"scoped\"` SKIPS the app-role\n * provisioning entirely: the embedded host runs OpenGeni's queries over a role\n * IT owns/manages (typically the schema owner), so OpenGeni neither creates\n * nor grants the `opengeni_app` role. Temporal-role provisioning is unaffected\n * by strategy.\n */\n rlsStrategy?: RlsStrategy;\n appRole?: string;\n appPassword?: string;\n /**\n * Optional cross-workspace projection role. It receives schema USAGE and\n * EXECUTE only on the host-export API; it receives no table privileges.\n * Provision it after the first migration run so the schema exists. The\n * provisioner also registers same-owner default privileges for future\n * host-export functions; shipped migrations preserve existing exporter ACLs\n * when a migration-only upgrade adds a function.\n */\n hostExportRole?: string;\n hostExportPassword?: string;\n temporalRole?: string;\n temporalPassword?: string;\n temporalDatabases?: string[];\n};\n\n/**\n * SDK entry point (Step I): provision the OpenGeni database roles + grants over\n * a host-supplied admin connection. This is the named, parameterized form of the\n * historical env-driven `provision-roles` script (which still works as a CLI via\n * the `import.meta.main` block at the bottom — it just reads env into these\n * options).\n *\n * STANDALONE (default): `provisionRoles(adminConnection)` with no options →\n * `targetSchema: \"public\"`, `rlsStrategy: \"force\"`, reads `opengeni_app` creds\n * from env. Byte-for-byte the historical script behavior.\n *\n * EMBEDDED: `provisionRoles(adminConnection, { targetSchema, rlsStrategy })` lets\n * a host provision the app role over a dedicated schema (force) OR skip the\n * app role entirely and own the connection role itself (scoped).\n */\nexport async function provisionRoles(\n adminConnection: string,\n options: ProvisionRolesOptions = {},\n): Promise<ProvisionResult> {\n const schema = validateIdentifier(\"targetSchema\", options.targetSchema ?? \"public\");\n const rlsStrategy: RlsStrategy = options.rlsStrategy ?? \"force\";\n\n const appRole = validateIdentifier(\n \"appRole\",\n options.appRole ?? (process.env.OPENGENI_APP_DATABASE_USER?.trim() || \"opengeni_app\"),\n );\n const appPassword = options.appPassword ?? process.env.OPENGENI_APP_DATABASE_PASSWORD;\n const hostExportRole = validateIdentifier(\n \"hostExportRole\",\n options.hostExportRole ??\n (process.env.OPENGENI_HOST_EXPORT_DATABASE_USER?.trim() || \"opengeni_host_exporter\"),\n );\n const hostExportPassword =\n options.hostExportPassword ?? process.env.OPENGENI_HOST_EXPORT_DATABASE_PASSWORD;\n const temporalRole = validateIdentifier(\n \"temporalRole\",\n options.temporalRole ??\n (process.env.OPENGENI_TEMPORAL_DATABASE_USER?.trim() || \"opengeni_temporal\"),\n );\n const temporalPassword =\n options.temporalPassword ?? process.env.OPENGENI_TEMPORAL_DATABASE_PASSWORD;\n const temporalDatabases = (\n options.temporalDatabases ??\n commaSeparated(process.env.OPENGENI_TEMPORAL_DATABASES ?? \"temporal,temporal_visibility\")\n ).map((name) => validateIdentifier(\"temporalDatabases\", name));\n\n const sql = postgres(adminConnection, { max: 1 });\n try {\n // FORCE strategy provisions the non-owner app role OpenGeni connects as.\n // SCOPED strategy: the host owns the connection role; OpenGeni provisions no\n // app role (skipped here), only the optional Temporal role.\n let provisionedAppRole: string | null = null;\n if (rlsStrategy === \"force\") {\n if (!appPassword) {\n throw new Error(\n \"OPENGENI_APP_DATABASE_PASSWORD (or appPassword) is required for rlsStrategy 'force'\",\n );\n }\n // Ownership and privilege-bearing role-graph edges cannot be safely\n // guessed away. Refuse to mutate an existing role until an operator has\n // explicitly transferred objects/removed those memberships; role\n // attributes and direct grants, however, are deterministic and are\n // converged below on every run. PostgreSQL 16+'s exact non-runtime-bearing\n // creator-management edge is classified separately.\n await assertAppRoleSafeToNormalize(sql, appRole);\n await ensureRestrictedAppLoginRole(sql, appRole, appPassword);\n provisionedAppRole = appRole;\n }\n\n if (temporalPassword) {\n await ensureLoginRole(sql, temporalRole, temporalPassword);\n for (const database of temporalDatabases) {\n await ensureDatabase(sql, database, temporalRole);\n await grantTemporalRoleInDatabase(adminConnection, database, temporalRole);\n }\n }\n\n if (hostExportPassword) {\n await ensureLoginRole(sql, hostExportRole, hostExportPassword);\n await grantHostExportRoleIfSchemaExists(sql, hostExportRole);\n }\n\n if (rlsStrategy === \"force\") {\n await grantAppRoleIfSchemaExists(sql, appRole, schema);\n }\n\n return {\n appRole: provisionedAppRole,\n hostExportRole: hostExportPassword ? hostExportRole : null,\n temporalRole: temporalPassword ? temporalRole : null,\n temporalDatabases: temporalPassword ? temporalDatabases : [],\n schema,\n rlsStrategy,\n };\n } finally {\n await sql.end();\n }\n}\n\n/**\n * The exporter is intentionally separate from `opengeni_app`: its functions\n * project every workspace into a host-owned sink and therefore cannot be made\n * available to the tenant-scoped application role.\n */\nasync function grantHostExportRoleIfSchemaExists(sql: postgres.Sql, role: string): Promise<void> {\n await sql.unsafe(`\nDO $$\nBEGIN\n IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'opengeni_host_export') THEN\n EXECUTE format('GRANT USAGE ON SCHEMA opengeni_host_export TO %I', ${literal(role)});\n EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_host_export TO %I', ${literal(role)});\n EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA opengeni_host_export GRANT EXECUTE ON FUNCTIONS TO %I', ${literal(role)});\n END IF;\nEND $$;\n`);\n}\n\nasync function ensureLoginRole(sql: postgres.Sql, role: string, password: string): Promise<void> {\n await sql.unsafe(`\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = ${literal(role)}) THEN\n EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', ${literal(role)}, ${literal(password)});\n ELSE\n EXECUTE format('ALTER ROLE %I LOGIN PASSWORD %L', ${literal(role)}, ${literal(password)});\n END IF;\nEND $$;\n`);\n}\n\nasync function ensureRestrictedAppLoginRole(\n sql: postgres.Sql,\n role: string,\n password: string,\n): Promise<void> {\n await sql.unsafe(`\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = ${literal(role)}) THEN\n EXECUTE format(\n 'CREATE ROLE %I WITH LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEROLE NOCREATEDB NOREPLICATION NOINHERIT PASSWORD %L',\n ${literal(role)},\n ${literal(password)}\n );\n ELSIF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = current_user AND rolsuper) THEN\n EXECUTE format(\n 'ALTER ROLE %I WITH LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEROLE NOCREATEDB NOREPLICATION NOINHERIT PASSWORD %L',\n ${literal(role)},\n ${literal(password)}\n );\n ELSE\n -- PostgreSQL requires elevated attributes merely to name SUPERUSER,\n -- BYPASSRLS, REPLICATION, or CREATEDB in ALTER ROLE, even when setting them\n -- false. The preflight has already proved those protected attributes are\n -- false, so a managed CREATEROLE administrator converges only the\n -- attributes it may change.\n EXECUTE format(\n 'ALTER ROLE %I WITH LOGIN NOCREATEROLE NOINHERIT PASSWORD %L',\n ${literal(role)},\n ${literal(password)}\n );\n END IF;\nEND $$;\n`);\n}\n\n/**\n * Fail rather than silently revoking role relationships or transferring owned\n * objects. Those operations have effects outside OpenGeni's runtime grant\n * contract and require an explicit, audited operator decision.\n */\nasync function assertAppRoleSafeToNormalize(sql: postgres.Sql, role: string): Promise<void> {\n const exists = await sql<{ exists: boolean }[]>`\n select exists(select 1 from pg_roles where rolname = ${role}) as exists\n `;\n if (!exists[0]?.exists) {\n return;\n }\n\n const [roleAttributes] = await sql<\n {\n administrator_superuser: boolean;\n app_superuser: boolean;\n app_bypass_rls: boolean;\n app_create_database: boolean;\n app_replication: boolean;\n }[]\n >`\n select\n administrator.rolsuper as administrator_superuser,\n app.rolsuper as app_superuser,\n app.rolbypassrls as app_bypass_rls,\n app.rolcreatedb as app_create_database,\n app.rolreplication as app_replication\n from pg_roles app\n join pg_roles administrator on administrator.rolname = current_user\n where app.rolname = ${role}\n `;\n if (roleAttributes && !roleAttributes.administrator_superuser) {\n const protectedAttributes = [\n roleAttributes.app_superuser ? \"SUPERUSER\" : null,\n roleAttributes.app_bypass_rls ? \"BYPASSRLS\" : null,\n roleAttributes.app_create_database ? \"CREATEDB\" : null,\n roleAttributes.app_replication ? \"REPLICATION\" : null,\n ].filter((attribute): attribute is string => attribute !== null);\n if (protectedAttributes.length > 0) {\n throw new Error(\n `Refusing to normalize app role ${role}: a non-superuser administrator cannot clear protected attributes (${protectedAttributes.join(\", \")})`,\n );\n }\n }\n\n const relationshipRows = await sql.unsafe<RoleRelationshipCatalogRow[]>(\n roleRelationshipsCatalogQuery(\"$1\"),\n [role],\n );\n const { unsafeRelationships } = classifyRoleRelationships(relationshipRows);\n if (unsafeRelationships.length > 0) {\n throw new Error(\n `Refusing to normalize app role ${role}: remove privilege-bearing role relationships first (${unsafeRelationships.join(\", \")})`,\n );\n }\n\n const ownedObjects = await sql<{ object_name: string }[]>`\n select ('database:' || d.datname)::text as object_name\n from pg_database d\n join pg_roles owner on owner.oid = d.datdba\n where owner.rolname = ${role}\n union all\n select ('schema:' || n.nspname)::text as object_name\n from pg_namespace n\n join pg_roles owner on owner.oid = n.nspowner\n where owner.rolname = ${role}\n and n.nspname <> 'information_schema'\n and n.nspname !~ '^pg_'\n union all\n select ('relation:' || n.nspname || '.' || c.relname)::text as object_name\n from pg_class c\n join pg_namespace n on n.oid = c.relnamespace\n join pg_roles owner on owner.oid = c.relowner\n where owner.rolname = ${role}\n and n.nspname <> 'information_schema'\n and n.nspname !~ '^pg_'\n union all\n select (\n 'routine:' || n.nspname || '.' || p.proname || '(' ||\n pg_get_function_identity_arguments(p.oid) || ')'\n )::text as object_name\n from pg_proc p\n join pg_namespace n on n.oid = p.pronamespace\n join pg_roles owner on owner.oid = p.proowner\n where owner.rolname = ${role}\n and n.nspname <> 'information_schema'\n and n.nspname !~ '^pg_'\n order by object_name\n `;\n if (ownedObjects.length > 0) {\n throw new Error(\n `Refusing to normalize app role ${role}: transfer owned objects first (${ownedObjects\n .map((row) => row.object_name)\n .join(\", \")})`,\n );\n }\n}\n\nasync function ensureDatabase(sql: postgres.Sql, database: string, owner: string): Promise<void> {\n const existing = await sql<{ exists: boolean }[]>`\n select exists(select 1 from pg_database where datname = ${database}) as exists\n `;\n if (!existing[0]?.exists) {\n await sql.unsafe(`CREATE DATABASE ${identifier(database)} OWNER ${identifier(owner)}`);\n }\n await sql.unsafe(\n `GRANT ALL PRIVILEGES ON DATABASE ${identifier(database)} TO ${identifier(owner)}`,\n );\n}\n\nasync function grantTemporalRoleInDatabase(\n adminConnection: string,\n database: string,\n role: string,\n): Promise<void> {\n const databaseUrl = databaseUrlFor(adminConnection, database);\n const databaseSql = postgres(databaseUrl, { max: 1 });\n try {\n await databaseSql.unsafe(`GRANT USAGE, CREATE ON SCHEMA public TO ${identifier(role)}`);\n } finally {\n await databaseSql.end();\n }\n}\n\n/**\n * Grant the app role table DML in the OpenGeni data schema + EXECUTE on the\n * `opengeni_private` helper functions. Schema-parameterized (Step I): standalone\n * passes `public`; embedded passes the dedicated schema. The grants are guarded\n * on schema existence so provisioning before migrate is a safe no-op.\n */\nasync function grantAppRoleIfSchemaExists(\n sql: postgres.Sql,\n role: string,\n schema: string,\n): Promise<void> {\n const runtimeFullDmlTables = `ARRAY[${RUNTIME_FULL_DML_TABLES.map(literal).join(\", \")}]`;\n const runtimeReadOnlyTables = `ARRAY[${RUNTIME_READ_ONLY_TABLES.map(literal).join(\", \")}]`;\n const runtimeReadInsertTables = `ARRAY[${RUNTIME_READ_INSERT_TABLES.map(literal).join(\", \")}]`;\n await sql.unsafe(`\nDO $$\nDECLARE\n owner_role text := current_user;\n runtime_table text;\nBEGIN\n EXECUTE format('REVOKE CREATE ON DATABASE %I FROM %I', current_database(), ${literal(role)});\n IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = ${literal(schema)}) THEN\n EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});\n EXECUTE format('REVOKE CREATE ON SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});\n EXECUTE format('REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});\n FOREACH runtime_table IN ARRAY ${runtimeFullDmlTables} LOOP\n IF to_regclass(format('%I.%I', ${literal(schema)}, runtime_table)) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.%I TO %I',\n ${literal(schema)},\n runtime_table,\n ${literal(role)}\n );\n END IF;\n END LOOP;\n FOREACH runtime_table IN ARRAY ${runtimeReadOnlyTables} LOOP\n IF to_regclass(format('%I.%I', ${literal(schema)}, runtime_table)) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT SELECT ON TABLE %I.%I TO %I',\n ${literal(schema)},\n runtime_table,\n ${literal(role)}\n );\n END IF;\n END LOOP;\n FOREACH runtime_table IN ARRAY ${runtimeReadInsertTables} LOOP\n IF to_regclass(format('%I.%I', ${literal(schema)}, runtime_table)) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT SELECT, INSERT ON TABLE %I.%I TO %I',\n ${literal(schema)},\n runtime_table,\n ${literal(role)}\n );\n END IF;\n END LOOP;\n -- Migration 0110 creates this target-schema-local SECURITY DEFINER\n -- capability before opengeni_app may exist. Re-converge its exact EXECUTE\n -- grant here so the supported migrate-then-provision order is equivalent to\n -- provisioning the role before that migration.\n IF to_regprocedure(\n format('%I.lock_nested_agent_depth_configuration()', ${literal(schema)})\n ) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT EXECUTE ON FUNCTION %I.lock_nested_agent_depth_configuration() TO %I',\n ${literal(schema)},\n ${literal(role)}\n );\n END IF;\n IF to_regprocedure(\n format(\n '%I.preference_registry_apply_lifecycle(text,uuid,integer,uuid,uuid,text,uuid,text,text)',\n ${literal(schema)}\n )\n ) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT EXECUTE ON FUNCTION %I.preference_registry_apply_lifecycle(text, uuid, integer, uuid, uuid, text, uuid, text, text) TO %I',\n ${literal(schema)},\n ${literal(role)}\n );\n END IF;\n IF to_regprocedure(\n format('%I.preference_registry_lock_heads(uuid[])', ${literal(schema)})\n ) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT EXECUTE ON FUNCTION %I.preference_registry_lock_heads(uuid[]) TO %I',\n ${literal(schema)},\n ${literal(role)}\n );\n END IF;\n IF to_regprocedure(\n format(\n '%I.preference_registry_get_or_create_snapshot(uuid,uuid,uuid,uuid,uuid,integer)',\n ${literal(schema)}\n )\n ) IS NOT NULL THEN\n EXECUTE format(\n 'GRANT EXECUTE ON FUNCTION %I.preference_registry_get_or_create_snapshot(uuid, uuid, uuid, uuid, uuid, integer) TO %I',\n ${literal(schema)},\n ${literal(role)}\n );\n END IF;\n EXECUTE format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});\n EXECUTE format(\n 'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I REVOKE ALL PRIVILEGES ON TABLES FROM %I',\n owner_role,\n ${literal(schema)},\n ${literal(role)}\n );\n EXECUTE format(\n 'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I GRANT USAGE, SELECT ON SEQUENCES TO %I',\n owner_role,\n ${literal(schema)},\n ${literal(role)}\n );\n END IF;\n IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'opengeni_private') THEN\n EXECUTE format('GRANT USAGE ON SCHEMA opengeni_private TO %I', ${literal(role)});\n EXECUTE format('REVOKE CREATE ON SCHEMA opengeni_private FROM %I', ${literal(role)});\n EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_private TO %I', ${literal(role)});\n EXECUTE format(\n 'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA opengeni_private GRANT EXECUTE ON FUNCTIONS TO %I',\n owner_role,\n ${literal(role)}\n );\n END IF;\nEND $$;\n`);\n}\n\nfunction commaSeparated(value: string): string[] {\n return value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n}\n\nfunction validateIdentifier(name: string, value: string): string {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {\n throw new Error(`${name} contains an invalid Postgres identifier: ${value}`);\n }\n return value;\n}\n\nfunction identifier(value: string): string {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction literal(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\nfunction databaseUrlFor(value: string, database: string): string {\n const url = new URL(value);\n url.pathname = `/${database}`;\n return url.toString();\n}\n\n// CLI form (unchanged behavior): read env into the SDK options and run. This is\n// what `bun src/provision-roles.ts` / the `provision-roles` package script\n// invokes — standalone byte-for-byte the historical script (public schema,\n// force strategy, env-driven creds).\nif (import.meta.main) {\n const adminUrl =\n process.env.OPENGENI_MIGRATIONS_DATABASE_URL ??\n process.env.OPENGENI_DATABASE_ADMIN_URL ??\n process.env.OPENGENI_DATABASE_URL;\n if (!adminUrl) {\n throw new Error(\n \"OPENGENI_MIGRATIONS_DATABASE_URL, OPENGENI_DATABASE_ADMIN_URL, or OPENGENI_DATABASE_URL is required\",\n );\n }\n const result = await provisionRoles(adminUrl, {\n ...(process.env.OPENGENI_DB_SCHEMA?.trim()\n ? { targetSchema: process.env.OPENGENI_DB_SCHEMA.trim() }\n : {}),\n });\n console.log(JSON.stringify(result, null, 2));\n}\n","import { sql } from \"drizzle-orm\";\nimport type { Database, RlsStrategy } from \"./index\";\nimport {\n classifyRoleRelationships,\n roleRelationshipsCatalogQuery,\n type RoleRelationshipCatalogRow,\n} from \"./role-relationships\";\n\n/**\n * The complete standalone tenant-table contract. Adding or removing a\n * FORCE-RLS table is an architectural change: update this list in the same\n * commit as the migration so startup cannot silently accept an unreviewed gap.\n */\nexport const FORCE_RLS_TABLES = [\n \"agent_run_states\",\n \"api_keys\",\n \"audit_events\",\n \"billing_customers\",\n \"capability_catalog_items\",\n \"capability_installations\",\n \"codex_capacity_waiters\",\n \"codex_credential_leases\",\n \"codex_reset_redemption_attempts\",\n \"codex_rotation_settings\",\n \"codex_subscription_credentials\",\n \"composer_drafts\",\n \"connections\",\n \"credit_ledger_entries\",\n \"device_enrollment_requests\",\n \"document_bases\",\n \"document_chunks\",\n \"documents\",\n \"enrollments\",\n \"file_uploads\",\n \"files\",\n \"github_installation_repositories\",\n \"github_installations\",\n \"host_export_config\",\n \"host_export_consumers\",\n \"host_export_cursor_state\",\n \"host_export_dead_letters\",\n \"host_export_outbox\",\n \"import_batches\",\n \"integration_oauth_state_nonces\",\n \"knowledge_memories\",\n \"machine_metrics_latest\",\n \"machine_metrics_series\",\n \"model_call_facts\",\n \"new_session_drafts\",\n \"pack_installations\",\n \"preference_registry_events\",\n \"preference_registry_preferences\",\n \"preference_registry_revisions\",\n \"preference_registry_snapshots\",\n \"rig_changes\",\n \"rig_versions\",\n \"rigs\",\n \"sandbox_checkpoint_artifacts\",\n \"sandbox_lease_holders\",\n \"sandbox_leases\",\n \"sandbox_pty_sessions\",\n \"sandbox_retained_processes\",\n \"sandbox_session_envelopes\",\n \"sandbox_workspace_mutation_admissions\",\n \"sandboxes\",\n \"scheduled_task_runs\",\n \"scheduled_tasks\",\n \"session_attempt_interruptions\",\n \"session_command_receipts\",\n \"session_events\",\n \"session_goals\",\n \"session_history_items\",\n \"session_human_input_requests\",\n \"session_list_snapshots\",\n \"session_mcp_servers\",\n \"session_pending_tool_calls\",\n \"session_pins\",\n \"session_recordings\",\n \"session_spawn_denials\",\n \"session_stream_acknowledgments\",\n \"session_system_update_outbox\",\n \"session_system_updates\",\n \"session_turn_attempts\",\n \"session_turns\",\n \"session_workflow_wake_outbox\",\n \"sessions\",\n \"slack_bot_delete_operations\",\n \"slack_bot_post_operations\",\n \"slack_bot_user_links\",\n \"slack_interaction_inbox\",\n \"slack_interaction_progress_deliveries\",\n \"slack_interactions\",\n \"social_connections\",\n \"social_posts\",\n \"usage_events\",\n \"workspace_artifact_events\",\n \"workspace_artifact_versions\",\n \"workspace_artifacts\",\n \"workspace_captures\",\n \"workspace_control_events\",\n \"workspace_inference_controls\",\n \"workspace_instruction_policy_activation_events\",\n \"workspace_instruction_policy_heads\",\n \"workspace_instruction_policy_revisions\",\n \"workspace_model_policies\",\n \"workspace_packs\",\n \"workspace_session_activity_revisions\",\n \"workspace_variable_set_variables\",\n \"workspace_variable_sets\",\n] as const;\n\n/**\n * Deployment-global and authentication tables used by ordinary API/worker\n * traffic. They intentionally do not carry workspace RLS: their access model\n * is implemented by the authentication/access layer or by exact global keys.\n */\nexport const NON_RLS_RUNTIME_TABLES = [\n \"auth_identities\",\n \"auth_rate_limits\",\n \"auth_sessions\",\n \"auth_users\",\n \"auth_verifications\",\n \"integration_oauth_clients\",\n \"managed_accounts\",\n \"nested_agent_depth_configuration\",\n \"stripe_webhook_events\",\n \"workspace_memberships\",\n \"workspaces\",\n] as const;\n\n/**\n * Exact full-CRUD class for the standalone runtime role. This is deliberately\n * explicit instead of being derived from FORCE_RLS_TABLES: adding a protected\n * table must not silently grant it broader DML than its migration intended.\n */\nexport const RUNTIME_FULL_DML_TABLES = [\n \"agent_run_states\",\n \"api_keys\",\n \"audit_events\",\n \"auth_identities\",\n \"auth_rate_limits\",\n \"auth_sessions\",\n \"auth_users\",\n \"auth_verifications\",\n \"billing_customers\",\n \"capability_catalog_items\",\n \"capability_installations\",\n \"codex_capacity_waiters\",\n \"codex_credential_leases\",\n \"codex_reset_redemption_attempts\",\n \"codex_rotation_settings\",\n \"codex_subscription_credentials\",\n \"composer_drafts\",\n \"connections\",\n \"credit_ledger_entries\",\n \"device_enrollment_requests\",\n \"document_bases\",\n \"document_chunks\",\n \"documents\",\n \"enrollments\",\n \"file_uploads\",\n \"files\",\n \"github_installation_repositories\",\n \"github_installations\",\n \"import_batches\",\n \"integration_oauth_clients\",\n \"integration_oauth_state_nonces\",\n \"knowledge_memories\",\n \"machine_metrics_latest\",\n \"machine_metrics_series\",\n \"managed_accounts\",\n \"model_call_facts\",\n \"new_session_drafts\",\n \"pack_installations\",\n \"rig_changes\",\n \"rig_versions\",\n \"rigs\",\n \"sandbox_checkpoint_artifacts\",\n \"sandbox_lease_holders\",\n \"sandbox_leases\",\n \"sandbox_pty_sessions\",\n \"sandbox_retained_processes\",\n \"sandbox_session_envelopes\",\n \"sandbox_workspace_mutation_admissions\",\n \"sandboxes\",\n \"scheduled_task_runs\",\n \"scheduled_tasks\",\n \"session_attempt_interruptions\",\n \"session_command_receipts\",\n \"session_events\",\n \"session_goals\",\n \"session_history_items\",\n \"session_human_input_requests\",\n \"session_list_snapshots\",\n \"session_mcp_servers\",\n \"session_pending_tool_calls\",\n \"session_pins\",\n \"session_recordings\",\n \"session_stream_acknowledgments\",\n \"session_system_update_outbox\",\n \"session_system_updates\",\n \"session_turn_attempts\",\n \"session_turns\",\n \"session_workflow_wake_outbox\",\n \"sessions\",\n \"slack_bot_delete_operations\",\n \"slack_bot_post_operations\",\n \"slack_bot_user_links\",\n \"slack_interaction_inbox\",\n \"slack_interaction_progress_deliveries\",\n \"slack_interactions\",\n \"social_connections\",\n \"social_posts\",\n \"stripe_webhook_events\",\n \"usage_events\",\n \"workspace_artifacts\",\n \"workspace_captures\",\n \"workspace_control_events\",\n \"workspace_inference_controls\",\n \"workspace_instruction_policy_heads\",\n \"workspace_memberships\",\n \"workspace_model_policies\",\n \"workspace_packs\",\n \"workspace_session_activity_revisions\",\n \"workspace_variable_set_variables\",\n \"workspace_variable_sets\",\n \"workspaces\",\n] as const;\n\n/** Configuration and lifecycle-owned audit rows are read-only at runtime. */\nexport const RUNTIME_READ_ONLY_TABLES = [\n \"nested_agent_depth_configuration\",\n \"preference_registry_events\",\n \"preference_registry_snapshots\",\n] as const;\n\n/** Append-only evidence/revision tables are insertable and queryable, never mutable. */\nexport const RUNTIME_READ_INSERT_TABLES = [\n \"preference_registry_preferences\",\n \"preference_registry_revisions\",\n \"session_spawn_denials\",\n \"workspace_artifact_events\",\n \"workspace_artifact_versions\",\n \"workspace_instruction_policy_activation_events\",\n \"workspace_instruction_policy_revisions\",\n] as const;\n\n/**\n * These FORCE-RLS tables are owned by security-definer host-export routines.\n * The ordinary application role must have no direct table privileges on them.\n */\nexport const PROTECTED_NO_DIRECT_DML_TABLES = [\n \"host_export_config\",\n \"host_export_consumers\",\n \"host_export_cursor_state\",\n \"host_export_dead_letters\",\n \"host_export_outbox\",\n] as const;\n\nexport type RuntimeTableDmlPrivilege = \"SELECT\" | \"INSERT\" | \"UPDATE\" | \"DELETE\";\nexport type RuntimeTablePrivilegeContract = Readonly<\n Record<string, readonly RuntimeTableDmlPrivilege[]>\n>;\n\nconst FULL_DML_PRIVILEGES = [\"SELECT\", \"INSERT\", \"UPDATE\", \"DELETE\"] as const;\n\n/** Exact per-table DML contract. Absence means no direct table privileges. */\nexport const RUNTIME_TABLE_PRIVILEGES: RuntimeTablePrivilegeContract = Object.freeze({\n ...Object.fromEntries(RUNTIME_FULL_DML_TABLES.map((table) => [table, FULL_DML_PRIVILEGES])),\n ...Object.fromEntries(RUNTIME_READ_ONLY_TABLES.map((table) => [table, [\"SELECT\"] as const])),\n ...Object.fromEntries(\n RUNTIME_READ_INSERT_TABLES.map((table) => [table, [\"SELECT\", \"INSERT\"] as const]),\n ),\n});\n\n/** All tables with any direct runtime DML; retained as the aggregate public contract. */\nexport const RUNTIME_DML_TABLES = Object.freeze(\n Object.keys(RUNTIME_TABLE_PRIVILEGES).sort((a, b) => a.localeCompare(b)),\n);\n\nexport type RuntimeDatabasePostureOptions = {\n rlsStrategy: RlsStrategy;\n expectedRole?: string;\n targetSchema?: string;\n protectedTables?: readonly string[];\n tablePrivileges?: RuntimeTablePrivilegeContract;\n protectedNoDirectDmlTables?: readonly string[];\n};\n\nexport type RuntimeDatabaseIdentity = {\n currentUser: string;\n sessionUser: string;\n databaseOwner: string;\n canConnectDatabase: boolean;\n canCreateInDatabase: boolean;\n rowSecurity: string;\n canLogin: boolean;\n superuser: boolean;\n inherit: boolean;\n createRole: boolean;\n createDatabase: boolean;\n replication: boolean;\n bypassRls: boolean;\n};\n\nexport type RuntimeSchemaPosture = {\n name: string;\n owner: string;\n usage: boolean;\n create: boolean;\n};\n\nexport type RuntimeTablePosture = {\n name: string;\n owner: string;\n rlsEnabled: boolean;\n rlsForced: boolean;\n rlsActive: boolean;\n policyCount: number;\n select: boolean;\n insert: boolean;\n update: boolean;\n delete: boolean;\n truncate: boolean;\n references: boolean;\n trigger: boolean;\n};\n\nexport type RuntimeRoutinePosture = {\n name: string;\n owner: string;\n execute: boolean;\n};\n\nexport type RuntimeDatabasePosture = {\n identity: RuntimeDatabaseIdentity;\n /** Privilege-bearing role relationships; exact PG16+ management-only grants are excluded. */\n memberships: string[];\n schemas: RuntimeSchemaPosture[];\n ownedSchemas: string[];\n ownedRelations: string[];\n tables: RuntimeTablePosture[];\n privateRoutines: RuntimeRoutinePosture[];\n};\n\nexport class RuntimeDatabasePostureError extends Error {\n readonly violations: readonly string[];\n\n constructor(violations: readonly string[]) {\n super(`Runtime database posture check failed: ${violations.join(\"; \")}`);\n this.name = \"RuntimeDatabasePostureError\";\n this.violations = violations;\n }\n}\n\ntype IdentityRow = {\n current_user: string;\n session_user: string;\n database_owner: string;\n can_connect_database: boolean;\n can_create_in_database: boolean;\n row_security: string;\n rolcanlogin: boolean;\n rolsuper: boolean;\n rolinherit: boolean;\n rolcreaterole: boolean;\n rolcreatedb: boolean;\n rolreplication: boolean;\n rolbypassrls: boolean;\n};\n\nfunction resultRows<T>(result: unknown): T[] {\n if (Array.isArray(result)) {\n return result as T[];\n }\n const rows = (result as { rows?: unknown } | null)?.rows;\n if (Array.isArray(rows)) {\n return rows as T[];\n }\n throw new Error(\"Runtime database posture query returned an unsupported result shape\");\n}\n\nfunction sorted(values: Iterable<string>): string[] {\n return [...values].sort((a, b) => a.localeCompare(b));\n}\n\nfunction difference(left: ReadonlySet<string>, right: ReadonlySet<string>): string[] {\n return sorted([...left].filter((value) => !right.has(value)));\n}\n\n/** Inspect only PostgreSQL catalogs and privilege functions; no tenant rows. */\nexport async function inspectRuntimeDatabasePosture(\n db: Database,\n options: RuntimeDatabasePostureOptions,\n): Promise<RuntimeDatabasePosture> {\n const targetSchema = options.targetSchema?.trim() || \"public\";\n\n return await db.transaction(\n async (tx) => {\n const identityRows = resultRows<IdentityRow>(\n await tx.execute(sql`\n select\n current_user::text as current_user,\n session_user::text as session_user,\n pg_get_userbyid(d.datdba)::text as database_owner,\n has_database_privilege(current_user, d.oid, 'CONNECT') as can_connect_database,\n has_database_privilege(current_user, d.oid, 'CREATE') as can_create_in_database,\n current_setting('row_security')::text as row_security,\n r.rolcanlogin,\n r.rolsuper,\n r.rolinherit,\n r.rolcreaterole,\n r.rolcreatedb,\n r.rolreplication,\n r.rolbypassrls\n from pg_roles r\n join pg_database d on d.datname = current_database()\n where r.rolname = current_user\n `),\n );\n const identity = identityRows[0];\n if (!identity) {\n throw new Error(\"Runtime database posture could not resolve the current PostgreSQL role\");\n }\n\n const mappedIdentity: RuntimeDatabaseIdentity = {\n currentUser: identity.current_user,\n sessionUser: identity.session_user,\n databaseOwner: identity.database_owner,\n canConnectDatabase: identity.can_connect_database,\n canCreateInDatabase: identity.can_create_in_database,\n rowSecurity: identity.row_security,\n canLogin: identity.rolcanlogin,\n superuser: identity.rolsuper,\n inherit: identity.rolinherit,\n createRole: identity.rolcreaterole,\n createDatabase: identity.rolcreatedb,\n replication: identity.rolreplication,\n bypassRls: identity.rolbypassrls,\n };\n\n // Scoped/embedded topology deliberately leaves ownership and isolation to\n // the host. Prove the connection identity is coherent, but do not impose\n // the standalone opengeni_app object/grant contract on the host's role.\n if (options.rlsStrategy === \"scoped\") {\n return {\n identity: mappedIdentity,\n memberships: [],\n schemas: [],\n ownedSchemas: [],\n ownedRelations: [],\n tables: [],\n privateRoutines: [],\n };\n }\n\n const relationshipRows = resultRows<RoleRelationshipCatalogRow>(\n await tx.execute(sql.raw(roleRelationshipsCatalogQuery(\"current_user\"))),\n );\n const memberships = classifyRoleRelationships(relationshipRows).unsafeRelationships;\n\n const schemas = resultRows<{\n name: string;\n owner: string;\n usage: boolean;\n create: boolean;\n }>(\n await tx.execute(sql`\n select\n n.nspname::text as name,\n pg_get_userbyid(n.nspowner)::text as owner,\n has_schema_privilege(current_user, n.oid, 'USAGE') as usage,\n has_schema_privilege(current_user, n.oid, 'CREATE') as create\n from pg_namespace n\n where n.nspname in (${targetSchema}, 'opengeni_private')\n order by n.nspname\n `),\n );\n\n const ownedSchemas = resultRows<{ name: string }>(\n await tx.execute(sql`\n select n.nspname::text as name\n from pg_namespace n\n join pg_roles r on r.oid = n.nspowner\n where r.rolname = current_user\n and n.nspname <> 'information_schema'\n and n.nspname !~ '^pg_'\n order by n.nspname\n `),\n ).map((row) => row.name);\n\n const ownedRelations = resultRows<{ name: string }>(\n await tx.execute(sql`\n select (n.nspname || '.' || c.relname)::text as name\n from pg_class c\n join pg_namespace n on n.oid = c.relnamespace\n join pg_roles r on r.oid = c.relowner\n where r.rolname = current_user\n and c.relkind in ('r', 'p', 'S', 'v', 'm', 'f')\n and n.nspname <> 'information_schema'\n and n.nspname !~ '^pg_'\n order by n.nspname, c.relname\n `),\n ).map((row) => row.name);\n\n const tables = resultRows<{\n name: string;\n owner: string;\n rls_enabled: boolean;\n rls_forced: boolean;\n rls_active: boolean;\n policy_count: number;\n can_select: boolean;\n can_insert: boolean;\n can_update: boolean;\n can_delete: boolean;\n can_truncate: boolean;\n can_references: boolean;\n can_trigger: boolean;\n }>(\n await tx.execute(sql`\n select\n c.relname::text as name,\n pg_get_userbyid(c.relowner)::text as owner,\n c.relrowsecurity as rls_enabled,\n c.relforcerowsecurity as rls_forced,\n row_security_active(c.oid) as rls_active,\n (select count(*)::int from pg_policy policy where policy.polrelid = c.oid) as policy_count,\n has_table_privilege(current_user, c.oid, 'SELECT') as can_select,\n has_table_privilege(current_user, c.oid, 'INSERT') as can_insert,\n has_table_privilege(current_user, c.oid, 'UPDATE') as can_update,\n has_table_privilege(current_user, c.oid, 'DELETE') as can_delete,\n has_table_privilege(current_user, c.oid, 'TRUNCATE') as can_truncate,\n has_table_privilege(current_user, c.oid, 'REFERENCES') as can_references,\n has_table_privilege(current_user, c.oid, 'TRIGGER') as can_trigger\n from pg_class c\n join pg_namespace n on n.oid = c.relnamespace\n where n.nspname = ${targetSchema}\n and c.relkind in ('r', 'p')\n order by c.relname\n `),\n ).map((row) => ({\n name: row.name,\n owner: row.owner,\n rlsEnabled: row.rls_enabled,\n rlsForced: row.rls_forced,\n rlsActive: row.rls_active,\n policyCount: row.policy_count,\n select: row.can_select,\n insert: row.can_insert,\n update: row.can_update,\n delete: row.can_delete,\n truncate: row.can_truncate,\n references: row.can_references,\n trigger: row.can_trigger,\n }));\n\n const privateRoutines = resultRows<{\n name: string;\n owner: string;\n can_execute: boolean;\n }>(\n await tx.execute(sql`\n select\n (p.proname || '(' || pg_get_function_identity_arguments(p.oid) || ')')::text as name,\n pg_get_userbyid(p.proowner)::text as owner,\n has_function_privilege(current_user, p.oid, 'EXECUTE') as can_execute\n from pg_proc p\n join pg_namespace n on n.oid = p.pronamespace\n where n.nspname = 'opengeni_private'\n and p.prokind in ('f', 'p')\n order by p.proname, pg_get_function_identity_arguments(p.oid)\n `),\n ).map((row) => ({\n name: row.name,\n owner: row.owner,\n execute: row.can_execute,\n }));\n\n return {\n identity: mappedIdentity,\n memberships,\n schemas,\n ownedSchemas,\n ownedRelations,\n tables,\n privateRoutines,\n };\n },\n { isolationLevel: \"repeatable read\", accessMode: \"read only\" },\n );\n}\n\n/** Pure deterministic evaluator used by startup/readiness and unit tests. */\nexport function evaluateRuntimeDatabasePosture(\n posture: RuntimeDatabasePosture,\n options: RuntimeDatabasePostureOptions,\n): string[] {\n const violations: string[] = [];\n const identity = posture.identity;\n\n if (!identity.currentUser || !identity.sessionUser) {\n violations.push(\"database identity is empty\");\n }\n if (identity.currentUser !== identity.sessionUser) {\n violations.push(\n `current_user ${identity.currentUser} does not match session_user ${identity.sessionUser}`,\n );\n }\n if (!identity.canConnectDatabase) {\n violations.push(\"runtime role lacks CONNECT on the current database\");\n }\n\n if (options.rlsStrategy === \"scoped\") {\n return violations;\n }\n\n const expectedRole = options.expectedRole?.trim() || \"opengeni_app\";\n const targetSchema = options.targetSchema?.trim() || \"public\";\n const protectedTables = new Set(options.protectedTables ?? FORCE_RLS_TABLES);\n const tablePrivileges = options.tablePrivileges ?? RUNTIME_TABLE_PRIVILEGES;\n const directRuntimeTables = new Set(Object.keys(tablePrivileges));\n const protectedNoDirectDmlTables = new Set(\n options.protectedNoDirectDmlTables ??\n (options.protectedTables ? [] : PROTECTED_NO_DIRECT_DML_TABLES),\n );\n\n if (identity.currentUser !== expectedRole || identity.sessionUser !== expectedRole) {\n violations.push(\n `runtime identity must be ${expectedRole} (current_user=${identity.currentUser}, session_user=${identity.sessionUser})`,\n );\n }\n if (!identity.canLogin) violations.push(\"runtime role is not LOGIN\");\n if (identity.superuser) violations.push(\"runtime role is SUPERUSER\");\n if (identity.bypassRls) violations.push(\"runtime role has BYPASSRLS\");\n if (identity.createRole) violations.push(\"runtime role has CREATEROLE\");\n if (identity.createDatabase) violations.push(\"runtime role has CREATEDB\");\n if (identity.replication) violations.push(\"runtime role has REPLICATION\");\n if (identity.inherit) violations.push(\"runtime role must be NOINHERIT\");\n if (identity.databaseOwner === expectedRole) violations.push(\"runtime role owns the database\");\n if (identity.canCreateInDatabase) {\n violations.push(\"runtime role has CREATE on the current database\");\n }\n if (identity.rowSecurity.toLowerCase() !== \"on\") {\n violations.push(`row_security is ${identity.rowSecurity}, expected on`);\n }\n if (posture.memberships.length > 0) {\n violations.push(`runtime role has memberships: ${sorted(posture.memberships).join(\", \")}`);\n }\n if (posture.ownedSchemas.length > 0) {\n violations.push(`runtime role owns schemas: ${sorted(posture.ownedSchemas).join(\", \")}`);\n }\n if (posture.ownedRelations.length > 0) {\n violations.push(`runtime role owns relations: ${sorted(posture.ownedRelations).join(\", \")}`);\n }\n\n for (const schemaName of new Set([targetSchema, \"opengeni_private\"])) {\n const schema = posture.schemas.find((candidate) => candidate.name === schemaName);\n if (!schema) {\n violations.push(`required schema ${schemaName} is missing`);\n continue;\n }\n if (schema.owner === expectedRole) {\n violations.push(`runtime role owns schema ${schemaName}`);\n }\n if (!schema.usage) violations.push(`runtime role lacks USAGE on schema ${schemaName}`);\n if (schema.create) violations.push(`runtime role has CREATE on schema ${schemaName}`);\n }\n\n const tableByName = new Map(posture.tables.map((table) => [table.name, table]));\n const actualRlsTables = new Set(\n posture.tables.filter((table) => table.rlsEnabled).map((table) => table.name),\n );\n const classifiedProtectedTables = new Set([\n ...directRuntimeTables,\n ...protectedNoDirectDmlTables,\n ]);\n const unclassifiedProtectedTables = difference(protectedTables, classifiedProtectedTables);\n if (unclassifiedProtectedTables.length > 0) {\n violations.push(\n `protected tables lack an explicit privilege class: ${unclassifiedProtectedTables.join(\", \")}`,\n );\n }\n const protectedNoDirectDmlOverlap = difference(\n protectedNoDirectDmlTables,\n new Set([...protectedNoDirectDmlTables].filter((table) => !directRuntimeTables.has(table))),\n );\n if (protectedNoDirectDmlOverlap.length > 0) {\n violations.push(\n `protected no-direct-DML tables also declare runtime privileges: ${protectedNoDirectDmlOverlap.join(\", \")}`,\n );\n }\n const nonProtectedNoDirectDmlTables = difference(protectedNoDirectDmlTables, protectedTables);\n if (nonProtectedNoDirectDmlTables.length > 0) {\n violations.push(\n `no-direct-DML tables are absent from the protected contract: ${nonProtectedNoDirectDmlTables.join(\", \")}`,\n );\n }\n const catalogTables = new Set(tableByName.keys());\n const missingRuntimeTables = difference(directRuntimeTables, catalogTables);\n if (missingRuntimeTables.length > 0) {\n violations.push(`runtime privilege tables are missing: ${missingRuntimeTables.join(\", \")}`);\n }\n const missingTables = difference(protectedTables, catalogTables);\n if (missingTables.length > 0) {\n violations.push(`protected tables are missing: ${missingTables.join(\", \")}`);\n }\n const undeclaredRlsTables = difference(actualRlsTables, protectedTables);\n if (undeclaredRlsTables.length > 0) {\n violations.push(\n `RLS tables are absent from the declared contract: ${undeclaredRlsTables.join(\", \")}`,\n );\n }\n\n for (const table of posture.tables) {\n const privileges = [\n [\"SELECT\", table.select],\n [\"INSERT\", table.insert],\n [\"UPDATE\", table.update],\n [\"DELETE\", table.delete],\n [\"TRUNCATE\", table.truncate],\n [\"REFERENCES\", table.references],\n [\"TRIGGER\", table.trigger],\n ] as const;\n const expectedPrivileges = new Set<string>(tablePrivileges[table.name] ?? []);\n if (table.owner === expectedRole) {\n violations.push(`runtime role owns table ${table.name}`);\n }\n const missingPrivileges = privileges\n .filter(([privilege, granted]) => expectedPrivileges.has(privilege) && !granted)\n .map(([privilege]) => privilege);\n if (missingPrivileges.length > 0) {\n violations.push(\n `table ${table.name} lacks required runtime privileges: ${missingPrivileges.join(\", \")}`,\n );\n }\n const excessPrivileges = privileges\n .filter(([privilege, granted]) => !expectedPrivileges.has(privilege) && granted)\n .map(([privilege]) => privilege);\n if (excessPrivileges.length > 0) {\n violations.push(\n `table ${table.name} grants excess runtime privileges: ${excessPrivileges.join(\", \")}`,\n );\n }\n }\n\n for (const tableName of protectedTables) {\n const table = tableByName.get(tableName);\n if (!table) continue;\n if (!table.rlsEnabled) violations.push(`table ${tableName} does not ENABLE RLS`);\n if (!table.rlsForced) violations.push(`table ${tableName} does not FORCE RLS`);\n if (!table.rlsActive) violations.push(`table ${tableName} has inactive RLS for runtime role`);\n if (table.policyCount < 1) violations.push(`table ${tableName} has no RLS policy`);\n }\n\n if (posture.privateRoutines.length === 0) {\n violations.push(\"opengeni_private has no helper routines\");\n }\n for (const routine of posture.privateRoutines) {\n if (routine.owner === expectedRole) {\n violations.push(`runtime role owns private routine ${routine.name}`);\n }\n if (!routine.execute) {\n violations.push(`runtime role lacks EXECUTE on private routine ${routine.name}`);\n }\n }\n\n return violations;\n}\n\nexport async function assertRuntimeDatabasePosture(\n db: Database,\n options: RuntimeDatabasePostureOptions,\n): Promise<RuntimeDatabasePosture> {\n const posture = await inspectRuntimeDatabasePosture(db, options);\n const violations = evaluateRuntimeDatabasePosture(posture, options);\n if (violations.length > 0) {\n throw new RuntimeDatabasePostureError(violations);\n }\n return posture;\n}\n\nexport function runtimeDatabaseReadyCheck(\n db: Database,\n options: RuntimeDatabasePostureOptions,\n): () => Promise<void> {\n return async () => {\n await assertRuntimeDatabasePosture(db, options);\n };\n}\n","export type RoleRelationshipDirection = \"inherits\" | \"member\";\n\nexport type RoleRelationshipCatalogRow = {\n relationship: string;\n direction: RoleRelationshipDirection;\n server_version_num: number;\n admin_option: boolean | null;\n inherit_option: boolean | null;\n set_option: boolean | null;\n member_is_superuser: boolean | null;\n member_can_create_role: boolean | null;\n grantor_role: string | null;\n grantor_is_superuser: boolean | null;\n};\n\nexport type ClassifiedRoleRelationships = {\n relationships: string[];\n managementOnlyRelationships: string[];\n unsafeRelationships: string[];\n};\n\ntype RoleTargetExpression = \"current_user\" | \"$1\";\n\n/**\n * One PostgreSQL 15-compatible catalog query for both provisioning and runtime\n * posture. PostgreSQL 16 added per-membership INHERIT and SET columns; reading\n * the catalog row through to_jsonb keeps this statement parseable on 15 while\n * leaving the missing options null and therefore fail-closed.\n */\nexport function roleRelationshipsCatalogQuery(targetRole: RoleTargetExpression): string {\n return `\nwith recursive target_role(oid, rolname) as (\n select oid, rolname\n from pg_roles\n where rolname = ${targetRole}\n), inherited_roles(oid, rolname) as (\n select parent.oid, parent.rolname\n from pg_auth_members membership\n join target_role target on target.oid = membership.member\n join pg_roles parent on parent.oid = membership.roleid\n union\n select parent.oid, parent.rolname\n from inherited_roles inherited\n join pg_auth_members membership on membership.member = inherited.oid\n join pg_roles parent on parent.oid = membership.roleid\n), relationships as (\n select\n ('inherits:' || inherited.rolname)::text as relationship,\n 'inherits'::text as direction,\n null::boolean as admin_option,\n null::boolean as inherit_option,\n null::boolean as set_option,\n null::boolean as member_is_superuser,\n null::boolean as member_can_create_role,\n null::text as grantor_role,\n null::boolean as grantor_is_superuser\n from inherited_roles inherited\n union all\n select\n ('member:' || member.rolname)::text as relationship,\n 'member'::text as direction,\n membership.admin_option,\n case\n when current_setting('server_version_num')::integer >= 160000\n then (to_jsonb(membership) ->> 'inherit_option')::boolean\n else null::boolean\n end as inherit_option,\n case\n when current_setting('server_version_num')::integer >= 160000\n then (to_jsonb(membership) ->> 'set_option')::boolean\n else null::boolean\n end as set_option,\n member.rolsuper as member_is_superuser,\n member.rolcreaterole as member_can_create_role,\n grantor.rolname::text as grantor_role,\n grantor.rolsuper as grantor_is_superuser\n from pg_auth_members membership\n join target_role target on target.oid = membership.roleid\n join pg_roles member on member.oid = membership.member\n left join pg_roles grantor on grantor.oid = membership.grantor\n)\nselect\n relationship,\n direction,\n current_setting('server_version_num')::integer as server_version_num,\n admin_option,\n inherit_option,\n set_option,\n member_is_superuser,\n member_can_create_role,\n grantor_role,\n grantor_is_superuser\nfrom relationships\norder by relationship, direction, grantor_role nulls first\n`;\n}\n\n/**\n * PostgreSQL 16+ automatically gives a non-superuser CREATEROLE principal an\n * ADMIN-only grant on a role it creates. The exact edge below permits role\n * management but cannot inherit the app role's privileges or SET ROLE into it.\n */\nexport function isManagementOnlyRoleRelationship(row: RoleRelationshipCatalogRow): boolean {\n return (\n row.direction === \"member\" &&\n row.server_version_num >= 160000 &&\n row.admin_option === true &&\n row.inherit_option === false &&\n row.set_option === false &&\n row.member_is_superuser === false &&\n row.member_can_create_role === true &&\n row.grantor_is_superuser === true\n );\n}\n\nfunction sortedUnique(values: Iterable<string>): string[] {\n return [...new Set(values)].sort((a, b) => a.localeCompare(b));\n}\n\n/** Classify exact management-only reverse grants while rejecting every other edge. */\nexport function classifyRoleRelationships(\n rows: readonly RoleRelationshipCatalogRow[],\n): ClassifiedRoleRelationships {\n const managementOnlyRows = rows.filter(isManagementOnlyRoleRelationship);\n const unsafeRows = rows.filter((row) => !isManagementOnlyRoleRelationship(row));\n\n return {\n relationships: sortedUnique(rows.map((row) => row.relationship)),\n managementOnlyRelationships: sortedUnique(managementOnlyRows.map((row) => row.relationship)),\n unsafeRelationships: sortedUnique(unsafeRows.map((row) => row.relationship)),\n };\n}\n"],"mappings":";AAAA,OAAO,cAAc;;;ACArB,SAAS,WAAW;;;AC6Bb,SAAS,8BAA8B,YAA0C;AACtF,SAAO;AAAA;AAAA;AAAA;AAAA,oBAIW,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6D9B;AAOO,SAAS,iCAAiC,KAA0C;AACzF,SACE,IAAI,cAAc,YAClB,IAAI,sBAAsB,QAC1B,IAAI,iBAAiB,QACrB,IAAI,mBAAmB,SACvB,IAAI,eAAe,SACnB,IAAI,wBAAwB,SAC5B,IAAI,2BAA2B,QAC/B,IAAI,yBAAyB;AAEjC;AAEA,SAAS,aAAa,QAAoC;AACxD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAC/D;AAGO,SAAS,0BACd,MAC6B;AAC7B,QAAM,qBAAqB,KAAK,OAAO,gCAAgC;AACvE,QAAM,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,iCAAiC,GAAG,CAAC;AAE9E,SAAO;AAAA,IACL,eAAe,aAAa,KAAK,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;AAAA,IAC/D,6BAA6B,aAAa,mBAAmB,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;AAAA,IAC3F,qBAAqB,aAAa,WAAW,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;AAAA,EAC7E;AACF;;;ADtHO,IAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AA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","names":["sql"]}