@opengeni/db 0.14.3 → 0.16.2

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 (63) hide show
  1. package/dist/{chunk-ELMUCYZF.js → chunk-4RUK5CON.js} +183 -42
  2. package/dist/chunk-4RUK5CON.js.map +1 -0
  3. package/dist/chunk-HZD7KVAR.js +4648 -0
  4. package/dist/chunk-HZD7KVAR.js.map +1 -0
  5. package/dist/{chunk-Y5WZZVQK.js → chunk-SK7YYHBI.js} +5 -2
  6. package/dist/chunk-SK7YYHBI.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 +6217 -10
  12. package/dist/index.js +6930 -2708
  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 +23533 -3
  28. package/dist/schema.js +17 -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-instruction-policies-schema.d.ts +735 -0
  34. package/dist/workspace-instruction-policies.d.ts +64 -0
  35. package/drizzle/0136_unified_session_tool_policy.sql +1 -1
  36. package/drizzle/0137_preference_registry.sql +1347 -0
  37. package/drizzle/0138_sandbox_checkpoint_artifacts_and_deadlines.sql +1074 -0
  38. package/drizzle/0139_codex_provider_artifact_invalidations.sql +51 -0
  39. package/drizzle/0140_sandbox_restore_and_reaper_fences.sql +276 -0
  40. package/drizzle/0142_sandbox_archive_capture_gate.sql +80 -0
  41. package/drizzle/0143_session_codex_compaction_mode.sql +20 -0
  42. package/drizzle/0144_sandbox_viewer_force_drain_gate.sql +95 -0
  43. package/drizzle/0145_model_call_facts.sql +96 -0
  44. package/drizzle/0146_slack_bot_delete_idempotency.sql +105 -0
  45. package/drizzle/0147_draft_latency_mode.sql +12 -0
  46. package/drizzle/0148_session_turn_latency_mode.sql +12 -0
  47. package/package.json +6 -6
  48. package/src/index.ts +4995 -421
  49. package/src/insights.ts +837 -0
  50. package/src/migrate.ts +9 -1
  51. package/src/new-session-drafts.ts +4 -0
  52. package/src/preference-registry-schema.ts +170 -0
  53. package/src/preference-registry.ts +1220 -0
  54. package/src/provision-roles.ts +95 -22
  55. package/src/role-relationships.ts +132 -0
  56. package/src/runtime-posture.ts +28 -26
  57. package/src/schema.ts +478 -5
  58. package/src/session-queue-commands.ts +39 -6
  59. package/dist/chunk-DW24CKCT.js +0 -4046
  60. package/dist/chunk-DW24CKCT.js.map +0 -1
  61. package/dist/chunk-ELMUCYZF.js.map +0 -1
  62. package/dist/chunk-Y5WZZVQK.js.map +0 -1
  63. package/dist/schema-DhkRhcuQ.d.ts +0 -22666
@@ -5,6 +5,11 @@ import {
5
5
  RUNTIME_READ_INSERT_TABLES,
6
6
  RUNTIME_READ_ONLY_TABLES,
7
7
  } from "./runtime-posture";
8
+ import {
9
+ classifyRoleRelationships,
10
+ roleRelationshipsCatalogQuery,
11
+ type RoleRelationshipCatalogRow,
12
+ } from "./role-relationships";
8
13
 
9
14
  export type ProvisionResult = {
10
15
  appRole: string | null;
@@ -106,10 +111,12 @@ export async function provisionRoles(
106
111
  "OPENGENI_APP_DATABASE_PASSWORD (or appPassword) is required for rlsStrategy 'force'",
107
112
  );
108
113
  }
109
- // Ownership and role-graph edges cannot be safely guessed away. Refuse to
110
- // mutate an existing role until an operator has explicitly transferred
111
- // objects/removed memberships; role attributes and direct grants, however,
112
- // are deterministic and are converged below on every run.
114
+ // Ownership and privilege-bearing role-graph edges cannot be safely
115
+ // guessed away. Refuse to mutate an existing role until an operator has
116
+ // explicitly transferred objects/removed those memberships; role
117
+ // attributes and direct grants, however, are deterministic and are
118
+ // converged below on every run. PostgreSQL 16+'s exact non-runtime-bearing
119
+ // creator-management edge is classified separately.
113
120
  await assertAppRoleSafeToNormalize(sql, appRole);
114
121
  await ensureRestrictedAppLoginRole(sql, appRole, appPassword);
