@opengeni/db 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/chunk-57MLICFR.js +121 -0
  2. package/dist/chunk-57MLICFR.js.map +1 -0
  3. package/dist/chunk-OGCE6O2X.js +52 -0
  4. package/dist/chunk-OGCE6O2X.js.map +1 -0
  5. package/dist/chunk-PSX56ZTL.js +1093 -0
  6. package/dist/chunk-PSX56ZTL.js.map +1 -0
  7. package/dist/chunk-PZ5AY32C.js +10 -0
  8. package/dist/chunk-PZ5AY32C.js.map +1 -0
  9. package/dist/index.d.ts +8 -0
  10. package/dist/index.js +5165 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/migrate.d.ts +40 -0
  13. package/dist/migrate.js +10 -0
  14. package/dist/migrate.js.map +1 -0
  15. package/dist/provision-roles.d.ts +2063 -0
  16. package/dist/provision-roles.js +8 -0
  17. package/dist/provision-roles.js.map +1 -0
  18. package/dist/schema-CaeZQAJQ.d.ts +9705 -0
  19. package/dist/schema.d.ts +3 -0
  20. package/dist/schema.js +110 -0
  21. package/dist/schema.js.map +1 -0
  22. package/drizzle/0000_initial.sql +179 -0
  23. package/drizzle/0001_workspace_auth_billing.sql +590 -0
  24. package/drizzle/0002_packs_and_social.sql +99 -0
  25. package/drizzle/0003_capability_catalog.sql +73 -0
  26. package/drizzle/0004_workspace_environments.sql +65 -0
  27. package/drizzle/0005_session_goals.sql +45 -0
  28. package/drizzle/0006_workspace_packs.sql +31 -0
  29. package/drizzle/0007_session_history_items.sql +66 -0
  30. package/drizzle/0008_session_first_party_mcp_permissions.sql +5 -0
  31. package/drizzle/0009_goal_sessions_first_party_goals_manage.sql +34 -0
  32. package/drizzle/0010_session_parent_linkage.sql +30 -0
  33. package/drizzle/0011_context_compaction.sql +33 -0
  34. package/drizzle/0012_compaction_summary_fractional_position.sql +19 -0
  35. package/drizzle/0013_session_compact_requested.sql +16 -0
  36. package/drizzle/0014_repair_orphaned_function_call_results.sql +125 -0
  37. package/drizzle/0015_workspace_agent_instructions.sql +17 -0
  38. package/drizzle/0016_session_create_idempotency.sql +27 -0
  39. package/drizzle/0017_sandbox_leases.sql +313 -0
  40. package/drizzle/0018_sandbox_os.sql +89 -0
  41. package/drizzle/0019_session_stream_acknowledgments.sql +94 -0
  42. package/drizzle/0020_session_recordings.sql +88 -0
  43. package/drizzle/0021_sandbox_pty_sessions.sql +70 -0
  44. package/drizzle/0022_sandbox_lease_terminal_url.sql +32 -0
  45. package/drizzle/0023_session_title.sql +19 -0
  46. package/drizzle/0024_codex_subscription_credentials.sql +51 -0
  47. package/drizzle/0024_sandboxes_enrollments_metrics.sql +262 -0
  48. package/drizzle/0025_device_enrollment_requests.sql +142 -0
  49. package/drizzle/0026_device_enrollment_user_code_resolver.sql +47 -0
  50. package/drizzle/0027_session_working_dir.sql +24 -0
  51. package/drizzle/0028_codex_multi_account.sql +85 -0
  52. package/drizzle/0029_session_history_item_producer.sql +31 -0
  53. package/drizzle/0030_agent_run_state_frozen_codex.sql +35 -0
  54. package/drizzle/0031_codex_usage_cache.sql +21 -0
  55. package/drizzle/0032_codex_account_cooldown.sql +18 -0
  56. package/drizzle/0033_codex_connector_cache.sql +20 -0
  57. package/drizzle/0034_sandbox_lease_image.sql +21 -0
  58. package/drizzle/meta/_journal.json +167 -0
  59. package/package.json +66 -0
  60. package/src/codex-token-resolver.ts +247 -0
  61. package/src/environment-crypto.ts +51 -0
  62. package/src/event-payload-sanitizer.ts +89 -0
  63. package/src/index.ts +7776 -0
  64. package/src/migrate.ts +95 -0
  65. package/src/provision-roles.ts +198 -0
  66. package/src/schema.ts +1110 -0
