@opengeni/db 0.31.1 → 0.36.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 (125) hide show
  1. package/dist/browser-auth.d.ts +209 -1
  2. package/dist/browser-downloads.d.ts +51 -0
  3. package/dist/browser-identities.d.ts +11 -2
  4. package/dist/browser-sessions.d.ts +83 -1
  5. package/dist/browser-state-artifacts.d.ts +75 -1
  6. package/dist/canonical-human-identities.d.ts +48 -0
  7. package/dist/canonical-human-identities.js +28 -0
  8. package/dist/canonical-human-identities.js.map +1 -0
  9. package/dist/capability-integrations.d.ts +8 -6
  10. package/dist/{chunk-MWDVXQOL.js → chunk-5KTIEDX7.js} +5773 -4212
  11. package/dist/chunk-5KTIEDX7.js.map +1 -0
  12. package/dist/chunk-FPVAD4CO.js +117 -0
  13. package/dist/chunk-FPVAD4CO.js.map +1 -0
  14. package/dist/chunk-GYO7ED2Q.js +259 -0
  15. package/dist/chunk-GYO7ED2Q.js.map +1 -0
  16. package/dist/{chunk-F5FT7BEZ.js → chunk-HSOWSYOX.js} +2 -2
  17. package/dist/{chunk-YHZXQ7HP.js → chunk-VKLB2ZGI.js} +446 -19
  18. package/dist/chunk-VKLB2ZGI.js.map +1 -0
  19. package/dist/{chunk-NBE6CHRI.js → chunk-VTPRIXKG.js} +49 -9
  20. package/dist/chunk-VTPRIXKG.js.map +1 -0
  21. package/dist/{chunk-ISI2TIUG.js → chunk-Z7VYMNPZ.js} +76 -4
  22. package/dist/chunk-Z7VYMNPZ.js.map +1 -0
  23. package/dist/database.d.ts +5 -0
  24. package/dist/editable-artifact-durable-export.js +2 -2
  25. package/dist/editable-artifacts.js +3 -3
  26. package/dist/index.d.ts +467 -29
  27. package/dist/index.js +23247 -16232
  28. package/dist/index.js.map +1 -1
  29. package/dist/insights.d.ts +23 -7
  30. package/dist/integration-bindings.d.ts +24 -24
  31. package/dist/integration-facets.d.ts +99 -0
  32. package/dist/interaction-revisions.d.ts +11 -0
  33. package/dist/interaction-schema.d.ts +698 -34
  34. package/dist/persistence-errors.d.ts +8 -0
  35. package/dist/provision-roles.js +1 -1
  36. package/dist/runtime-posture.d.ts +8 -7
  37. package/dist/schema.d.ts +9464 -5412
  38. package/dist/schema.js +51 -9
  39. package/dist/session-control.d.ts +29 -3
  40. package/dist/session-tenancy.d.ts +44 -0
  41. package/dist/session-tenancy.js +16 -0
  42. package/dist/session-tenancy.js.map +1 -0
  43. package/dist/slack-task-policy-schema.d.ts +847 -0
  44. package/dist/slack-task-policy.d.ts +65 -0
  45. package/dist/task-notes-schema.d.ts +924 -0
  46. package/dist/task-notes.d.ts +85 -0
  47. package/dist/video-generation.d.ts +9 -0
  48. package/dist/video-generation.js +5 -3
  49. package/dist/xai-subscription.d.ts +326 -0
  50. package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
  51. package/drizzle/0213_browser_interaction_authority.sql +67 -0
  52. package/drizzle/0214_browser_download_saves.sql +34 -0
  53. package/drizzle/0215_browser_controller_host.sql +385 -0
  54. package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
  55. package/drizzle/0217_external_browser_auth_operations.sql +22 -0
  56. package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
  57. package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
  58. package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
  59. package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
  60. package/drizzle/0220_session_channels.sql +54 -0
  61. package/drizzle/0221_sessions_channel_index.sql +5 -0
  62. package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
  63. package/drizzle/0222_sessions_channel_fk.sql +89 -0
  64. package/drizzle/0223_pending_tool_event_output.sql +55 -0
  65. package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
  66. package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
  67. package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
  68. package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
  69. package/drizzle/0227_slack_native_actions.sql +145 -0
  70. package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
  71. package/drizzle/0228_slack_task_policy.sql +400 -0
  72. package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
  73. package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
  74. package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
  75. package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
  76. package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
  77. package/drizzle/0234_xai_subscription_authority.sql +1139 -0
  78. package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
  79. package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
  80. package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
  81. package/drizzle/0237_interaction_transition_reaper.sql +263 -0
  82. package/drizzle/0238_goal_persistence_policy.sql +471 -0
  83. package/drizzle/0238_recover_unclaimed_session_turns.sql +307 -0
  84. package/drizzle/0238_supergrok_realtime_model.sql +29 -0
  85. package/drizzle/0239_supergrok_video_funding.sql +50 -0
  86. package/drizzle/0239_task_tree_notes.sql +990 -0
  87. package/package.json +13 -5
  88. package/src/browser-auth.ts +2600 -123
  89. package/src/browser-downloads.ts +517 -0
  90. package/src/browser-identities.ts +220 -3
  91. package/src/browser-sessions.ts +334 -45
  92. package/src/browser-state-artifacts.ts +402 -12
  93. package/src/canonical-human-identities.ts +317 -0
  94. package/src/capability-integrations.ts +121 -213
  95. package/src/database.ts +41 -1
  96. package/src/index.ts +4625 -411
  97. package/src/insights.ts +109 -28
  98. package/src/integration-bindings.ts +101 -101
  99. package/src/{integration-features.ts → integration-facets.ts} +187 -184
  100. package/src/interaction-revisions.ts +33 -3
  101. package/src/interaction-schema.ts +311 -8
  102. package/src/pack-components.ts +43 -43
  103. package/src/pack-installations.ts +0 -43
  104. package/src/persistence-errors.ts +60 -1
  105. package/src/plugin-packages.ts +9 -9
  106. package/src/provision-roles.ts +232 -0
  107. package/src/runtime-posture.ts +227 -14
  108. package/src/schema.ts +1188 -75
  109. package/src/session-control.ts +122 -37
  110. package/src/session-queue-commands.ts +78 -1
  111. package/src/session-tenancy.ts +188 -0
  112. package/src/session-tool-call-settlement.ts +9 -3
  113. package/src/slack-task-policy-schema.ts +130 -0
  114. package/src/slack-task-policy.ts +341 -0
  115. package/src/task-notes-schema.ts +135 -0
  116. package/src/task-notes.ts +178 -0
  117. package/src/video-generation.ts +90 -12
  118. package/src/workspace-artifacts.ts +1 -1
  119. package/src/xai-subscription.ts +1887 -0
  120. package/dist/chunk-ISI2TIUG.js.map +0 -1
  121. package/dist/chunk-MWDVXQOL.js.map +0 -1
  122. package/dist/chunk-NBE6CHRI.js.map +0 -1
  123. package/dist/chunk-YHZXQ7HP.js.map +0 -1
  124. package/dist/integration-features.d.ts +0 -99
  125. /package/dist/{chunk-F5FT7BEZ.js.map → chunk-HSOWSYOX.js.map} +0 -0