115
122
  provisionedAppRole = appRole;
@@ -190,12 +197,23 @@ BEGIN
190
197
  ${literal(role)},
191
198
  ${literal(password)}
192
199
  );
193
- ELSE
200
+ ELSIF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = current_user AND rolsuper) THEN
194
201
  EXECUTE format(
195
202
  'ALTER ROLE %I WITH LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEROLE NOCREATEDB NOREPLICATION NOINHERIT PASSWORD %L',
196
203
  ${literal(role)},
197
204
  ${literal(password)}
198
205
  );
206
+ ELSE
207
+ -- PostgreSQL requires elevated attributes merely to name SUPERUSER,
208
+ -- BYPASSRLS, REPLICATION, or CREATEDB in ALTER ROLE, even when setting them
209
+ -- false. The preflight has already proved those protected attributes are
210
+ -- false, so a managed CREATEROLE administrator converges only the
211
+ -- attributes it may change.
212
+ EXECUTE format(
213
+ 'ALTER ROLE %I WITH LOGIN NOCREATEROLE NOINHERIT PASSWORD %L',
214
+ ${literal(role)},
215
+ ${literal(password)}
216
+ );
199
217
  END IF;
200
218
  END $$;
201
219
  `);
@@ -214,25 +232,47 @@ async function assertAppRoleSafeToNormalize(sql: postgres.Sql, role: string): Pr
214
232
  return;
215
233
  }
216
234
 
217
- const memberships = await sql<{ relationship: string }[]>`
218
- select ('inherits:' || parent.rolname)::text as relationship
219
- from pg_auth_members membership
220
- join pg_roles member on member.oid = membership.member
221
- join pg_roles parent on parent.oid = membership.roleid
222
- where member.rolname = ${role}
223
- union all
224
- select ('member:' || member.rolname)::text as relationship
225
- from pg_auth_members membership
226
- join pg_roles member on member.oid = membership.member
227
- join pg_roles parent on parent.oid = membership.roleid
228
- where parent.rolname = ${role}
229
- order by relationship
235
+ const [roleAttributes] = await sql<
236
+ {
237
+ administrator_superuser: boolean;
238
+ app_superuser: boolean;
239
+ app_bypass_rls: boolean;
240
+ app_create_database: boolean;
241
+ app_replication: boolean;
242
+ }[]
243
+ >`
244
+ select
245
+ administrator.rolsuper as administrator_superuser,
246
+ app.rolsuper as app_superuser,
247
+ app.rolbypassrls as app_bypass_rls,
248
+ app.rolcreatedb as app_create_database,
249
+ app.rolreplication as app_replication
250
+ from pg_roles app
251
+ join pg_roles administrator on administrator.rolname = current_user
252
+ where app.rolname = ${role}
230
253
  `;
231
- if (memberships.length > 0) {
254
+ if (roleAttributes && !roleAttributes.administrator_superuser) {
255
+ const protectedAttributes = [
256
+ roleAttributes.app_superuser ? "SUPERUSER" : null,
257
+ roleAttributes.app_bypass_rls ? "BYPASSRLS" : null,
258
+ roleAttributes.app_create_database ? "CREATEDB" : null,
259
+ roleAttributes.app_replication ? "REPLICATION" : null,
260
+ ].filter((attribute): attribute is string => attribute !== null);
261
+ if (protectedAttributes.length > 0) {
262
+ throw new Error(
263
+ `Refusing to normalize app role ${role}: a non-superuser administrator cannot clear protected attributes (${protectedAttributes.join(", ")})`,
264
+ );
265
+ }
266
+ }
267
+
268
+ const relationshipRows = await sql.unsafe<RoleRelationshipCatalogRow[]>(
269
+ roleRelationshipsCatalogQuery("$1"),
270
+ [role],
271
+ );
272
+ const { unsafeRelationships } = classifyRoleRelationships(relationshipRows);
273
+ if (unsafeRelationships.length > 0) {
232
274
  throw new Error(
233
- `Refusing to normalize app role ${role}: remove role relationships first (${memberships
234
- .map((row) => row.relationship)
235
- .join(", ")})`,
275
+ `Refusing to normalize app role ${role}: remove privilege-bearing role relationships first (${unsafeRelationships.join(", ")})`,
236
276
  );
237
277
  }
238
278
 
@@ -372,6 +412,39 @@ BEGIN
372
412
  ${literal(role)}
373
413
  );