@@ -0,0 +1,590 @@
1
+ -- Workspace auth + managed billing control plane.
2
+ -- Upgrades pre-workspace databases in place: new auth/account/billing tables,
3
+ -- account_id/workspace_id scoping on all existing tables (backfilled into a
4
+ -- default account/workspace when rows exist), workspace-scoped indexes, and RLS.
5
+ -- Every statement is idempotent so the file is safe to re-run.
6
+
7
+ CREATE TABLE IF NOT EXISTS "auth_users" (
8
+ "id" text PRIMARY KEY NOT NULL,
9
+ "name" text NOT NULL,
10
+ "email" text NOT NULL,
11
+ "email_verified" boolean NOT NULL DEFAULT false,
12
+ "image" text,
13
+ "created_at" timestamptz NOT NULL DEFAULT now(),
14
+ "updated_at" timestamptz NOT NULL DEFAULT now()
15
+ );
16
+ CREATE UNIQUE INDEX IF NOT EXISTS "auth_users_email_idx" ON "auth_users" (lower("email"));
17
+
18
+ CREATE TABLE IF NOT EXISTS "auth_sessions" (
19
+ "id" text PRIMARY KEY NOT NULL,
20
+ "user_id" text NOT NULL REFERENCES "auth_users"("id") ON DELETE CASCADE,
21
+ "token" text NOT NULL,
22
+ "expires_at" timestamptz NOT NULL,
23
+ "ip_address" text,
24
+ "user_agent" text,
25
+ "created_at" timestamptz NOT NULL DEFAULT now(),
26
+ "updated_at" timestamptz NOT NULL DEFAULT now()
27
+ );
28
+ CREATE UNIQUE INDEX IF NOT EXISTS "auth_sessions_token_idx" ON "auth_sessions" ("token");
29
+ CREATE INDEX IF NOT EXISTS "auth_sessions_user_id_idx" ON "auth_sessions" ("user_id");
30
+
31
+ CREATE TABLE IF NOT EXISTS "auth_identities" (
32
+ "id" text PRIMARY KEY NOT NULL,
33
+ "user_id" text NOT NULL REFERENCES "auth_users"("id") ON DELETE CASCADE,
34
+ "account_id" text NOT NULL,
35
+ "provider_id" text NOT NULL,
36
+ "access_token" text,
37
+ "refresh_token" text,
38
+ "id_token" text,
39
+ "access_token_expires_at" timestamptz,
40
+ "refresh_token_expires_at" timestamptz,
41
+ "scope" text,
42
+ "password" text,
43
+ "created_at" timestamptz NOT NULL DEFAULT now(),
44
+ "updated_at" timestamptz NOT NULL DEFAULT now()
45
+ );
46
+ CREATE INDEX IF NOT EXISTS "auth_identities_user_id_idx" ON "auth_identities" ("user_id");
47
+ CREATE UNIQUE INDEX IF NOT EXISTS "auth_identities_provider_account_idx" ON "auth_identities" ("provider_id", "account_id");
48
+
49
+ CREATE TABLE IF NOT EXISTS "auth_verifications" (
50
+ "id" text PRIMARY KEY NOT NULL,
51
+ "identifier" text NOT NULL,
52
+ "value" text NOT NULL,
53
+ "expires_at" timestamptz NOT NULL,
54
+ "created_at" timestamptz NOT NULL DEFAULT now(),
55
+ "updated_at" timestamptz NOT NULL DEFAULT now()
56
+ );
57
+ CREATE INDEX IF NOT EXISTS "auth_verifications_identifier_idx" ON "auth_verifications" ("identifier");
58
+
59
+ CREATE TABLE IF NOT EXISTS "auth_rate_limits" (
60
+ "id" text PRIMARY KEY NOT NULL,
61
+ "key" text NOT NULL,
62
+ "count" integer NOT NULL,
63
+ "last_request" bigint NOT NULL
64
+ );
65
+ CREATE UNIQUE INDEX IF NOT EXISTS "auth_rate_limits_key_idx" ON "auth_rate_limits" ("key");
66
+
67
+ CREATE TABLE IF NOT EXISTS "managed_accounts" (
68
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
69
+ "name" text NOT NULL,
70
+ "external_source" text,
71
+ "external_id" text,
72
+ "created_at" timestamptz NOT NULL DEFAULT now(),
73
+ "updated_at" timestamptz NOT NULL DEFAULT now()
74
+ );
75
+ CREATE UNIQUE INDEX IF NOT EXISTS "managed_accounts_external_idx" ON "managed_accounts" ("external_source", "external_id");
76
+
77
+ CREATE TABLE IF NOT EXISTS "workspaces" (
78
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
79
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
80
+ "name" text NOT NULL,
81
+ "slug" text,
82
+ "external_source" text,
83
+ "external_id" text,
84
+ "created_at" timestamptz NOT NULL DEFAULT now(),
85
+ "updated_at" timestamptz NOT NULL DEFAULT now()
86
+ );
87
+ CREATE INDEX IF NOT EXISTS "workspaces_account_idx" ON "workspaces" ("account_id");
88
+ CREATE UNIQUE INDEX IF NOT EXISTS "workspaces_account_slug_idx" ON "workspaces" ("account_id", "slug") WHERE "slug" IS NOT NULL;
89
+ CREATE UNIQUE INDEX IF NOT EXISTS "workspaces_external_idx" ON "workspaces" ("external_source", "external_id");
90
+
91
+ CREATE TABLE IF NOT EXISTS "workspace_memberships" (
92
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
93
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
94
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
95
+ "subject_id" text NOT NULL,
96
+ "subject_label" text,
97
+ "role" text NOT NULL DEFAULT 'member',
98
+ "permissions" jsonb NOT NULL DEFAULT '[]'::jsonb,
99
+ "created_at" timestamptz NOT NULL DEFAULT now(),
100
+ "updated_at" timestamptz NOT NULL DEFAULT now()
101
+ );
102
+ CREATE UNIQUE INDEX IF NOT EXISTS "workspace_memberships_subject_workspace_idx" ON "workspace_memberships" ("subject_id", "workspace_id");
103
+ CREATE INDEX IF NOT EXISTS "workspace_memberships_subject_idx" ON "workspace_memberships" ("subject_id");
104
+ CREATE INDEX IF NOT EXISTS "workspace_memberships_account_idx" ON "workspace_memberships" ("account_id");
105
+
106
+ CREATE TABLE IF NOT EXISTS "api_keys" (
107
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
108
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
109
+ "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE,
110
+ "name" text NOT NULL,
111
+ "prefix" text NOT NULL,
112
+ "key_hash" text NOT NULL,
113
+ "permissions" jsonb NOT NULL DEFAULT '[]'::jsonb,
114
+ "expires_at" timestamptz,
115
+ "revoked_at" timestamptz,
116
+ "last_used_at" timestamptz,
117
+ "created_at" timestamptz NOT NULL DEFAULT now(),
118
+ "updated_at" timestamptz NOT NULL DEFAULT now()
119
+ );
120
+ CREATE INDEX IF NOT EXISTS "api_keys_prefix_idx" ON "api_keys" ("prefix");
121
+ CREATE UNIQUE INDEX IF NOT EXISTS "api_keys_key_hash_idx" ON "api_keys" ("key_hash");
122
+ CREATE INDEX IF NOT EXISTS "api_keys_account_idx" ON "api_keys" ("account_id");
123
+ CREATE INDEX IF NOT EXISTS "api_keys_workspace_idx" ON "api_keys" ("workspace_id");
124
+
125
+ CREATE TABLE IF NOT EXISTS "github_installations" (
126
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
127
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
128
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
129
+ "installation_id" integer NOT NULL,
130
+ "account_login" text,
131
+ "account_type" text,
132
+ "created_at" timestamptz NOT NULL DEFAULT now(),
133
+ "updated_at" timestamptz NOT NULL DEFAULT now()
134
+ );
135
+ CREATE UNIQUE INDEX IF NOT EXISTS "github_installations_workspace_installation_idx" ON "github_installations" ("workspace_id", "installation_id");
136
+ CREATE INDEX IF NOT EXISTS "github_installations_installation_idx" ON "github_installations" ("installation_id");
137
+ CREATE INDEX IF NOT EXISTS "github_installations_workspace_idx" ON "github_installations" ("workspace_id");
138
+
139
+ CREATE TABLE IF NOT EXISTS "usage_events" (
140
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
141
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
142
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
143
+ "subject_id" text,
144
+ "event_type" text NOT NULL,
145
+ "quantity" bigint NOT NULL,
146
+ "unit" text NOT NULL,
147
+ "source_resource_type" text,
148
+ "source_resource_id" text,
149
+ "idempotency_key" text NOT NULL,
150
+ "occurred_at" timestamptz NOT NULL,
151
+ "recorded_at" timestamptz NOT NULL DEFAULT now(),
152
+ "exported_to_billing_at" timestamptz,
153
+ "billing_provider_event_id" text
154
+ );
155
+ CREATE UNIQUE INDEX IF NOT EXISTS "usage_events_idempotency_idx" ON "usage_events" ("idempotency_key");
156
+ CREATE INDEX IF NOT EXISTS "usage_events_workspace_metric_idx" ON "usage_events" ("workspace_id", "event_type", "occurred_at");
157
+ CREATE INDEX IF NOT EXISTS "usage_events_account_metric_idx" ON "usage_events" ("account_id", "event_type", "occurred_at");
158
+
159
+ CREATE TABLE IF NOT EXISTS "credit_ledger_entries" (
160
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
161
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
162
+ "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE SET NULL,
163
+ "type" text NOT NULL,
164
+ "amount_micros" bigint NOT NULL,
165
+ "currency" text NOT NULL DEFAULT 'usd',
166
+ "source_type" text,
167
+ "source_id" text,
168
+ "idempotency_key" text NOT NULL,
169
+ "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
170
+ "occurred_at" timestamptz NOT NULL DEFAULT now(),
171
+ "created_at" timestamptz NOT NULL DEFAULT now()
172
+ );
173
+ CREATE UNIQUE INDEX IF NOT EXISTS "credit_ledger_entries_idempotency_idx" ON "credit_ledger_entries" ("idempotency_key");
174
+ CREATE INDEX IF NOT EXISTS "credit_ledger_entries_account_created_idx" ON "credit_ledger_entries" ("account_id", "created_at");
175
+
176
+ CREATE TABLE IF NOT EXISTS "billing_customers" (
177
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
178
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
179
+ "provider" text NOT NULL DEFAULT 'stripe',
180
+ "provider_customer_id" text NOT NULL,
181
+ "email" text,
182
+ "created_at" timestamptz NOT NULL DEFAULT now(),
183
+ "updated_at" timestamptz NOT NULL DEFAULT now()
184
+ );
185
+ CREATE UNIQUE INDEX IF NOT EXISTS "billing_customers_account_provider_idx" ON "billing_customers" ("account_id", "provider");
186
+ CREATE UNIQUE INDEX IF NOT EXISTS "billing_customers_provider_customer_idx" ON "billing_customers" ("provider", "provider_customer_id");
187
+
188
+ CREATE TABLE IF NOT EXISTS "stripe_webhook_events" (
189
+ "id" text PRIMARY KEY NOT NULL,
190
+ "type" text NOT NULL,
191
+ "livemode" text NOT NULL DEFAULT 'false',
192
+ "payload" jsonb NOT NULL,
193
+ "processed_at" timestamptz,
194
+ "created_at" timestamptz NOT NULL DEFAULT now()
195
+ );
196
+
197
+ CREATE TABLE IF NOT EXISTS "audit_events" (
198
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
199
+ "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE SET NULL,
200
+ "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE SET NULL,
201
+ "subject_id" text,
202
+ "action" text NOT NULL,
203
+ "target_type" text,
204
+ "target_id" text,
205
+ "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
206
+ "occurred_at" timestamptz NOT NULL DEFAULT now()
207
+ );
208
+ CREATE INDEX IF NOT EXISTS "audit_events_account_created_idx" ON "audit_events" ("account_id", "occurred_at");
209
+ CREATE INDEX IF NOT EXISTS "audit_events_workspace_created_idx" ON "audit_events" ("workspace_id", "occurred_at");
210
+
211
+ -- Scope all pre-existing tables to an account and workspace. Columns start
212
+ -- nullable so existing rows survive; they are backfilled below and then locked
213
+ -- down with SET NOT NULL.
214
+
215
+ ALTER TABLE "sessions" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
216
+ ALTER TABLE "sessions" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
217
+ ALTER TABLE "files" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
218
+ ALTER TABLE "files" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
219
+ ALTER TABLE "file_uploads" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
220
+ ALTER TABLE "file_uploads" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
221
+ ALTER TABLE "document_bases" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
222
+ ALTER TABLE "document_bases" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
223
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
224
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
225
+ ALTER TABLE "document_chunks" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
226
+ ALTER TABLE "document_chunks" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
227
+ ALTER TABLE "session_turns" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
228
+ ALTER TABLE "session_turns" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
229
+ ALTER TABLE "session_events" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
230
+ ALTER TABLE "session_events" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
231
+ ALTER TABLE "agent_run_states" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
232
+ ALTER TABLE "agent_run_states" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
233
+ ALTER TABLE "scheduled_tasks" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
234
+ ALTER TABLE "scheduled_tasks" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
235
+ ALTER TABLE "scheduled_task_runs" ADD COLUMN IF NOT EXISTS "account_id" uuid REFERENCES "managed_accounts"("id") ON DELETE CASCADE;
236
+ ALTER TABLE "scheduled_task_runs" ADD COLUMN IF NOT EXISTS "workspace_id" uuid REFERENCES "workspaces"("id") ON DELETE CASCADE;
237
+
238
+ -- Backfill: rows created before workspaces existed are adopted by a default
239
+ -- account/workspace, created on demand and marked with a stable external id so
240
+ -- re-runs find it again.
241
+ DO $$
242
+ DECLARE
243
+ v_account_id uuid;
244
+ v_workspace_id uuid;
245
+ BEGIN
246
+ IF EXISTS (SELECT 1 FROM "sessions" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
247
+ OR EXISTS (SELECT 1 FROM "files" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
248
+ OR EXISTS (SELECT 1 FROM "file_uploads" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
249
+ OR EXISTS (SELECT 1 FROM "document_bases" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
250
+ OR EXISTS (SELECT 1 FROM "documents" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
251
+ OR EXISTS (SELECT 1 FROM "document_chunks" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
252
+ OR EXISTS (SELECT 1 FROM "session_turns" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
253
+ OR EXISTS (SELECT 1 FROM "session_events" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
254
+ OR EXISTS (SELECT 1 FROM "agent_run_states" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
255
+ OR EXISTS (SELECT 1 FROM "scheduled_tasks" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
256
+ OR EXISTS (SELECT 1 FROM "scheduled_task_runs" WHERE "workspace_id" IS NULL OR "account_id" IS NULL)
257
+ THEN
258
+ SELECT "id" INTO v_account_id
259
+ FROM "managed_accounts"
260
+ WHERE "external_source" = 'opengeni-migration' AND "external_id" = '0001-default-account';
261
+ IF v_account_id IS NULL THEN
262
+ INSERT INTO "managed_accounts" ("name", "external_source", "external_id")
263
+ VALUES ('Default Account', 'opengeni-migration', '0001-default-account')
264
+ RETURNING "id" INTO v_account_id;
265
+ END IF;
266
+
267
+ SELECT "id" INTO v_workspace_id
268
+ FROM "workspaces"
269
+ WHERE "external_source" = 'opengeni-migration' AND "external_id" = '0001-default-workspace';
270
+ IF v_workspace_id IS NULL THEN
271
+ INSERT INTO "workspaces" ("account_id", "name", "slug", "external_source", "external_id")
272
+ VALUES (v_account_id, 'Default Workspace', 'default', 'opengeni-migration', '0001-default-workspace')
273
+ RETURNING "id" INTO v_workspace_id;
274
+ END IF;
275
+
276
+ UPDATE "sessions" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
277
+ UPDATE "files" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
278
+ UPDATE "file_uploads" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
279
+ UPDATE "document_bases" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
280
+ UPDATE "documents" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
281
+ UPDATE "document_chunks" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
282
+ UPDATE "session_turns" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
283
+ UPDATE "session_events" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
284
+ UPDATE "agent_run_states" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
285
+ UPDATE "scheduled_tasks" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
286
+ UPDATE "scheduled_task_runs" SET "account_id" = v_account_id, "workspace_id" = v_workspace_id WHERE "workspace_id" IS NULL OR "account_id" IS NULL;
287
+ END IF;
288
+ END $$;
289
+
290
+ ALTER TABLE "sessions" ALTER COLUMN "account_id" SET NOT NULL;
291
+ ALTER TABLE "sessions" ALTER COLUMN "workspace_id" SET NOT NULL;
292
+ ALTER TABLE "files" ALTER COLUMN "account_id" SET NOT NULL;
293
+ ALTER TABLE "files" ALTER COLUMN "workspace_id" SET NOT NULL;
294
+ ALTER TABLE "file_uploads" ALTER COLUMN "account_id" SET NOT NULL;
295
+ ALTER TABLE "file_uploads" ALTER COLUMN "workspace_id" SET NOT NULL;
296
+ ALTER TABLE "document_bases" ALTER COLUMN "account_id" SET NOT NULL;
297
+ ALTER TABLE "document_bases" ALTER COLUMN "workspace_id" SET NOT NULL;
298
+ ALTER TABLE "documents" ALTER COLUMN "account_id" SET NOT NULL;
299
+ ALTER TABLE "documents" ALTER COLUMN "workspace_id" SET NOT NULL;
300
+ ALTER TABLE "document_chunks" ALTER COLUMN "account_id" SET NOT NULL;
301
+ ALTER TABLE "document_chunks" ALTER COLUMN "workspace_id" SET NOT NULL;
302
+ ALTER TABLE "session_turns" ALTER COLUMN "account_id" SET NOT NULL;
303
+ ALTER TABLE "session_turns" ALTER COLUMN "workspace_id" SET NOT NULL;
304
+ ALTER TABLE "session_events" ALTER COLUMN "account_id" SET NOT NULL;
305
+ ALTER TABLE "session_events" ALTER COLUMN "workspace_id" SET NOT NULL;
306
+ ALTER TABLE "agent_run_states" ALTER COLUMN "account_id" SET NOT NULL;
307
+ ALTER TABLE "agent_run_states" ALTER COLUMN "workspace_id" SET NOT NULL;
308
+ ALTER TABLE "scheduled_tasks" ALTER COLUMN "account_id" SET NOT NULL;
309
+ ALTER TABLE "scheduled_tasks" ALTER COLUMN "workspace_id" SET NOT NULL;
310
+ ALTER TABLE "scheduled_task_runs" ALTER COLUMN "account_id" SET NOT NULL;
311
+ ALTER TABLE "scheduled_task_runs" ALTER COLUMN "workspace_id" SET NOT NULL;
312
+
313
+ -- session_turns columns are always written explicitly now; drop the legacy
314
+ -- placeholder defaults so the schema matches a fresh install.
315
+ ALTER TABLE "session_turns" ALTER COLUMN "position" DROP DEFAULT;
316
+ ALTER TABLE "session_turns" ALTER COLUMN "prompt" DROP DEFAULT;
317
+ ALTER TABLE "session_turns" ALTER COLUMN "model" DROP DEFAULT;
318
+ ALTER TABLE "session_turns" ALTER COLUMN "reasoning_effort" DROP DEFAULT;
319
+ ALTER TABLE "session_turns" ALTER COLUMN "sandbox_backend" DROP DEFAULT;
320
+
321
+ -- Replace session-scoped indexes with workspace-scoped equivalents.
322
+ DROP INDEX IF EXISTS "documents_base_file_idx";
323
+ DROP INDEX IF EXISTS "documents_base_status_idx";
324
+ DROP INDEX IF EXISTS "document_chunks_document_index_idx";
325
+ DROP INDEX IF EXISTS "document_chunks_base_idx";
326
+ DROP INDEX IF EXISTS "session_events_session_sequence_idx";
327
+ DROP INDEX IF EXISTS "session_events_client_event_idx";
328
+ DROP INDEX IF EXISTS "session_events_producer_idx";
329
+ DROP INDEX IF EXISTS "session_events_session_created_idx";
330
+ DROP INDEX IF EXISTS "session_turns_queue_idx";
331
+ DROP INDEX IF EXISTS "scheduled_tasks_temporal_schedule_id_idx";
332
+ DROP INDEX IF EXISTS "scheduled_tasks_status_idx";
333
+ DROP INDEX IF EXISTS "scheduled_task_runs_task_created_idx";
334
+ DROP INDEX IF EXISTS "scheduled_task_runs_session_idx";
335
+
336
+ CREATE INDEX IF NOT EXISTS "sessions_workspace_created_idx" ON "sessions" ("workspace_id", "created_at");
337
+ CREATE INDEX IF NOT EXISTS "files_workspace_created_idx" ON "files" ("workspace_id", "created_at");
338
+ CREATE INDEX IF NOT EXISTS "file_uploads_workspace_idx" ON "file_uploads" ("workspace_id");
339
+ CREATE INDEX IF NOT EXISTS "document_bases_workspace_created_idx" ON "document_bases" ("workspace_id", "created_at");
340
+ CREATE UNIQUE INDEX IF NOT EXISTS "documents_workspace_base_file_idx" ON "documents" ("workspace_id", "base_id", "file_id");
341
+ CREATE INDEX IF NOT EXISTS "documents_workspace_base_status_idx" ON "documents" ("workspace_id", "base_id", "status");
342
+ CREATE UNIQUE INDEX IF NOT EXISTS "document_chunks_workspace_document_index_idx" ON "document_chunks" ("workspace_id", "document_id", "chunk_index");
343
+ CREATE INDEX IF NOT EXISTS "document_chunks_workspace_base_idx" ON "document_chunks" ("workspace_id", "base_id");
344
+ CREATE INDEX IF NOT EXISTS "session_turns_workspace_queue_idx" ON "session_turns" ("workspace_id", "session_id", "status", "position");
345
+ CREATE UNIQUE INDEX IF NOT EXISTS "session_events_workspace_session_sequence_idx" ON "session_events" ("workspace_id", "session_id", "sequence");
346
+ CREATE UNIQUE INDEX IF NOT EXISTS "session_events_workspace_client_event_idx" ON "session_events" ("workspace_id", "session_id", "client_event_id") WHERE "client_event_id" IS NOT NULL;
347
+ CREATE UNIQUE INDEX IF NOT EXISTS "session_events_workspace_producer_idx" ON "session_events" ("workspace_id", "session_id", "producer_id", "producer_seq") WHERE "producer_id" IS NOT NULL AND "producer_seq" IS NOT NULL;
348
+ CREATE INDEX IF NOT EXISTS "session_events_workspace_session_created_idx" ON "session_events" ("workspace_id", "session_id", "created_at");
349
+ CREATE UNIQUE INDEX IF NOT EXISTS "scheduled_tasks_workspace_temporal_schedule_id_idx" ON "scheduled_tasks" ("workspace_id", "temporal_schedule_id");
350
+ CREATE INDEX IF NOT EXISTS "scheduled_tasks_workspace_status_idx" ON "scheduled_tasks" ("workspace_id", "status");
351
+ CREATE INDEX IF NOT EXISTS "scheduled_task_runs_workspace_task_created_idx" ON "scheduled_task_runs" ("workspace_id", "task_id", "created_at");
352
+ CREATE INDEX IF NOT EXISTS "scheduled_task_runs_workspace_session_idx" ON "scheduled_task_runs" ("workspace_id", "session_id");
353
+
354
+ CREATE UNIQUE INDEX IF NOT EXISTS "workspaces_id_account_idx" ON "workspaces" ("id", "account_id");
355
+
356
+ DO $$
357
+ BEGIN
358
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_memberships_workspace_account_fk') THEN
359
+ ALTER TABLE "workspace_memberships" ADD CONSTRAINT "workspace_memberships_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
360
+ END IF;
361
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'api_keys_workspace_account_fk') THEN
362
+ ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
363
+ END IF;
364
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'sessions_workspace_account_fk') THEN
365
+ ALTER TABLE "sessions" ADD CONSTRAINT "sessions_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
366
+ END IF;
367
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'files_workspace_account_fk') THEN
368
+ ALTER TABLE "files" ADD CONSTRAINT "files_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
369
+ END IF;
370
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'file_uploads_workspace_account_fk') THEN
371
+ ALTER TABLE "file_uploads" ADD CONSTRAINT "file_uploads_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
372
+ END IF;
373
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'document_bases_workspace_account_fk') THEN
374
+ ALTER TABLE "document_bases" ADD CONSTRAINT "document_bases_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
375
+ END IF;
376
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'documents_workspace_account_fk') THEN
377
+ ALTER TABLE "documents" ADD CONSTRAINT "documents_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
378
+ END IF;
379
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'document_chunks_workspace_account_fk') THEN
380
+ ALTER TABLE "document_chunks" ADD CONSTRAINT "document_chunks_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
381
+ END IF;
382
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'session_turns_workspace_account_fk') THEN
383
+ ALTER TABLE "session_turns" ADD CONSTRAINT "session_turns_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
384
+ END IF;
385
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'session_events_workspace_account_fk') THEN
386
+ ALTER TABLE "session_events" ADD CONSTRAINT "session_events_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
387
+ END IF;
388
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'agent_run_states_workspace_account_fk') THEN
389
+ ALTER TABLE "agent_run_states" ADD CONSTRAINT "agent_run_states_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
390
+ END IF;
391
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'scheduled_tasks_workspace_account_fk') THEN
392
+ ALTER TABLE "scheduled_tasks" ADD CONSTRAINT "scheduled_tasks_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
393
+ END IF;
394
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'scheduled_task_runs_workspace_account_fk') THEN
395
+ ALTER TABLE "scheduled_task_runs" ADD CONSTRAINT "scheduled_task_runs_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
396
+ END IF;
397
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'github_installations_workspace_account_fk') THEN
398
+ ALTER TABLE "github_installations" ADD CONSTRAINT "github_installations_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
399
+ END IF;
400
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'usage_events_workspace_account_fk') THEN
401
+ ALTER TABLE "usage_events" ADD CONSTRAINT "usage_events_workspace_account_fk" FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
402
+ END IF;
403
+ END $$;
404
+
405
+ CREATE SCHEMA IF NOT EXISTS opengeni_private;
406
+
407
+ CREATE OR REPLACE FUNCTION opengeni_private.current_workspace_id()
408
+ RETURNS uuid
409
+ LANGUAGE sql
410
+ STABLE
411
+ AS $$
412
+ SELECT nullif(current_setting('opengeni.workspace_id', true), '')::uuid;
413
+ $$;
414
+
415
+ CREATE OR REPLACE FUNCTION opengeni_private.current_account_id()
416
+ RETURNS uuid
417
+ LANGUAGE sql
418
+ STABLE
419
+ AS $$
420
+ SELECT nullif(current_setting('opengeni.account_id', true), '')::uuid;
421
+ $$;
422
+
423
+ CREATE OR REPLACE FUNCTION opengeni_private.workspace_rls_visible(account_id uuid, workspace_id uuid)
424
+ RETURNS boolean
425
+ LANGUAGE sql
426
+ STABLE
427
+ AS $$
428
+ SELECT account_id = opengeni_private.current_account_id()
429
+ AND workspace_id = opengeni_private.current_workspace_id();
430
+ $$;
431
+
432
+ CREATE OR REPLACE FUNCTION opengeni_private.account_rls_visible(account_id uuid)
433
+ RETURNS boolean
434
+ LANGUAGE sql
435
+ STABLE
436
+ AS $$
437
+ SELECT account_id = opengeni_private.current_account_id();
438
+ $$;
439
+
440
+ CREATE OR REPLACE FUNCTION opengeni_private.optional_workspace_rls_visible(account_id uuid, workspace_id uuid)
441
+ RETURNS boolean
442
+ LANGUAGE sql
443
+ STABLE
444
+ AS $$
445
+ SELECT account_id = opengeni_private.current_account_id()
446
+ AND (
447
+ opengeni_private.current_workspace_id() IS NULL
448
+ OR workspace_id IS NULL
449
+ OR workspace_id = opengeni_private.current_workspace_id()
450
+ );
451
+ $$;
452
+
453
+ CREATE OR REPLACE FUNCTION opengeni_private.current_api_key_hash()
454
+ RETURNS text
455
+ LANGUAGE sql
456
+ STABLE
457
+ AS $$
458
+ SELECT nullif(current_setting('opengeni.api_key_hash', true), '');
459
+ $$;
460
+
461
+ DO $$
462
+ DECLARE
463
+ table_name text;
464
+ BEGIN
465
+ FOREACH table_name IN ARRAY ARRAY[
466
+ 'sessions',
467
+ 'session_events',
468
+ 'session_turns',
469
+ 'agent_run_states',
470
+ 'files',
471
+ 'file_uploads',
472
+ 'document_bases',
473
+ 'documents',
474
+ 'document_chunks',
475
+ 'scheduled_tasks',
476
+ 'scheduled_task_runs',
477
+ 'github_installations'
478
+ ]
479
+ LOOP
480
+ EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', table_name);
481
+ EXECUTE format('ALTER TABLE %I FORCE ROW LEVEL SECURITY', table_name);
482
+ IF EXISTS (
483
+ SELECT 1 FROM pg_policies
484
+ WHERE schemaname = current_schema() AND tablename = table_name AND policyname = 'workspace_isolation'
485
+ ) THEN
486
+ EXECUTE format('DROP POLICY workspace_isolation ON %I', table_name);
487
+ END IF;
488
+ EXECUTE format(
489
+ 'CREATE POLICY workspace_isolation ON %I USING (opengeni_private.workspace_rls_visible(account_id, workspace_id)) WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id))',
490
+ table_name
491
+ );
492
+ END LOOP;
493
+ END $$;
494
+
495
+ ALTER TABLE "api_keys" ENABLE ROW LEVEL SECURITY;
496
+ ALTER TABLE "api_keys" FORCE ROW LEVEL SECURITY;
497
+ DO $$
498
+ BEGIN
499
+ IF EXISTS (
500
+ SELECT 1 FROM pg_policies
501
+ WHERE schemaname = current_schema() AND tablename = 'api_keys' AND policyname = 'api_keys_account_workspace_or_hash_isolation'
502
+ ) THEN
503
+ DROP POLICY "api_keys_account_workspace_or_hash_isolation" ON "api_keys";
504
+ END IF;
505
+ END $$;
506
+ CREATE POLICY "api_keys_account_workspace_or_hash_isolation" ON "api_keys"
507
+ USING (
508
+ opengeni_private.optional_workspace_rls_visible(account_id, workspace_id)
509
+ OR key_hash = opengeni_private.current_api_key_hash()
510
+ )
511
+ WITH CHECK (
512
+ opengeni_private.optional_workspace_rls_visible(account_id, workspace_id)
513
+ OR key_hash = opengeni_private.current_api_key_hash()
514
+ );
515
+
516
+ ALTER TABLE "usage_events" ENABLE ROW LEVEL SECURITY;
517
+ ALTER TABLE "usage_events" FORCE ROW LEVEL SECURITY;
518
+ DO $$
519
+ BEGIN
520
+ IF EXISTS (
521
+ SELECT 1 FROM pg_policies
522
+ WHERE schemaname = current_schema() AND tablename = 'usage_events' AND policyname = 'usage_events_account_workspace_isolation'
523
+ ) THEN
524
+ DROP POLICY "usage_events_account_workspace_isolation" ON "usage_events";
525
+ END IF;
526
+ END $$;
527
+ CREATE POLICY "usage_events_account_workspace_isolation" ON "usage_events"
528
+ USING (opengeni_private.optional_workspace_rls_visible(account_id, workspace_id))
529
+ WITH CHECK (opengeni_private.optional_workspace_rls_visible(account_id, workspace_id));
530
+
531
+ ALTER TABLE "credit_ledger_entries" ENABLE ROW LEVEL SECURITY;
532
+ ALTER TABLE "credit_ledger_entries" FORCE ROW LEVEL SECURITY;
533
+ DO $$
534
+ BEGIN
535
+ IF EXISTS (
536
+ SELECT 1 FROM pg_policies
537
+ WHERE schemaname = current_schema() AND tablename = 'credit_ledger_entries' AND policyname = 'credit_ledger_account_workspace_isolation'
538
+ ) THEN
539
+ DROP POLICY "credit_ledger_account_workspace_isolation" ON "credit_ledger_entries";
540
+ END IF;
541
+ END $$;
542
+ CREATE POLICY "credit_ledger_account_workspace_isolation" ON "credit_ledger_entries"
543
+ USING (opengeni_private.optional_workspace_rls_visible(account_id, workspace_id))
544
+ WITH CHECK (opengeni_private.optional_workspace_rls_visible(account_id, workspace_id));
545
+
546
+ ALTER TABLE "billing_customers" ENABLE ROW LEVEL SECURITY;
547
+ ALTER TABLE "billing_customers" FORCE ROW LEVEL SECURITY;
548
+ DO $$
549
+ BEGIN
550
+ IF EXISTS (
551
+ SELECT 1 FROM pg_policies
552
+ WHERE schemaname = current_schema() AND tablename = 'billing_customers' AND policyname = 'billing_customers_account_isolation'
553
+ ) THEN
554
+ DROP POLICY "billing_customers_account_isolation" ON "billing_customers";
555
+ END IF;
556
+ END $$;
557
+ CREATE POLICY "billing_customers_account_isolation" ON "billing_customers"
558
+ USING (opengeni_private.account_rls_visible(account_id))
559
+ WITH CHECK (opengeni_private.account_rls_visible(account_id));
560
+
561
+ ALTER TABLE "audit_events" ENABLE ROW LEVEL SECURITY;
562
+ ALTER TABLE "audit_events" FORCE ROW LEVEL SECURITY;
563
+ DO $$
564
+ BEGIN
565
+ IF EXISTS (
566
+ SELECT 1 FROM pg_policies
567
+ WHERE schemaname = current_schema() AND tablename = 'audit_events' AND policyname = 'audit_events_account_workspace_isolation'
568
+ ) THEN
569
+ DROP POLICY "audit_events_account_workspace_isolation" ON "audit_events";
570
+ END IF;
571
+ END $$;
572
+ CREATE POLICY "audit_events_account_workspace_isolation" ON "audit_events"
573
+ USING (
574
+ account_id IS NULL
575
+ OR opengeni_private.optional_workspace_rls_visible(account_id, workspace_id)
576
+ )
577
+ WITH CHECK (
578
+ account_id IS NULL
579
+ OR opengeni_private.optional_workspace_rls_visible(account_id, workspace_id)
580
+ );
581
+
582
+ DO $$
583
+ BEGIN
584
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
585
+ GRANT USAGE ON SCHEMA public TO opengeni_app;
586
+ GRANT USAGE ON SCHEMA opengeni_private TO opengeni_app;
587
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO opengeni_app;
588
+ GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_private TO opengeni_app;
589
+ END IF;
590
+ END $$;