@@ -0,0 +1,299 @@
1
+ -- deployment-mode: rolling
2
+ -- Additive organization-tenancy Slice A foundation only. This migration names
3
+ -- durable organization membership, user-resource authority/grant identity,
4
+ -- retention policy, and generic session visibility/fork provenance. It does
5
+ -- not move an existing resource, grant runtime access, or activate sharing.
6
+
7
+ SET LOCAL lock_timeout = '5s';
8
+ SET LOCAL statement_timeout = '10min';
9
+
10
+ CREATE TABLE "organization_memberships" (
11
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
12
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
13
+ "subject_id" text NOT NULL,
14
+ "status" text NOT NULL DEFAULT 'provisioning',
15
+ "personal_workspace_id" uuid,
16
+ "authorization_revision" bigint NOT NULL DEFAULT 1,
17
+ "revoked_at" timestamptz,
18
+ "personal_retention_until" timestamptz,
19
+ "created_at" timestamptz NOT NULL DEFAULT now(),
20
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
21
+ CONSTRAINT "organization_memberships_status_check" CHECK (
22
+ "status" IN ('provisioning', 'active', 'suspended', 'revoked')
23
+ ),
24
+ CONSTRAINT "organization_memberships_active_personal_workspace_check" CHECK (
25
+ "status" <> 'active' OR "personal_workspace_id" IS NOT NULL
26
+ ),
27
+ CONSTRAINT "organization_memberships_subject_check" CHECK (
28
+ length(btrim("subject_id")) BETWEEN 1 AND 1024
29
+ ),
30
+ CONSTRAINT "organization_memberships_revision_check" CHECK (
31
+ "authorization_revision" > 0
32
+ ),
33
+ CONSTRAINT "organization_memberships_revocation_check" CHECK (
34
+ ("status" = 'revoked' AND "revoked_at" IS NOT NULL)
35
+ OR ("status" <> 'revoked' AND "revoked_at" IS NULL)
36
+ ),
37
+ CONSTRAINT "organization_memberships_personal_workspace_account_fk"
38
+ FOREIGN KEY ("personal_workspace_id", "account_id")
39
+ REFERENCES "workspaces"("id", "account_id") ON DELETE RESTRICT
40
+ );
41
+
42
+ CREATE UNIQUE INDEX "organization_memberships_id_account_idx"
43
+ ON "organization_memberships" ("id", "account_id");
44
+ CREATE UNIQUE INDEX "organization_memberships_account_subject_idx"
45
+ ON "organization_memberships" ("account_id", "subject_id");
46
+ CREATE UNIQUE INDEX "organization_memberships_personal_workspace_idx"
47
+ ON "organization_memberships" ("account_id", "personal_workspace_id")
48
+ WHERE "personal_workspace_id" IS NOT NULL;
49
+
50
+ CREATE TABLE "organization_user_retention_policies" (
51
+ "account_id" uuid PRIMARY KEY REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
52
+ "mode" text NOT NULL DEFAULT 'retain',
53
+ "retention_days" integer,
54
+ "version" bigint NOT NULL DEFAULT 1,
55
+ "updated_by_membership_id" uuid,
56
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
57
+ CONSTRAINT "organization_user_retention_policies_updater_fk"
58
+ FOREIGN KEY ("updated_by_membership_id", "account_id")
59
+ REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
60
+ CONSTRAINT "organization_user_retention_policies_mode_check" CHECK (
61
+ "mode" IN ('retain', 'delete_after')
62
+ ),
63
+ CONSTRAINT "organization_user_retention_policies_duration_check" CHECK (
64
+ ("mode" = 'retain' AND "retention_days" IS NULL)
65
+ OR (
66
+ "mode" = 'delete_after'
67
+ AND "retention_days" IS NOT NULL
68
+ AND "retention_days" BETWEEN 1 AND 3650
69
+ )
70
+ ),
71
+ CONSTRAINT "organization_user_retention_policies_version_check" CHECK (
72
+ "version" > 0
73
+ )
74
+ );
75
+
76
+ CREATE TABLE "organization_user_resource_authorities" (
77
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
78
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
79
+ "organization_membership_id" uuid NOT NULL,
80
+ "resource_kind" text NOT NULL,
81
+ "resource_id" uuid NOT NULL,
82
+ "origin_workspace_id" uuid,
83
+ "generation" bigint NOT NULL DEFAULT 1,
84
+ "status" text NOT NULL DEFAULT 'active',
85
+ "revoked_at" timestamptz,
86
+ "created_at" timestamptz NOT NULL DEFAULT now(),
87
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
88
+ CONSTRAINT "organization_user_resource_authorities_membership_fk"
89
+ FOREIGN KEY ("organization_membership_id", "account_id")
90
+ REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
91
+ CONSTRAINT "organization_user_resource_authorities_origin_workspace_fk"
92
+ FOREIGN KEY ("origin_workspace_id", "account_id")
93
+ REFERENCES "workspaces"("id", "account_id")
94
+ ON DELETE SET NULL ("origin_workspace_id"),
95
+ CONSTRAINT "organization_user_resource_authorities_kind_check" CHECK (
96
+ "resource_kind" = lower(btrim("resource_kind"))
97
+ AND length("resource_kind") BETWEEN 1 AND 64
98
+ AND "resource_kind" ~ '^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$'
99
+ ),
100
+ CONSTRAINT "organization_user_resource_authorities_generation_check" CHECK (
101
+ "generation" > 0
102
+ ),
103
+ CONSTRAINT "organization_user_resource_authorities_status_check" CHECK (
104
+ "status" IN ('active', 'retained', 'revoked')
105
+ ),
106
+ CONSTRAINT "organization_user_resource_authorities_revocation_check" CHECK (
107
+ ("status" = 'revoked' AND "revoked_at" IS NOT NULL)
108
+ OR ("status" <> 'revoked' AND "revoked_at" IS NULL)
109
+ )
110
+ );
111
+
112
+ CREATE UNIQUE INDEX "organization_user_resource_authorities_id_account_idx"
113
+ ON "organization_user_resource_authorities" ("id", "account_id");
114
+ CREATE UNIQUE INDEX "organization_user_resource_authorities_id_account_membership_idx"
115
+ ON "organization_user_resource_authorities" ("id", "account_id", "organization_membership_id");
116
+ CREATE UNIQUE INDEX "organization_user_resource_authorities_resource_identity_idx"
117
+ ON "organization_user_resource_authorities" (
118
+ "account_id",
119
+ "organization_membership_id",
120
+ "resource_kind",
121
+ "resource_id"
122
+ );
123
+
124
+ -- Existing sessions remain explicitly workspace-owned/workspace-visible. Null
125
+ -- owner and fork facts are non-authority; epoch 1 is the legacy baseline.
126
+ ALTER TABLE "sessions"
127
+ ADD COLUMN IF NOT EXISTS "owner_organization_membership_id" uuid,
128
+ ADD COLUMN IF NOT EXISTS "visibility" text NOT NULL DEFAULT 'workspace_shared',
129
+ ADD COLUMN IF NOT EXISTS "authority_epoch" integer NOT NULL DEFAULT 1,
130
+ ADD COLUMN IF NOT EXISTS "forked_from_session_id" uuid,
131
+ ADD COLUMN IF NOT EXISTS "forked_from_authority_epoch" integer,
132
+ ADD COLUMN IF NOT EXISTS "forked_from_visibility" text,
133
+ ADD COLUMN IF NOT EXISTS "forked_at" timestamptz,
134
+ ADD COLUMN IF NOT EXISTS "forked_by_organization_membership_id" uuid;
135
+
136
+ CREATE UNIQUE INDEX IF NOT EXISTS "sessions_id_account_idx"
137
+ ON "sessions" ("id", "account_id");
138
+
139
+ ALTER TABLE "sessions"
140
+ ADD CONSTRAINT "sessions_owner_organization_membership_fk"
141
+ FOREIGN KEY ("owner_organization_membership_id", "account_id")
142
+ REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT NOT VALID,
143
+ ADD CONSTRAINT "sessions_forked_from_session_account_fk"
144
+ FOREIGN KEY ("forked_from_session_id", "account_id")
145
+ REFERENCES "sessions"("id", "account_id") ON DELETE RESTRICT NOT VALID,
146
+ ADD CONSTRAINT "sessions_forked_by_organization_membership_fk"
147
+ FOREIGN KEY ("forked_by_organization_membership_id", "account_id")
148
+ REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT NOT VALID,
149
+ ADD CONSTRAINT "sessions_visibility_check" CHECK (
150
+ "visibility" IN ('user_private', 'workspace_shared')
151
+ ) NOT VALID,
152
+ ADD CONSTRAINT "sessions_private_owner_check" CHECK (
153
+ "visibility" <> 'user_private' OR "owner_organization_membership_id" IS NOT NULL
154
+ ) NOT VALID,
155
+ ADD CONSTRAINT "sessions_authority_epoch_check" CHECK (
156
+ "authority_epoch" > 0
157
+ ) NOT VALID,
158
+ ADD CONSTRAINT "sessions_fork_provenance_check" CHECK (
159
+ (
160
+ "forked_from_session_id" IS NULL
161
+ AND "forked_from_authority_epoch" IS NULL
162
+ AND "forked_from_visibility" IS NULL
163
+ AND "forked_at" IS NULL
164
+ AND "forked_by_organization_membership_id" IS NULL
165
+ ) OR (
166
+ "forked_from_session_id" IS NOT NULL
167
+ AND "forked_from_authority_epoch" IS NOT NULL
168
+ AND "forked_from_authority_epoch" > 0
169
+ AND "forked_from_visibility" IN ('user_private', 'workspace_shared')
170
+ AND "forked_at" IS NOT NULL
171
+ AND "forked_by_organization_membership_id" IS NOT NULL
172
+ )
173
+ ) NOT VALID;
174
+
175
+ ALTER TABLE "sessions"
176
+ VALIDATE CONSTRAINT "sessions_owner_organization_membership_fk",
177
+ VALIDATE CONSTRAINT "sessions_forked_from_session_account_fk",
178
+ VALIDATE CONSTRAINT "sessions_forked_by_organization_membership_fk",
179
+ VALIDATE CONSTRAINT "sessions_visibility_check",
180
+ VALIDATE CONSTRAINT "sessions_private_owner_check",
181
+ VALIDATE CONSTRAINT "sessions_authority_epoch_check",
182
+ VALIDATE CONSTRAINT "sessions_fork_provenance_check";
183
+
184
+ CREATE TABLE "organization_user_resource_grants" (
185
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
186
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
187
+ "authority_id" uuid NOT NULL,
188
+ "owner_organization_membership_id" uuid NOT NULL,
189
+ "workspace_id" uuid NOT NULL,
190
+ "session_id" uuid,
191
+ "action" text NOT NULL,
192
+ "mode" text NOT NULL,
193
+ "context" text NOT NULL,
194
+ "authority_epoch" integer,
195
+ "generation" bigint NOT NULL DEFAULT 1,
196
+ "status" text NOT NULL DEFAULT 'active',
197
+ "expires_at" timestamptz,
198
+ "revoked_at" timestamptz,
199
+ "created_at" timestamptz NOT NULL DEFAULT now(),
200
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
201
+ CONSTRAINT "organization_user_resource_grants_authority_fk"
202
+ FOREIGN KEY ("authority_id", "account_id")
203
+ REFERENCES "organization_user_resource_authorities"("id", "account_id")
204
+ ON DELETE RESTRICT,
205
+ CONSTRAINT "organization_user_resource_grants_owner_membership_fk"
206
+ FOREIGN KEY ("owner_organization_membership_id", "account_id")
207
+ REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
208
+ CONSTRAINT "organization_user_resource_grants_authority_owner_fk"
209
+ FOREIGN KEY ("authority_id", "account_id", "owner_organization_membership_id")
210
+ REFERENCES "organization_user_resource_authorities"(
211
+ "id", "account_id", "organization_membership_id"
212
+ ) ON DELETE RESTRICT,
213
+ CONSTRAINT "organization_user_resource_grants_workspace_account_fk"
214
+ FOREIGN KEY ("workspace_id", "account_id")
215
+ REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
216
+ CONSTRAINT "organization_user_resource_grants_session_workspace_fk"
217
+ FOREIGN KEY ("workspace_id", "session_id")
218
+ REFERENCES "sessions"("workspace_id", "id") ON DELETE RESTRICT,
219
+ CONSTRAINT "organization_user_resource_grants_action_check" CHECK (
220
+ "action" = lower(btrim("action"))
221
+ AND length("action") BETWEEN 1 AND 64
222
+ AND "action" ~ '^[a-z0-9](?:[a-z0-9._:-]*[a-z0-9])?$'
223
+ ),
224
+ CONSTRAINT "organization_user_resource_grants_mode_check" CHECK (
225
+ "mode" IN ('once', 'session', 'always')
226
+ ),
227
+ CONSTRAINT "organization_user_resource_grants_context_check" CHECK (
228
+ "context" IN ('user_private', 'workspace_shared')
229
+ ),
230
+ CONSTRAINT "organization_user_resource_grants_session_fence_check" CHECK (
231
+ (
232
+ "mode" = 'always'
233
+ AND "session_id" IS NULL
234
+ AND "authority_epoch" IS NULL
235
+ ) OR (
236
+ "mode" IN ('once', 'session')
237
+ AND
238
+ "session_id" IS NOT NULL
239
+ AND "authority_epoch" IS NOT NULL
240
+ AND "authority_epoch" > 0
241
+ )
242
+ ),
243
+ CONSTRAINT "organization_user_resource_grants_generation_check" CHECK (
244
+ "generation" > 0
245
+ ),
246
+ CONSTRAINT "organization_user_resource_grants_status_check" CHECK (
247
+ "status" IN ('active', 'consumed', 'revoked', 'expired')
248
+ ),
249
+ CONSTRAINT "organization_user_resource_grants_revocation_check" CHECK (
250
+ ("status" = 'revoked' AND "revoked_at" IS NOT NULL)
251
+ OR ("status" <> 'revoked' AND "revoked_at" IS NULL)
252
+ )
253
+ );
254
+
255
+ CREATE UNIQUE INDEX "organization_user_resource_grants_id_account_idx"
256
+ ON "organization_user_resource_grants" ("id", "account_id");
257
+ CREATE INDEX "organization_user_resource_grants_authority_workspace_idx"
258
+ ON "organization_user_resource_grants" ("authority_id", "workspace_id", "status");
259
+
260
+ -- Slice A is intentionally inert. Each table has one explicit system-only
261
+ -- deny-all policy and no direct opengeni_app privileges, so the runtime role
262
+ -- remains denied until a later reviewed dual-write/backfill/validate/activate
263
+ -- slice installs exact behavior.
264
+ ALTER TABLE "organization_memberships" ENABLE ROW LEVEL SECURITY;
265
+ ALTER TABLE "organization_memberships" FORCE ROW LEVEL SECURITY;
266
+ ALTER TABLE "organization_user_retention_policies" ENABLE ROW LEVEL SECURITY;
267
+ ALTER TABLE "organization_user_retention_policies" FORCE ROW LEVEL SECURITY;
268
+ ALTER TABLE "organization_user_resource_authorities" ENABLE ROW LEVEL SECURITY;
269
+ ALTER TABLE "organization_user_resource_authorities" FORCE ROW LEVEL SECURITY;
270
+ ALTER TABLE "organization_user_resource_grants" ENABLE ROW LEVEL SECURITY;
271
+ ALTER TABLE "organization_user_resource_grants" FORCE ROW LEVEL SECURITY;
272
+
273
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_memberships";
274
+ CREATE POLICY organization_tenancy_system_only ON "organization_memberships"
275
+ USING (false) WITH CHECK (false);
276
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_user_retention_policies";
277
+ CREATE POLICY organization_tenancy_system_only ON "organization_user_retention_policies"
278
+ USING (false) WITH CHECK (false);
279
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_user_resource_authorities";
280
+ CREATE POLICY organization_tenancy_system_only ON "organization_user_resource_authorities"
281
+ USING (false) WITH CHECK (false);
282
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_user_resource_grants";
283
+ CREATE POLICY organization_tenancy_system_only ON "organization_user_resource_grants"
284
+ USING (false) WITH CHECK (false);
285
+
286
+ COMMENT ON TABLE "organization_memberships" IS
287
+ 'Organization membership and personal-workspace lifecycle metadata; not workspace content access.';
288
+ COMMENT ON COLUMN "organization_memberships"."personal_workspace_id" IS
289
+ 'One personal workspace per membership; lifecycle metadata, never user-resource ownership.';
290
+ COMMENT ON TABLE "organization_user_resource_authorities" IS
291
+ 'Stable organization+membership user-resource authority identity; inert in Slice A.';
292
+ COMMENT ON COLUMN "organization_user_resource_authorities"."origin_workspace_id" IS
293
+ 'Non-authoritative provenance only; deletion clears this fact without transferring authority.';
294
+ COMMENT ON TABLE "organization_user_resource_grants" IS
295
+ 'Opaque once/session/always user-resource grant identities; inert in Slice A.';
296
+ COMMENT ON COLUMN "sessions"."visibility" IS
297
+ 'Generic user_private/workspace_shared tenancy visibility; Slice A adds storage only.';
298
+ COMMENT ON COLUMN "sessions"."authority_epoch" IS
299
+ 'Monotonic execution-authority fence for later sharing/fork runtime; Slice A baseline is 1.';
@@ -0,0 +1,317 @@
1
+ -- deployment-mode: rolling
2
+ -- Activate only the managed-human provisioning seam for organization tenancy.
3
+ -- This migration does not expose organization/member CRUD, resource authority or
4
+ -- grant writes, session sharing, or personal-workspace runtime access.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '10min';
8
+
9
+ -- Keep all four Slice A tables FORCE-RLS and replace the inert deny-all policy
10
+ -- with one transaction-local capability marker. The marker is set only inside
11
+ -- the exact SECURITY DEFINER lifecycle routine below; opengeni_app still has
12
+ -- no direct table privileges on any of these tables.
13
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_memberships";
14
+ CREATE POLICY organization_tenancy_lifecycle ON "organization_memberships"
15
+ USING (
16
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
17
+ = 'managed_human_provisioning'
18
+ )
19
+ WITH CHECK (
20
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
21
+ = 'managed_human_provisioning'
22
+ );
23
+
24
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_user_retention_policies";
25
+ CREATE POLICY organization_tenancy_lifecycle ON "organization_user_retention_policies"
26
+ USING (
27
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
28
+ = 'managed_human_provisioning'
29
+ )
30
+ WITH CHECK (
31
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
32
+ = 'managed_human_provisioning'
33
+ );
34
+
35
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_user_resource_authorities";
36
+ CREATE POLICY organization_tenancy_lifecycle ON "organization_user_resource_authorities"
37
+ USING (
38
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
39
+ = 'managed_human_provisioning'
40
+ )
41
+ WITH CHECK (
42
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
43
+ = 'managed_human_provisioning'
44
+ );
45
+
46
+ DROP POLICY IF EXISTS organization_tenancy_system_only ON "organization_user_resource_grants";
47
+ CREATE POLICY organization_tenancy_lifecycle ON "organization_user_resource_grants"
48
+ USING (
49
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
50
+ = 'managed_human_provisioning'
51
+ )
52
+ WITH CHECK (
53
+ current_setting('opengeni.organization_tenancy_lifecycle', true)
54
+ = 'managed_human_provisioning'
55
+ );
56
+
57
+ DO $organization_tenancy_managed_human_provisioning$
58
+ DECLARE
59
+ data_schema text := current_schema();
60
+ BEGIN
61
+ EXECUTE format($ddl$
62
+ CREATE OR REPLACE FUNCTION %1$I.ensure_managed_human_personal_workspace(
63
+ p_account_id uuid,
64
+ p_subject_id text,
65
+ p_personal_workspace_id uuid
66
+ )
67
+ RETURNS TABLE (
68
+ organization_membership_id uuid,
69
+ personal_workspace_id uuid
70
+ )
71
+ LANGUAGE plpgsql
72
+ SECURITY DEFINER
73
+ SET search_path = %1$I, pg_catalog
74
+ AS $body$
75
+ DECLARE
76
+ expected_external_id text;
77
+ membership_row organization_memberships%%ROWTYPE;
78
+ workspace_row workspaces%%ROWTYPE;
79
+ previous_workspace_id text := pg_catalog.current_setting(
80
+ 'opengeni.workspace_id',
81
+ true
82
+ );
83
+ previous_lifecycle_marker text := pg_catalog.current_setting(
84
+ 'opengeni.organization_tenancy_lifecycle',
85
+ true
86
+ );
87
+ BEGIN
88
+ IF p_account_id IS NULL
89
+ OR p_subject_id IS NULL
90
+ OR p_personal_workspace_id IS NULL
91
+ THEN
92
+ RAISE EXCEPTION 'managed-human provisioning requires complete identity'
93
+ USING ERRCODE = '42501';
94
+ END IF;
95
+
96
+ IF p_subject_id <> pg_catalog.btrim(p_subject_id)
97
+ OR pg_catalog.length(p_subject_id) NOT BETWEEN 1 AND 1024
98
+ OR p_subject_id NOT LIKE 'user:%%'
99
+ THEN
100
+ RAISE EXCEPTION 'managed-human provisioning subject is invalid'
101
+ USING ERRCODE = '42501';
102
+ END IF;
103
+
104
+ expected_external_id := pg_catalog.substr(p_subject_id, 6);
105
+ IF expected_external_id IS NULL OR pg_catalog.length(expected_external_id) < 1 THEN
106
+ RAISE EXCEPTION 'managed-human provisioning subject is invalid'
107
+ USING ERRCODE = '42501';
108
+ END IF;
109
+
110
+ IF p_account_id IS DISTINCT FROM opengeni_private.current_account_id() THEN
111
+ RAISE EXCEPTION 'managed-human provisioning account authority is invalid'
112
+ USING ERRCODE = '42501';
113
+ END IF;
114
+
115
+ IF NOT EXISTS (
116
+ SELECT 1
117
+ FROM managed_accounts account_row
118
+ WHERE account_row.id = p_account_id
119
+ AND account_row.external_source = 'better-auth:user'
120
+ AND account_row.external_id = expected_external_id
121
+ ) THEN
122
+ RAISE EXCEPTION 'managed-human provisioning account identity is invalid'
123
+ USING ERRCODE = '42501';
124
+ END IF;
125
+
126
+ IF NOT EXISTS (
127
+ SELECT 1
128
+ FROM workspace_memberships owner_membership
129
+ INNER JOIN workspaces owner_workspace
130
+ ON owner_workspace.id = owner_membership.workspace_id
131
+ AND owner_workspace.account_id = owner_membership.account_id
132
+ WHERE owner_membership.account_id = p_account_id
133
+ AND owner_membership.subject_id = p_subject_id
134
+ AND owner_membership.role = 'owner'
135
+ ) THEN
136
+ RAISE EXCEPTION 'managed-human provisioning owner membership is invalid'
137
+ USING ERRCODE = '42501';
138
+ END IF;
139
+
140
+ SELECT *
141
+ INTO workspace_row
142
+ FROM workspaces candidate_workspace
143
+ WHERE candidate_workspace.id = p_personal_workspace_id
144
+ FOR UPDATE;
145
+ IF NOT FOUND
146
+ OR workspace_row.account_id IS DISTINCT FROM p_account_id
147
+ OR workspace_row.external_source IS DISTINCT FROM 'opengeni:organization-membership'
148
+ OR workspace_row.external_id IS DISTINCT FROM (
149
+ p_account_id::text || ':' || p_subject_id
150
+ )
151
+ THEN
152
+ RAISE EXCEPTION 'managed-human provisioning personal workspace identity is invalid'
153
+ USING ERRCODE = '42501';
154
+ END IF;
155
+
156
+ IF EXISTS (
157
+ SELECT 1
158
+ FROM workspaces conflicting_workspace
159
+ WHERE conflicting_workspace.external_source = 'opengeni:organization-membership'
160
+ AND conflicting_workspace.external_id = (
161
+ p_account_id::text || ':' || p_subject_id
162
+ )
163
+ AND conflicting_workspace.id <> p_personal_workspace_id
164
+ ) THEN
165
+ RAISE EXCEPTION 'managed-human provisioning personal workspace identity conflicts'
166
+ USING ERRCODE = '23505';
167
+ END IF;
168
+
169
+ IF EXISTS (
170
+ SELECT 1
171
+ FROM workspace_memberships personal_access
172
+ WHERE personal_access.workspace_id = p_personal_workspace_id
173
+ ) THEN
174
+ RAISE EXCEPTION 'managed-human personal workspace already has runtime access'
175
+ USING ERRCODE = '42501';
176
+ END IF;
177
+
178
+ -- workspace_inference_controls is FORCE-RLS and needs the exact workspace
179
+ -- context for this structural validation. Restore the caller context on
180
+ -- both success and error before returning from this capability.
181
+ PERFORM pg_catalog.set_config(
182
+ 'opengeni.workspace_id',
183
+ p_personal_workspace_id::text,
184
+ true
185
+ );
186
+ IF NOT EXISTS (
187
+ SELECT 1
188
+ FROM workspace_inference_controls control_row
189
+ WHERE control_row.workspace_id = p_personal_workspace_id
190
+ AND control_row.account_id = p_account_id
191
+ ) THEN
192
+ RAISE EXCEPTION 'managed-human personal workspace control is missing'
193
+ USING ERRCODE = '42501';
194
+ END IF;
195
+
196
+ PERFORM pg_catalog.set_config(
197
+ 'opengeni.organization_tenancy_lifecycle',
198
+ 'managed_human_provisioning',
199
+ true
200
+ );
201
+
202
+ INSERT INTO organization_memberships (
203
+ account_id,
204
+ subject_id,
205
+ status,
206
+ personal_workspace_id
207
+ ) VALUES (
208
+ p_account_id,
209
+ p_subject_id,
210
+ 'active',
211
+ p_personal_workspace_id
212
+ )
213
+ ON CONFLICT (account_id, subject_id) DO NOTHING;
214
+
215
+ SELECT *
216
+ INTO membership_row
217
+ FROM organization_memberships candidate_membership
218
+ WHERE candidate_membership.account_id = p_account_id
219
+ AND candidate_membership.subject_id = p_subject_id
220
+ FOR UPDATE;
221
+ IF NOT FOUND THEN
222
+ RAISE EXCEPTION 'managed-human organization membership was not created'
223
+ USING ERRCODE = 'P0002';
224
+ END IF;
225
+
226
+ IF membership_row.status IN ('suspended', 'revoked') THEN
227
+ RAISE EXCEPTION 'managed-human organization membership is not active'
228
+ USING ERRCODE = '42501';
229
+ END IF;
230
+ IF membership_row.status = 'active'
231
+ AND membership_row.personal_workspace_id IS DISTINCT FROM p_personal_workspace_id
232
+ THEN
233
+ RAISE EXCEPTION 'managed-human organization membership personal workspace conflicts'
234
+ USING ERRCODE = '23505';
235
+ END IF;
236
+ IF membership_row.status = 'provisioning'
237
+ AND membership_row.personal_workspace_id IS NOT NULL
238
+ AND membership_row.personal_workspace_id IS DISTINCT FROM p_personal_workspace_id
239
+ THEN
240
+ RAISE EXCEPTION 'managed-human organization membership personal workspace conflicts'
241
+ USING ERRCODE = '23505';
242
+ END IF;
243
+
244
+ IF membership_row.status = 'provisioning' THEN
245
+ UPDATE organization_memberships
246
+ SET status = 'active',
247
+ personal_workspace_id = p_personal_workspace_id,
248
+ updated_at = pg_catalog.clock_timestamp()
249
+ WHERE id = membership_row.id;
250
+ membership_row.status := 'active';
251
+ membership_row.personal_workspace_id := p_personal_workspace_id;
252
+ END IF;
253
+
254
+ IF membership_row.status <> 'active'
255
+ OR membership_row.personal_workspace_id IS DISTINCT FROM p_personal_workspace_id
256
+ THEN
257
+ RAISE EXCEPTION 'managed-human organization membership did not converge'
258
+ USING ERRCODE = '42501';
259
+ END IF;
260
+
261
+ PERFORM pg_catalog.set_config(
262
+ 'opengeni.workspace_id',
263
+ CASE
264
+ WHEN previous_workspace_id IS NULL THEN ''
265
+ ELSE previous_workspace_id
266
+ END,
267
+ true
268
+ );
269
+ PERFORM pg_catalog.set_config(
270
+ 'opengeni.organization_tenancy_lifecycle',
271
+ CASE
272
+ WHEN previous_lifecycle_marker IS NULL THEN ''
273
+ ELSE previous_lifecycle_marker
274
+ END,
275
+ true
276
+ );
277
+
278
+ organization_membership_id := membership_row.id;
279
+ personal_workspace_id := membership_row.personal_workspace_id;
280
+ RETURN NEXT;
281
+ EXCEPTION WHEN OTHERS THEN
282
+ PERFORM pg_catalog.set_config(
283
+ 'opengeni.workspace_id',
284
+ CASE
285
+ WHEN previous_workspace_id IS NULL THEN ''
286
+ ELSE previous_workspace_id
287
+ END,
288
+ true
289
+ );
290
+ PERFORM pg_catalog.set_config(
291
+ 'opengeni.organization_tenancy_lifecycle',
292
+ CASE
293
+ WHEN previous_lifecycle_marker IS NULL THEN ''
294
+ ELSE previous_lifecycle_marker
295
+ END,
296
+ true
297
+ );
298
+ RAISE;
299
+ END;
300
+ $body$;
301
+ $ddl$, data_schema);
302
+
303
+ EXECUTE format(
304
+ 'REVOKE ALL ON FUNCTION %I.ensure_managed_human_personal_workspace(uuid,text,uuid) FROM PUBLIC',
305
+ data_schema
306
+ );
307
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
308
+ EXECUTE format(
309
+ 'GRANT EXECUTE ON FUNCTION %I.ensure_managed_human_personal_workspace(uuid,text,uuid) TO opengeni_app',
310
+ data_schema
311
+ );
312
+ END IF;
313
+ END
314
+ $organization_tenancy_managed_human_provisioning$;
315
+
316
+ COMMENT ON FUNCTION ensure_managed_human_personal_workspace(uuid, text, uuid) IS
317
+ 'Lifecycle-only managed-human organization membership and personal-workspace provisioning; no workspace access grant.';