374
414
  END IF;
415
+ IF to_regprocedure(
416
+ format(
417
+ '%I.preference_registry_apply_lifecycle(text,uuid,integer,uuid,uuid,text,uuid,text,text)',
418
+ ${literal(schema)}
419
+ )
420
+ ) IS NOT NULL THEN
421
+ EXECUTE format(
422
+ 'GRANT EXECUTE ON FUNCTION %I.preference_registry_apply_lifecycle(text, uuid, integer, uuid, uuid, text, uuid, text, text) TO %I',
423
+ ${literal(schema)},
424
+ ${literal(role)}
425
+ );
426
+ END IF;
427
+ IF to_regprocedure(
428
+ format('%I.preference_registry_lock_heads(uuid[])', ${literal(schema)})
429
+ ) IS NOT NULL THEN
430
+ EXECUTE format(
431
+ 'GRANT EXECUTE ON FUNCTION %I.preference_registry_lock_heads(uuid[]) TO %I',
432
+ ${literal(schema)},
433
+ ${literal(role)}
434
+ );
435
+ END IF;
436
+ IF to_regprocedure(
437
+ format(
438
+ '%I.preference_registry_get_or_create_snapshot(uuid,uuid,uuid,uuid,uuid,integer)',
439
+ ${literal(schema)}
440
+ )
441
+ ) IS NOT NULL THEN
442
+ EXECUTE format(
443
+ 'GRANT EXECUTE ON FUNCTION %I.preference_registry_get_or_create_snapshot(uuid, uuid, uuid, uuid, uuid, integer) TO %I',
444
+ ${literal(schema)},
445
+ ${literal(role)}
446
+ );
447
+ END IF;
375
448
  EXECUTE format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});
376
449
  EXECUTE format(
377
450
  'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I REVOKE ALL PRIVILEGES ON TABLES FROM %I',
@@ -0,0 +1,132 @@
1
+ export type RoleRelationshipDirection = "inherits" | "member";
2
+
3
+ export type RoleRelationshipCatalogRow = {
4
+ relationship: string;
5
+ direction: RoleRelationshipDirection;
6
+ server_version_num: number;
7
+ admin_option: boolean | null;
8
+ inherit_option: boolean | null;
9
+ set_option: boolean | null;
10
+ member_is_superuser: boolean | null;
11
+ member_can_create_role: boolean | null;
12
+ grantor_role: string | null;
13
+ grantor_is_superuser: boolean | null;
14
+ };
15
+
16
+ export type ClassifiedRoleRelationships = {
17
+ relationships: string[];
18
+ managementOnlyRelationships: string[];
19
+ unsafeRelationships: string[];
20
+ };
21
+
22
+ type RoleTargetExpression = "current_user" | "$1";
23
+
24
+ /**
25
+ * One PostgreSQL 15-compatible catalog query for both provisioning and runtime
26
+ * posture. PostgreSQL 16 added per-membership INHERIT and SET columns; reading
27
+ * the catalog row through to_jsonb keeps this statement parseable on 15 while
28
+ * leaving the missing options null and therefore fail-closed.
29
+ */
30
+ export function roleRelationshipsCatalogQuery(targetRole: RoleTargetExpression): string {
31
+ return `
32
+ with recursive target_role(oid, rolname) as (
33
+ select oid, rolname
34
+ from pg_roles
35
+ where rolname = ${targetRole}
36
+ ), inherited_roles(oid, rolname) as (
37
+ select parent.oid, parent.rolname
38
+ from pg_auth_members membership
39
+ join target_role target on target.oid = membership.member
40
+ join pg_roles parent on parent.oid = membership.roleid
41
+ union
42
+ select parent.oid, parent.rolname
43
+ from inherited_roles inherited
44
+ join pg_auth_members membership on membership.member = inherited.oid
45
+ join pg_roles parent on parent.oid = membership.roleid
46
+ ), relationships as (
47
+ select
48
+ ('inherits:' || inherited.rolname)::text as relationship,
49
+ 'inherits'::text as direction,
50
+ null::boolean as admin_option,
51
+ null::boolean as inherit_option,
52
+ null::boolean as set_option,
53
+ null::boolean as member_is_superuser,
54
+ null::boolean as member_can_create_role,
55
+ null::text as grantor_role,
56
+ null::boolean as grantor_is_superuser
57
+ from inherited_roles inherited
58
+ union all
59
+ select
60
+ ('member:' || member.rolname)::text as relationship,
61
+ 'member'::text as direction,
62
+ membership.admin_option,
63
+ case
64
+ when current_setting('server_version_num')::integer >= 160000
65
+ then (to_jsonb(membership) ->> 'inherit_option')::boolean
66
+ else null::boolean
67
+ end as inherit_option,
68
+ case
69
+ when current_setting('server_version_num')::integer >= 160000
70
+ then (to_jsonb(membership) ->> 'set_option')::boolean
71
+ else null::boolean
72
+ end as set_option,
73
+ member.rolsuper as member_is_superuser,
74
+ member.rolcreaterole as member_can_create_role,
75
+ grantor.rolname::text as grantor_role,
76
+ grantor.rolsuper as grantor_is_superuser
77
+ from pg_auth_members membership
78
+ join target_role target on target.oid = membership.roleid
79
+ join pg_roles member on member.oid = membership.member
80
+ left join pg_roles grantor on grantor.oid = membership.grantor
81
+ )
82
+ select
83
+ relationship,
84
+ direction,
85
+ current_setting('server_version_num')::integer as server_version_num,
86
+ admin_option,
87
+ inherit_option,
88
+ set_option,
89
+ member_is_superuser,
90
+ member_can_create_role,
91
+ grantor_role,
92
+ grantor_is_superuser
93
+ from relationships
94
+ order by relationship, direction, grantor_role nulls first
95
+ `;
96
+ }
97
+
98
+ /**
99
+ * PostgreSQL 16+ automatically gives a non-superuser CREATEROLE principal an
100
+ * ADMIN-only grant on a role it creates. The exact edge below permits role
101
+ * management but cannot inherit the app role's privileges or SET ROLE into it.
102
+ */
103
+ export function isManagementOnlyRoleRelationship(row: RoleRelationshipCatalogRow): boolean {
104
+ return (
105
+ row.direction === "member" &&
106
+ row.server_version_num >= 160000 &&
107
+ row.admin_option === true &&
108
+ row.inherit_option === false &&
109
+ row.set_option === false &&
110
+ row.member_is_superuser === false &&
111
+ row.member_can_create_role === true &&
112
+ row.grantor_is_superuser === true
113
+ );
114
+ }
115
+
116
+ function sortedUnique(values: Iterable<string>): string[] {
117
+ return [...new Set(values)].sort((a, b) => a.localeCompare(b));
118
+ }
119
+
120
+ /** Classify exact management-only reverse grants while rejecting every other edge. */
121
+ export function classifyRoleRelationships(
122
+ rows: readonly RoleRelationshipCatalogRow[],
123
+ ): ClassifiedRoleRelationships {
124
+ const managementOnlyRows = rows.filter(isManagementOnlyRoleRelationship);
125
+ const unsafeRows = rows.filter((row) => !isManagementOnlyRoleRelationship(row));
126
+
127
+ return {
128
+ relationships: sortedUnique(rows.map((row) => row.relationship)),
129
+ managementOnlyRelationships: sortedUnique(managementOnlyRows.map((row) => row.relationship)),
130
+ unsafeRelationships: sortedUnique(unsafeRows.map((row) => row.relationship)),
131
+ };
132
+ }
@@ -1,5 +1,10 @@
1
1
  import { sql } from "drizzle-orm";
2
2
  import type { Database, RlsStrategy } from "./index";
3
+ import {
4
+ classifyRoleRelationships,
5
+ roleRelationshipsCatalogQuery,
6
+ type RoleRelationshipCatalogRow,
7
+ } from "./role-relationships";
3
8
 
4
9
  /**
5
10
  * The complete standalone tenant-table contract. Adding or removing a
@@ -40,11 +45,17 @@ export const FORCE_RLS_TABLES = [
40
45
  "knowledge_memories",
41
46
  "machine_metrics_latest",
42
47
  "machine_metrics_series",
48
+ "model_call_facts",
43
49
  "new_session_drafts",
44
50
  "pack_installations",
51
+ "preference_registry_events",
52
+ "preference_registry_preferences",
53
+ "preference_registry_revisions",
54
+ "preference_registry_snapshots",
45
55
  "rig_changes",
46
56
  "rig_versions",
47
57
  "rigs",
58
+ "sandbox_checkpoint_artifacts",
48
59
  "sandbox_lease_holders",
49
60
  "sandbox_leases",
50
61
  "sandbox_pty_sessions",
@@ -73,6 +84,7 @@ export const FORCE_RLS_TABLES = [
73
84
  "session_turns",
74
85
  "session_workflow_wake_outbox",
75
86
  "sessions",
87
+ "slack_bot_delete_operations",
76
88
  "slack_bot_post_operations",
77
89
  "social_connections",
78
90
  "social_posts",
@@ -150,11 +162,13 @@ export const RUNTIME_FULL_DML_TABLES = [
150
162
  "machine_metrics_latest",
151
163
  "machine_metrics_series",
152
164
  "managed_accounts",
165
+ "model_call_facts",
153
166
  "new_session_drafts",
154
167
  "pack_installations",
155
168
  "rig_changes",
156
169
  "rig_versions",
157
170
  "rigs",
171
+ "sandbox_checkpoint_artifacts",
158
172
  "sandbox_lease_holders",
159
173
  "sandbox_leases",
160
174
  "sandbox_pty_sessions",
@@ -182,6 +196,7 @@ export const RUNTIME_FULL_DML_TABLES = [
182
196
  "session_turns",
183
197
  "session_workflow_wake_outbox",
184
198
  "sessions",
199
+ "slack_bot_delete_operations",
185
200
  "slack_bot_post_operations",
186
201
  "social_connections",
187
202
  "social_posts",
@@ -200,11 +215,17 @@ export const RUNTIME_FULL_DML_TABLES = [
200
215
  "workspaces",
201
216
  ] as const;
202
217
 
203
- /** Configuration is deployment-global and intentionally read-only at runtime. */
204
- export const RUNTIME_READ_ONLY_TABLES = ["nested_agent_depth_configuration"] as const;
218
+ /** Configuration and lifecycle-owned audit rows are read-only at runtime. */
219
+ export const RUNTIME_READ_ONLY_TABLES = [
220
+ "nested_agent_depth_configuration",
221
+ "preference_registry_events",
222
+ "preference_registry_snapshots",
223
+ ] as const;
205
224
 
206
225
  /** Append-only evidence/revision tables are insertable and queryable, never mutable. */
207
226
  export const RUNTIME_READ_INSERT_TABLES = [
227
+ "preference_registry_preferences",
228
+ "preference_registry_revisions",
208
229
  "session_spawn_denials",
209
230
  "workspace_instruction_policy_activation_events",
210
231
  "workspace_instruction_policy_revisions",
@@ -299,6 +320,7 @@ export type RuntimeRoutinePosture = {
299
320
 
300
321
  export type RuntimeDatabasePosture = {
301
322
  identity: RuntimeDatabaseIdentity;
323
+ /** Privilege-bearing role relationships; exact PG16+ management-only grants are excluded. */
302
324
  memberships: string[];
303
325
  schemas: RuntimeSchemaPosture[];
304
326
  ownedSchemas: string[];
@@ -418,30 +440,10 @@ export async function inspectRuntimeDatabasePosture(
418
440
  };
419
441
  }
420
442
 
421
- const memberships = resultRows<{ relationship: string }>(
422
- await tx.execute(sql`
423
- with recursive inherited_roles(oid, rolname) as (
424
- select parent.oid, parent.rolname
425
- from pg_auth_members membership
426
- join pg_roles member on member.oid = membership.member
427
- join pg_roles parent on parent.oid = membership.roleid
428
- where member.rolname = current_user
429
- union
430
- select parent.oid, parent.rolname
431
- from inherited_roles inherited
432
- join pg_auth_members membership on membership.member = inherited.oid
433
- join pg_roles parent on parent.oid = membership.roleid
434
- )
435
- select ('inherits:' || rolname)::text as relationship from inherited_roles
436
- union
437
- select ('member:' || member.rolname)::text as relationship
438
- from pg_auth_members membership
439
- join pg_roles parent on parent.oid = membership.roleid
440
- join pg_roles member on member.oid = membership.member
441
- where parent.rolname = current_user
442
- order by relationship
443
- `),
444
- ).map((row) => row.relationship);
443
+ const relationshipRows = resultRows<RoleRelationshipCatalogRow>(
444
+ await tx.execute(sql.raw(roleRelationshipsCatalogQuery("current_user"))),
445
+ );
446
+ const memberships = classifyRoleRelationships(relationshipRows).unsafeRelationships;
445
447
 
446
448
  const schemas = resultRows<{
447
449
  name: string;