@opengeni/db 1.0.1 → 3.1.0-canary.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 (265) hide show
  1. package/dist/attached-browser-devices.d.ts +7 -0
  2. package/dist/automations.d.ts +154 -0
  3. package/dist/canonical-human-identities.js +3 -3
  4. package/dist/capability-integrations.d.ts +13 -0
  5. package/dist/child-lifecycle-notices.d.ts +68 -0
  6. package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
  7. package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
  8. package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
  9. package/dist/chunk-NMUBV7Z5.js.map +1 -0
  10. package/dist/chunk-OF5OBB76.js +4457 -0
  11. package/dist/chunk-OF5OBB76.js.map +1 -0
  12. package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
  13. package/dist/chunk-PR2WMLOQ.js.map +1 -0
  14. package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
  15. package/dist/chunk-SCPUJS53.js.map +1 -0
  16. package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
  17. package/dist/chunk-SIBYBA2D.js.map +1 -0
  18. package/dist/chunk-TIKF3KTI.js +383 -0
  19. package/dist/chunk-TIKF3KTI.js.map +1 -0
  20. package/dist/chunk-ZZ5LJEFX.js +516 -0
  21. package/dist/chunk-ZZ5LJEFX.js.map +1 -0
  22. package/dist/codemode-operations.d.ts +1 -6
  23. package/dist/company-brain-context-selection-schema.d.ts +705 -0
  24. package/dist/company-brain-context-selection.d.ts +94 -0
  25. package/dist/company-brain-governed-writes.d.ts +24 -0
  26. package/dist/company-brain.d.ts +55 -0
  27. package/dist/company-profile-agent-admin.d.ts +15 -0
  28. package/dist/company-profile-schema.d.ts +619 -2
  29. package/dist/company-profile.d.ts +9 -4
  30. package/dist/connection-authority.d.ts +95 -0
  31. package/dist/connection-scopes.d.ts +8 -0
  32. package/dist/connection-tenancy-backfill.d.ts +41 -0
  33. package/dist/connection-token-resolver.d.ts +30 -1
  34. package/dist/database.d.ts +19 -0
  35. package/dist/editable-artifact-durable-export.js +2 -2
  36. package/dist/editable-artifacts.d.ts +2 -2
  37. package/dist/editable-artifacts.js +3 -3
  38. package/dist/governed-learning-activation-schema.d.ts +984 -0
  39. package/dist/governed-learning-activation.d.ts +98 -0
  40. package/dist/governed-learning-evaluator-schema.d.ts +633 -0
  41. package/dist/governed-learning-evaluator.d.ts +28 -0
  42. package/dist/index.d.ts +1595 -81
  43. package/dist/index.js +21571 -8126
  44. package/dist/index.js.map +1 -1
  45. package/dist/insights.d.ts +18 -0
  46. package/dist/integration-bindings.d.ts +1 -0
  47. package/dist/integration-facets.d.ts +30 -0
  48. package/dist/interaction-placement-loss.d.ts +22 -0
  49. package/dist/managed-human-provisioning.d.ts +25 -0
  50. package/dist/memory-domain.d.ts +3 -3
  51. package/dist/migrate.d.ts +11 -1
  52. package/dist/migrate.js +1 -1
  53. package/dist/new-session-drafts.d.ts +43 -5
  54. package/dist/organization-membership-backfill.d.ts +174 -0
  55. package/dist/organization-membership-lifecycle.d.ts +162 -0
  56. package/dist/pr-review.d.ts +107 -0
  57. package/dist/preference-registry-schema.d.ts +2 -0
  58. package/dist/preference-registry.d.ts +2 -0
  59. package/dist/provision-roles.js +1 -1
  60. package/dist/runtime-posture.d.ts +22 -8
  61. package/dist/schema.d.ts +24914 -13703
  62. package/dist/schema.js +99 -1
  63. package/dist/scoped-knowledge-schema.d.ts +52 -1
  64. package/dist/session-background-commands.d.ts +125 -0
  65. package/dist/session-background-commands.js +34 -0
  66. package/dist/session-background-commands.js.map +1 -0
  67. package/dist/session-control.d.ts +156 -3
  68. package/dist/session-goal-pacing.d.ts +68 -0
  69. package/dist/session-queue-commands.d.ts +41 -4
  70. package/dist/session-realtime-ledger.d.ts +2 -0
  71. package/dist/session-tenancy.d.ts +71 -1
  72. package/dist/session-tenancy.js +25 -3
  73. package/dist/session-tool-call-settlement.d.ts +9 -4
  74. package/dist/slack-routing-personal-workspace.d.ts +23 -0
  75. package/dist/slack-routing.d.ts +153 -0
  76. package/dist/slack-task-policy-schema.d.ts +34 -0
  77. package/dist/slack-task-policy.d.ts +8 -0
  78. package/dist/slack-user-link-access.d.ts +1 -0
  79. package/dist/task-notes-schema.d.ts +594 -0
  80. package/dist/task-notes.d.ts +39 -0
  81. package/dist/turn-initiator.d.ts +2 -0
  82. package/dist/user-resource-authority.d.ts +109 -0
  83. package/dist/video-generation.js +3 -3
  84. package/dist/workspace-authority.d.ts +81 -0
  85. package/dist/workspace-instruction-policies-schema.d.ts +279 -0
  86. package/dist/workspace-instruction-policies.d.ts +62 -0
  87. package/dist/workspace-learning-policy.d.ts +1 -0
  88. package/dist/workspace-membership-permissions.d.ts +2 -0
  89. package/drizzle/0240_enrollment_connection_authority.sql +39 -0
  90. package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
  91. package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
  92. package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
  93. package/drizzle/0245_model_context_contribution_facts.sql +88 -0
  94. package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
  95. package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
  96. package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
  97. package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
  98. package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
  99. package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
  100. package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
  101. package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
  102. package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
  103. package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
  104. package/drizzle/0256_connection_authority_delegation.sql +703 -0
  105. package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
  106. package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
  107. package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
  108. package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
  109. package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
  110. package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
  111. package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
  112. package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
  113. package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
  114. package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
  115. package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
  116. package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
  117. package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
  118. package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
  119. package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
  120. package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
  121. package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
  122. package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
  123. package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
  124. package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
  125. package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
  126. package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
  127. package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
  128. package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
  129. package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
  130. package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
  131. package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
  132. package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
  133. package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
  134. package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
  135. package/drizzle/0289_session_composer_policy_authority.sql +597 -0
  136. package/drizzle/0290_organization_membership_backfill.sql +268 -0
  137. package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
  138. package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
  139. package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
  140. package/drizzle/0294_preference_activation_authority.sql +237 -0
  141. package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
  142. package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
  143. package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
  144. package/drizzle/0298_organization_tenancy_parity.sql +609 -0
  145. package/drizzle/0299_organization_membership_lock_order.sql +291 -0
  146. package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
  147. package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
  148. package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
  149. package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
  150. package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
  151. package/drizzle/0305_personal_resource_grant_management.sql +584 -0
  152. package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
  153. package/drizzle/0307_session_attention_state.sql +51 -0
  154. package/drizzle/0308_session_archives.sql +35 -0
  155. package/drizzle/0309_channel_project_pins.sql +6 -0
  156. package/drizzle/0310_channel_project_order.sql +27 -0
  157. package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
  158. package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
  159. package/drizzle/0313_private_child_session_authority.sql +365 -0
  160. package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
  161. package/drizzle/0315_personal_github_repository_selection.sql +624 -0
  162. package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
  163. package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
  164. package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
  165. package/drizzle/0319_event_triggered_automations.sql +254 -0
  166. package/drizzle/0320_pr_review_pack.sql +113 -0
  167. package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
  168. package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
  169. package/drizzle/0323_organization_private_session_enablement.sql +489 -0
  170. package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
  171. package/drizzle/0325_child_lifecycle_notices.sql +44 -0
  172. package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
  173. package/drizzle/0327_slack_first_task_hint.sql +29 -0
  174. package/drizzle/0328_session_background_commands.sql +590 -0
  175. package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
  176. package/drizzle/0330_api_key_descriptions.sql +20 -0
  177. package/drizzle/0331_managed_organization_creation.sql +163 -0
  178. package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
  179. package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
  180. package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
  181. package/drizzle/0335_slack_workspace_routing.sql +460 -0
  182. package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
  183. package/drizzle/0337_slack_routed_action_handles.sql +189 -0
  184. package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
  185. package/drizzle/0339_document_authority_reclassification.sql +1153 -0
  186. package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
  187. package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
  188. package/package.json +10 -6
  189. package/src/attached-browser-devices.ts +233 -5
  190. package/src/automations.ts +911 -0
  191. package/src/browser-sessions.ts +10 -0
  192. package/src/capability-integrations.ts +175 -1
  193. package/src/child-lifecycle-notices.ts +241 -0
  194. package/src/codemode-operations.ts +1 -35
  195. package/src/company-brain-context-selection-schema.ts +100 -0
  196. package/src/company-brain-context-selection.ts +359 -0
  197. package/src/company-brain-governed-writes.ts +1170 -0
  198. package/src/company-brain.ts +309 -0
  199. package/src/company-profile-agent-admin.ts +225 -0
  200. package/src/company-profile-schema.ts +121 -1
  201. package/src/company-profile.ts +21 -0
  202. package/src/computer-sessions.ts +10 -1
  203. package/src/connection-authority.ts +290 -0
  204. package/src/connection-scopes.ts +20 -0
  205. package/src/connection-tenancy-backfill.ts +81 -0
  206. package/src/connection-token-resolver.ts +263 -31
  207. package/src/database.ts +75 -2
  208. package/src/editable-artifacts-schema.ts +10 -5
  209. package/src/editable-artifacts.ts +21 -11
  210. package/src/governed-learning-activation-schema.ts +121 -0
  211. package/src/governed-learning-activation.ts +588 -0
  212. package/src/governed-learning-evaluator-schema.ts +72 -0
  213. package/src/governed-learning-evaluator.ts +217 -0
  214. package/src/index.ts +11651 -1548
  215. package/src/insights.ts +107 -0
  216. package/src/integration-bindings.ts +88 -6
  217. package/src/integration-facets.ts +287 -84
  218. package/src/interaction-placement-loss.ts +234 -0
  219. package/src/managed-human-provisioning.ts +116 -0
  220. package/src/memory-domain.ts +27 -32
  221. package/src/migrate.ts +77 -1
  222. package/src/new-session-drafts.ts +229 -35
  223. package/src/organization-membership-backfill.ts +735 -0
  224. package/src/organization-membership-lifecycle.ts +985 -0
  225. package/src/pr-review.ts +890 -0
  226. package/src/preference-registry.ts +31 -1
  227. package/src/provision-roles.ts +877 -6
  228. package/src/runtime-posture-cli.ts +2 -0
  229. package/src/runtime-posture.ts +1291 -76
  230. package/src/schema.ts +3303 -428
  231. package/src/scoped-knowledge-schema.ts +16 -8
  232. package/src/scoped-knowledge.ts +1 -1
  233. package/src/session-background-commands.ts +759 -0
  234. package/src/session-control.ts +640 -74
  235. package/src/session-goal-pacing.ts +126 -0
  236. package/src/session-queue-commands.ts +437 -84
  237. package/src/session-realtime-context.ts +3 -3
  238. package/src/session-realtime-ledger.ts +22 -11
  239. package/src/session-realtime-mirror.ts +1 -1
  240. package/src/session-tenancy.ts +403 -49
  241. package/src/session-tool-call-settlement.ts +38 -29
  242. package/src/slack-routing-personal-workspace.ts +67 -0
  243. package/src/slack-routing.ts +469 -0
  244. package/src/slack-task-policy-schema.ts +5 -0
  245. package/src/slack-task-policy.ts +18 -0
  246. package/src/slack-user-link-access.ts +17 -1
  247. package/src/task-notes-schema.ts +74 -0
  248. package/src/task-notes.ts +124 -2
  249. package/src/turn-initiator.ts +8 -0
  250. package/src/user-resource-authority.ts +377 -0
  251. package/src/workspace-authority.ts +160 -0
  252. package/src/workspace-instruction-policies-schema.ts +53 -1
  253. package/src/workspace-instruction-policies.ts +528 -77
  254. package/src/workspace-learning-policy.ts +21 -5
  255. package/src/workspace-membership-permissions.ts +9 -0
  256. package/dist/chunk-4SR4HKKA.js.map +0 -1
  257. package/dist/chunk-6ZYKCRFO.js.map +0 -1
  258. package/dist/chunk-NXAFLCQX.js +0 -2468
  259. package/dist/chunk-NXAFLCQX.js.map +0 -1
  260. package/dist/chunk-SK7YYHBI.js.map +0 -1
  261. package/dist/chunk-WD5ROSLL.js +0 -117
  262. package/dist/chunk-WD5ROSLL.js.map +0 -1
  263. package/dist/chunk-ZWREYEWA.js.map +0 -1
  264. /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
  265. /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
@@ -0,0 +1,1700 @@
1
+ -- deployment-mode: rolling
2
+ -- Activate explicit organization, workspace, and organization+user ownership
3
+ -- for Connected Machines and Rigs. Existing rows remain workspace-owned.
4
+
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '10min';
7
+
8
+ -- Freeze the active data schema into every SECURITY DEFINER routine created by
9
+ -- this migration. Standalone installs resolve to public; embedded installs
10
+ -- resolve to their dedicated schema. pg_temp remains explicitly last.
11
+ SELECT pg_catalog.set_config(
12
+ 'search_path',
13
+ pg_catalog.format('%I, pg_catalog, pg_temp', pg_catalog.current_schema()),
14
+ true
15
+ );
16
+
17
+ ALTER TABLE rigs
18
+ ADD COLUMN generation bigint NOT NULL DEFAULT 1,
19
+ ADD COLUMN status text NOT NULL DEFAULT 'active',
20
+ ADD COLUMN revoked_at timestamptz;
21
+ ALTER TABLE rigs DROP CONSTRAINT rigs_authority_scope_check;
22
+ ALTER TABLE rigs DROP CONSTRAINT rigs_authority_shape_check;
23
+ ALTER TABLE rigs ADD CONSTRAINT rigs_authority_scope_check
24
+ CHECK (authority_scope IN ('organization', 'workspace', 'user')) NOT VALID;
25
+ ALTER TABLE rigs ADD CONSTRAINT rigs_authority_shape_check CHECK (
26
+ (authority_scope IN ('organization', 'workspace') AND authority_id IS NULL
27
+ AND owner_organization_membership_id IS NULL)
28
+ OR (authority_scope = 'user' AND authority_id IS NOT NULL
29
+ AND owner_organization_membership_id IS NOT NULL)
30
+ ) NOT VALID;
31
+ ALTER TABLE rigs ADD CONSTRAINT rigs_generation_check CHECK (generation > 0) NOT VALID;
32
+ ALTER TABLE rigs ADD CONSTRAINT rigs_status_check
33
+ CHECK (status IN ('active', 'revoked')) NOT VALID;
34
+ ALTER TABLE rigs ADD CONSTRAINT rigs_revocation_check CHECK (
35
+ (status = 'active' AND revoked_at IS NULL)
36
+ OR (status = 'revoked' AND revoked_at IS NOT NULL)
37
+ ) NOT VALID;
38
+ ALTER TABLE rigs VALIDATE CONSTRAINT rigs_authority_scope_check;
39
+ ALTER TABLE rigs VALIDATE CONSTRAINT rigs_authority_shape_check;
40
+ ALTER TABLE rigs VALIDATE CONSTRAINT rigs_generation_check;
41
+ ALTER TABLE rigs VALIDATE CONSTRAINT rigs_status_check;
42
+ ALTER TABLE rigs VALIDATE CONSTRAINT rigs_revocation_check;
43
+ DROP INDEX rigs_workspace_name_idx;
44
+ CREATE UNIQUE INDEX rigs_workspace_name_active_idx ON rigs(workspace_id, name)
45
+ WHERE authority_scope = 'workspace' AND status = 'active';
46
+ CREATE UNIQUE INDEX rigs_organization_name_active_idx ON rigs(account_id, name)
47
+ WHERE authority_scope = 'organization' AND status = 'active';
48
+ CREATE UNIQUE INDEX rigs_user_name_active_idx
49
+ ON rigs(account_id, owner_organization_membership_id, name)
50
+ WHERE authority_scope = 'user' AND status = 'active';
51
+
52
+ ALTER TABLE enrollments
53
+ ADD COLUMN authority_scope text NOT NULL DEFAULT 'workspace',
54
+ ADD COLUMN authority_id uuid,
55
+ ADD COLUMN owner_organization_membership_id uuid,
56
+ ADD COLUMN origin_workspace_id uuid,
57
+ ADD COLUMN generation bigint NOT NULL DEFAULT 1;
58
+ UPDATE enrollments SET origin_workspace_id = workspace_id WHERE origin_workspace_id IS NULL;
59
+ ALTER TABLE enrollments ADD CONSTRAINT enrollments_authority_scope_check
60
+ CHECK (authority_scope IN ('organization', 'workspace', 'user')) NOT VALID;
61
+ ALTER TABLE enrollments ADD CONSTRAINT enrollments_authority_shape_check CHECK (
62
+ (authority_scope IN ('organization', 'workspace') AND authority_id IS NULL
63
+ AND owner_organization_membership_id IS NULL)
64
+ OR (authority_scope = 'user' AND authority_id IS NOT NULL
65
+ AND owner_organization_membership_id IS NOT NULL)
66
+ ) NOT VALID;
67
+ ALTER TABLE enrollments ADD CONSTRAINT enrollments_generation_check
68
+ CHECK (generation > 0) NOT VALID;
69
+ ALTER TABLE enrollments ADD CONSTRAINT enrollments_authority_fk
70
+ FOREIGN KEY (authority_id, account_id, owner_organization_membership_id)
71
+ REFERENCES organization_user_resource_authorities(
72
+ id, account_id, organization_membership_id
73
+ ) ON DELETE RESTRICT NOT VALID;
74
+ ALTER TABLE enrollments ADD CONSTRAINT enrollments_origin_workspace_fk
75
+ FOREIGN KEY (origin_workspace_id, account_id)
76
+ REFERENCES workspaces(id, account_id) ON DELETE SET NULL (origin_workspace_id) NOT VALID;
77
+ ALTER TABLE enrollments VALIDATE CONSTRAINT enrollments_authority_scope_check;
78
+ ALTER TABLE enrollments VALIDATE CONSTRAINT enrollments_authority_shape_check;
79
+ ALTER TABLE enrollments VALIDATE CONSTRAINT enrollments_generation_check;
80
+ ALTER TABLE enrollments VALIDATE CONSTRAINT enrollments_authority_fk;
81
+ ALTER TABLE enrollments VALIDATE CONSTRAINT enrollments_origin_workspace_fk;
82
+ DROP INDEX enrollments_workspace_pubkey_idx;
83
+ CREATE UNIQUE INDEX enrollments_workspace_pubkey_idx
84
+ ON enrollments(workspace_id, pubkey)
85
+ WHERE authority_scope = 'workspace';
86
+ CREATE UNIQUE INDEX enrollments_organization_pubkey_idx
87
+ ON enrollments(account_id, pubkey)
88
+ WHERE authority_scope = 'organization';
89
+ CREATE UNIQUE INDEX enrollments_user_pubkey_idx
90
+ ON enrollments(account_id, owner_organization_membership_id, pubkey)
91
+ WHERE authority_scope = 'user';
92
+
93
+ CREATE TABLE opengeni_private.scoped_compute_capabilities (
94
+ backend_pid integer NOT NULL,
95
+ transaction_id xid8 NOT NULL,
96
+ capability_kind text NOT NULL,
97
+ created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
98
+ CONSTRAINT scoped_compute_capabilities_kind_check
99
+ CHECK (capability_kind IN ('read', 'write', 'runtime')),
100
+ CONSTRAINT scoped_compute_capabilities_pk
101
+ PRIMARY KEY (backend_pid, transaction_id, capability_kind)
102
+ );
103
+ REVOKE ALL ON TABLE opengeni_private.scoped_compute_capabilities FROM PUBLIC;
104
+
105
+ CREATE OR REPLACE FUNCTION opengeni_private.scoped_compute_capability_active(
106
+ p_capability_kind text DEFAULT NULL
107
+ ) RETURNS boolean
108
+ LANGUAGE sql STABLE SECURITY DEFINER SET search_path = pg_catalog AS $$
109
+ SELECT EXISTS (
110
+ SELECT 1 FROM opengeni_private.scoped_compute_capabilities capability
111
+ WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
112
+ AND capability.transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
113
+ AND (p_capability_kind IS NULL OR capability.capability_kind = p_capability_kind)
114
+ )
115
+ $$;
116
+ REVOKE ALL ON FUNCTION opengeni_private.scoped_compute_capability_active(text) FROM PUBLIC;
117
+
118
+ -- RLS can also run under a supported non-bypass structural owner (for example
119
+ -- the workspace activity initializer), not only opengeni_app. PostgreSQL checks
120
+ -- every policy function's EXECUTE ACL before it can short-circuit a false owner
121
+ -- branch, so policies use this public, value-free guard rather than exposing the
122
+ -- private predicate itself. The caller supplies current_user from the policy;
123
+ -- even a direct spoof can only observe a same-transaction capability that the
124
+ -- caller cannot mint because the private table remains inaccessible.
125
+ CREATE OR REPLACE FUNCTION scoped_compute_policy_capability_active(
126
+ p_actor text, p_expected_owner text, p_capability_kind text DEFAULT NULL
127
+ ) RETURNS boolean
128
+ LANGUAGE sql STABLE SECURITY DEFINER SET search_path FROM CURRENT AS $$
129
+ SELECT p_actor = p_expected_owner
130
+ AND opengeni_private.scoped_compute_capability_active(p_capability_kind)
131
+ $$;
132
+ GRANT EXECUTE ON FUNCTION scoped_compute_policy_capability_active(text, text, text)
133
+ TO PUBLIC;
134
+
135
+ CREATE OR REPLACE FUNCTION sync_personal_enrollment_authority() RETURNS trigger
136
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
137
+ BEGIN
138
+ IF NEW.authority_scope <> 'user' OR (
139
+ NEW.generation = OLD.generation AND NEW.status = OLD.status
140
+ ) THEN
141
+ RETURN NEW;
142
+ END IF;
143
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
144
+ backend_pid, transaction_id, capability_kind
145
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
146
+ ON CONFLICT DO NOTHING;
147
+ UPDATE organization_user_resource_authorities
148
+ SET generation = NEW.generation, status = NEW.status,
149
+ revoked_at = CASE WHEN NEW.status = 'revoked' THEN coalesce(NEW.revoked_at, clock_timestamp())
150
+ ELSE NULL END,
151
+ updated_at = clock_timestamp()
152
+ WHERE id = NEW.authority_id AND account_id = NEW.account_id
153
+ AND organization_membership_id = NEW.owner_organization_membership_id
154
+ AND resource_kind = 'connected_machine' AND resource_id = NEW.id;
155
+ IF NOT FOUND THEN
156
+ RAISE EXCEPTION 'personal machine authority is missing' USING ERRCODE = '42501';
157
+ END IF;
158
+ UPDATE organization_user_resource_grants
159
+ SET status = 'revoked', revoked_at = clock_timestamp(),
160
+ generation = generation + 1, updated_at = clock_timestamp()
161
+ WHERE authority_id = NEW.authority_id AND account_id = NEW.account_id
162
+ AND status = 'active';
163
+ DELETE FROM opengeni_private.scoped_compute_capabilities
164
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
165
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
166
+ AND capability_kind = 'write';
167
+ RETURN NEW;
168
+ EXCEPTION WHEN OTHERS THEN
169
+ DELETE FROM opengeni_private.scoped_compute_capabilities
170
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
171
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
172
+ RAISE;
173
+ END
174
+ $$;
175
+ CREATE TRIGGER sync_personal_enrollment_authority_after_update
176
+ AFTER UPDATE OF generation, status ON enrollments
177
+ FOR EACH ROW EXECUTE FUNCTION sync_personal_enrollment_authority();
178
+ REVOKE ALL ON FUNCTION sync_personal_enrollment_authority() FROM PUBLIC;
179
+
180
+ DO $policies$
181
+ DECLARE
182
+ data_schema text := current_schema();
183
+ migration_owner text := current_user;
184
+ table_name text;
185
+ BEGIN
186
+ FOREACH table_name IN ARRAY ARRAY[
187
+ 'rigs', 'rig_versions', 'rig_changes', 'enrollments', 'sandboxes',
188
+ 'organization_memberships', 'workspace_memberships',
189
+ 'organization_user_resource_authorities', 'organization_user_resource_grants',
190
+ 'sessions', 'workspace_session_activity_revisions', 'session_turns', 'session_turn_attempts',
191
+ 'session_attempt_interruptions', 'session_attempt_personal_resource_admissions',
192
+ 'session_attempt_personal_resource_snapshots',
193
+ 'personal_resource_once_consumption_receipts'
194
+ ] LOOP
195
+ EXECUTE format(
196
+ 'CREATE POLICY scoped_compute_capability_read ON %I.%I FOR SELECT USING '
197
+ || '(scoped_compute_policy_capability_active(current_user, %L, NULL))',
198
+ data_schema, table_name, migration_owner
199
+ );
200
+ END LOOP;
201
+ FOREACH table_name IN ARRAY ARRAY[
202
+ 'rigs', 'rig_versions', 'enrollments', 'sandboxes', 'sessions',
203
+ 'workspace_session_activity_revisions',
204
+ 'organization_user_resource_authorities', 'organization_user_resource_grants'
205
+ ] LOOP
206
+ EXECUTE format(
207
+ 'CREATE POLICY scoped_compute_capability_insert ON %I.%I FOR INSERT WITH CHECK '
208
+ || '(scoped_compute_policy_capability_active(current_user, %L, ''write''))',
209
+ data_schema, table_name, migration_owner
210
+ );
211
+ EXECUTE format(
212
+ 'CREATE POLICY scoped_compute_capability_update ON %I.%I FOR UPDATE USING '
213
+ || '(scoped_compute_policy_capability_active(current_user, %L, ''write'')) '
214
+ || 'WITH CHECK (scoped_compute_policy_capability_active('
215
+ || 'current_user, %L, ''write''))',
216
+ data_schema, table_name, migration_owner, migration_owner
217
+ );
218
+ END LOOP;
219
+ END
220
+ $policies$;
221
+
222
+ CREATE OR REPLACE FUNCTION scoped_compute_actor_membership(
223
+ p_account_id uuid,
224
+ p_workspace_id uuid
225
+ ) RETURNS uuid
226
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
227
+ DECLARE
228
+ caller_subject text := nullif(pg_catalog.current_setting('opengeni.subject_id', true), '');
229
+ membership_id uuid;
230
+ personal_workspace_id uuid;
231
+ BEGIN
232
+ IF p_account_id IS DISTINCT FROM nullif(
233
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
234
+ OR p_workspace_id IS DISTINCT FROM nullif(
235
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
236
+ THEN
237
+ RAISE EXCEPTION 'scoped compute actor scope mismatch' USING ERRCODE = '42501';
238
+ END IF;
239
+ SELECT membership.id, membership.personal_workspace_id
240
+ INTO membership_id, personal_workspace_id
241
+ FROM organization_memberships membership
242
+ WHERE membership.account_id = p_account_id
243
+ AND membership.subject_id = caller_subject
244
+ AND membership.status = 'active' AND membership.revoked_at IS NULL
245
+ FOR SHARE;
246
+ IF membership_id IS NULL THEN RETURN NULL; END IF;
247
+ IF personal_workspace_id IS DISTINCT FROM p_workspace_id THEN
248
+ PERFORM 1 FROM workspace_memberships workspace_membership
249
+ WHERE workspace_membership.account_id = p_account_id
250
+ AND workspace_membership.workspace_id = p_workspace_id
251
+ AND workspace_membership.subject_id = caller_subject
252
+ FOR KEY SHARE;
253
+ IF NOT FOUND THEN
254
+ RAISE EXCEPTION 'scoped compute actor lacks current workspace access'
255
+ USING ERRCODE = '42501';
256
+ END IF;
257
+ END IF;
258
+ RETURN membership_id;
259
+ END
260
+ $$;
261
+ REVOKE ALL ON FUNCTION scoped_compute_actor_membership(uuid, uuid) FROM PUBLIC;
262
+
263
+ CREATE OR REPLACE FUNCTION scoped_rig_json(p_rig_id uuid) RETURNS jsonb
264
+ LANGUAGE sql STABLE SECURITY DEFINER SET search_path FROM CURRENT AS $$
265
+ SELECT pg_catalog.jsonb_build_object(
266
+ 'id', rig.id, 'accountId', rig.account_id, 'workspaceId', rig.workspace_id,
267
+ 'scope', rig.authority_scope, 'generation', rig.generation, 'status', rig.status,
268
+ 'name', rig.name, 'description', rig.description, 'createdBy', rig.created_by,
269
+ 'activeVersion', (
270
+ SELECT pg_catalog.jsonb_build_object(
271
+ 'id', version.id, 'rigId', version.rig_id, 'version', version.version,
272
+ 'image', version.image, 'setupScript', version.setup_script,
273
+ 'checks', version.checks, 'credentialHooks', version.credential_hooks,
274
+ 'defaultVariableSetIds', version.default_variable_set_ids,
275
+ 'changelog', version.changelog, 'providerImages', version.provider_images,
276
+ 'createdBy', version.created_by, 'active', version.active,
277
+ 'createdAt', version.created_at
278
+ ) FROM rig_versions version
279
+ WHERE version.rig_id = rig.id AND version.account_id = rig.account_id
280
+ AND version.active = true LIMIT 1
281
+ ),
282
+ 'activeVersionHealth', CASE WHEN EXISTS (
283
+ SELECT 1 FROM rig_versions version
284
+ WHERE version.rig_id = rig.id AND version.account_id = rig.account_id
285
+ AND version.active = true
286
+ ) THEN pg_catalog.jsonb_build_object('checkHealth', 'unknown', 'lastVerifiedAt', NULL)
287
+ ELSE NULL END,
288
+ 'versionCount', (SELECT count(*)::integer FROM rig_versions version
289
+ WHERE version.rig_id = rig.id AND version.account_id = rig.account_id),
290
+ 'createdAt', rig.created_at, 'updatedAt', rig.updated_at
291
+ ) FROM rigs rig WHERE rig.id = p_rig_id
292
+ $$;
293
+ REVOKE ALL ON FUNCTION scoped_rig_json(uuid) FROM PUBLIC;
294
+
295
+ CREATE OR REPLACE FUNCTION list_scoped_rigs(
296
+ p_account_id uuid,
297
+ p_workspace_id uuid,
298
+ p_rig_id uuid DEFAULT NULL,
299
+ p_name text DEFAULT NULL,
300
+ p_scope text DEFAULT NULL
301
+ ) RETURNS SETOF jsonb
302
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
303
+ DECLARE
304
+ actor_membership_id uuid;
305
+ rig_row record;
306
+ BEGIN
307
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
308
+ backend_pid, transaction_id, capability_kind
309
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
310
+ ON CONFLICT DO NOTHING;
311
+ actor_membership_id := scoped_compute_actor_membership(p_account_id, p_workspace_id);
312
+ IF p_scope IS NOT NULL AND p_scope NOT IN ('organization', 'workspace', 'user') THEN
313
+ RAISE EXCEPTION 'invalid rig scope' USING ERRCODE = '22023';
314
+ END IF;
315
+ FOR rig_row IN
316
+ SELECT rig.id FROM rigs rig
317
+ WHERE rig.account_id = p_account_id AND rig.status = 'active'
318
+ AND (p_rig_id IS NULL OR rig.id = p_rig_id)
319
+ AND (p_name IS NULL OR rig.name = p_name)
320
+ AND (p_scope IS NULL OR rig.authority_scope = p_scope)
321
+ AND (
322
+ rig.authority_scope = 'organization'
323
+ OR (rig.authority_scope = 'workspace' AND rig.workspace_id = p_workspace_id)
324
+ OR (rig.authority_scope = 'user'
325
+ AND rig.owner_organization_membership_id = actor_membership_id)
326
+ )
327
+ ORDER BY CASE rig.authority_scope WHEN 'user' THEN 1 WHEN 'workspace' THEN 2 ELSE 3 END,
328
+ rig.created_at, rig.id
329
+ LOOP
330
+ RETURN NEXT scoped_rig_json(rig_row.id);
331
+ END LOOP;
332
+ DELETE FROM opengeni_private.scoped_compute_capabilities
333
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
334
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
335
+ AND capability_kind = 'read';
336
+ RETURN;
337
+ EXCEPTION WHEN OTHERS THEN
338
+ DELETE FROM opengeni_private.scoped_compute_capabilities
339
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
340
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
341
+ AND capability_kind = 'read';
342
+ RAISE;
343
+ END
344
+ $$;
345
+ REVOKE ALL ON FUNCTION list_scoped_rigs(uuid, uuid, uuid, text, text) FROM PUBLIC;
346
+
347
+ CREATE OR REPLACE FUNCTION count_scoped_rigs(
348
+ p_account_id uuid, p_workspace_id uuid, p_scope text
349
+ ) RETURNS integer
350
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
351
+ DECLARE actor_membership_id uuid; result_count integer;
352
+ BEGIN
353
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
354
+ backend_pid, transaction_id, capability_kind
355
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
356
+ ON CONFLICT DO NOTHING;
357
+ actor_membership_id := scoped_compute_actor_membership(p_account_id, p_workspace_id);
358
+ IF p_scope NOT IN ('organization', 'workspace', 'user') THEN
359
+ RAISE EXCEPTION 'invalid rig scope' USING ERRCODE = '22023';
360
+ END IF;
361
+ SELECT count(*)::integer INTO result_count FROM rigs rig
362
+ WHERE rig.account_id = p_account_id AND rig.status = 'active'
363
+ AND rig.authority_scope = p_scope
364
+ AND (p_scope = 'organization'
365
+ OR (p_scope = 'workspace' AND rig.workspace_id = p_workspace_id)
366
+ OR (p_scope = 'user' AND rig.owner_organization_membership_id = actor_membership_id));
367
+ DELETE FROM opengeni_private.scoped_compute_capabilities
368
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
369
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
370
+ AND capability_kind = 'read';
371
+ RETURN result_count;
372
+ EXCEPTION WHEN OTHERS THEN
373
+ DELETE FROM opengeni_private.scoped_compute_capabilities
374
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
375
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
376
+ AND capability_kind = 'read';
377
+ RAISE;
378
+ END
379
+ $$;
380
+ REVOKE ALL ON FUNCTION count_scoped_rigs(uuid, uuid, text) FROM PUBLIC;
381
+
382
+ CREATE OR REPLACE FUNCTION create_scoped_rig(
383
+ p_account_id uuid,
384
+ p_workspace_id uuid,
385
+ p_scope text,
386
+ p_name text,
387
+ p_description text,
388
+ p_created_by text,
389
+ p_initial_version jsonb,
390
+ p_allow_organization boolean DEFAULT false
391
+ ) RETURNS jsonb
392
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
393
+ DECLARE
394
+ owner_membership organization_memberships%ROWTYPE;
395
+ caller_subject text := nullif(pg_catalog.current_setting('opengeni.subject_id', true), '');
396
+ rig_id uuid := pg_catalog.gen_random_uuid();
397
+ authority_id uuid;
398
+ result jsonb;
399
+ BEGIN
400
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
401
+ backend_pid, transaction_id, capability_kind
402
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
403
+ ON CONFLICT DO NOTHING;
404
+ IF p_account_id IS DISTINCT FROM nullif(
405
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
406
+ OR p_workspace_id IS DISTINCT FROM nullif(
407
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
408
+ OR p_scope NOT IN ('organization', 'workspace', 'user')
409
+ THEN RAISE EXCEPTION 'invalid scoped rig creation request' USING ERRCODE = '42501';
410
+ END IF;
411
+ IF p_scope = 'organization' AND NOT p_allow_organization THEN
412
+ RAISE EXCEPTION 'organization rig creation requires account authority'
413
+ USING ERRCODE = '42501';
414
+ ELSIF p_scope = 'user' THEN
415
+ SELECT membership.* INTO STRICT owner_membership
416
+ FROM organization_memberships membership
417
+ WHERE membership.account_id = p_account_id AND membership.subject_id = caller_subject
418
+ AND membership.status = 'active' AND membership.revoked_at IS NULL FOR SHARE;
419
+ IF owner_membership.personal_workspace_id IS DISTINCT FROM p_workspace_id THEN
420
+ PERFORM 1 FROM workspace_memberships workspace_membership
421
+ WHERE workspace_membership.account_id = p_account_id
422
+ AND workspace_membership.workspace_id = p_workspace_id
423
+ AND workspace_membership.subject_id = caller_subject FOR KEY SHARE;
424
+ IF NOT FOUND THEN RAISE EXCEPTION 'rig owner lacks workspace access'
425
+ USING ERRCODE = '42501'; END IF;
426
+ END IF;
427
+ authority_id := pg_catalog.gen_random_uuid();
428
+ INSERT INTO organization_user_resource_authorities(
429
+ id, account_id, organization_membership_id, resource_kind, resource_id,
430
+ origin_workspace_id, generation, status
431
+ ) VALUES (authority_id, p_account_id, owner_membership.id, 'rig', rig_id,
432
+ p_workspace_id, 1, 'active');
433
+ END IF;
434
+ INSERT INTO rigs(
435
+ id, account_id, workspace_id, name, description, created_by, authority_scope,
436
+ authority_id, owner_organization_membership_id, origin_workspace_id,
437
+ generation, status
438
+ ) VALUES (
439
+ rig_id, p_account_id, p_workspace_id, p_name, p_description, p_created_by, p_scope,
440
+ authority_id, CASE WHEN p_scope = 'user' THEN owner_membership.id ELSE NULL END,
441
+ p_workspace_id, 1, 'active'
442
+ );
443
+ INSERT INTO rig_versions(
444
+ account_id, workspace_id, rig_id, version, image, setup_script, checks,
445
+ credential_hooks, default_variable_set_ids, changelog, provider_images,
446
+ created_by, active
447
+ ) VALUES (
448
+ p_account_id, p_workspace_id, rig_id, 1, p_initial_version ->> 'image',
449
+ p_initial_version ->> 'setupScript', coalesce(p_initial_version -> 'checks', '[]'::jsonb),
450
+ coalesce(p_initial_version -> 'credentialHooks', '[]'::jsonb),
451
+ coalesce(p_initial_version -> 'defaultVariableSetIds', '[]'::jsonb),
452
+ p_initial_version ->> 'changelog', '{}'::jsonb,
453
+ coalesce(p_initial_version ->> 'createdBy', p_created_by), true
454
+ );
455
+ DELETE FROM opengeni_private.scoped_compute_capabilities
456
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
457
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
458
+ AND capability_kind = 'write';
459
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
460
+ backend_pid, transaction_id, capability_kind
461
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
462
+ ON CONFLICT DO NOTHING;
463
+ result := scoped_rig_json(rig_id);
464
+ DELETE FROM opengeni_private.scoped_compute_capabilities
465
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
466
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
467
+ AND capability_kind = 'read';
468
+ RETURN result;
469
+ EXCEPTION WHEN OTHERS THEN
470
+ DELETE FROM opengeni_private.scoped_compute_capabilities
471
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
472
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
473
+ RAISE;
474
+ END
475
+ $$;
476
+ REVOKE ALL ON FUNCTION create_scoped_rig(
477
+ uuid, uuid, text, text, text, text, jsonb, boolean
478
+ ) FROM PUBLIC;
479
+
480
+ CREATE OR REPLACE FUNCTION mutate_scoped_rig(
481
+ p_account_id uuid, p_workspace_id uuid, p_rig_id uuid, p_operation text,
482
+ p_name text DEFAULT NULL, p_name_present boolean DEFAULT false,
483
+ p_description text DEFAULT NULL, p_description_present boolean DEFAULT false,
484
+ p_allow_organization boolean DEFAULT false
485
+ ) RETURNS jsonb
486
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
487
+ DECLARE
488
+ actor_membership_id uuid;
489
+ rig_row rigs%ROWTYPE;
490
+ active_sessions integer;
491
+ result jsonb;
492
+ BEGIN
493
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
494
+ backend_pid, transaction_id, capability_kind
495
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
496
+ ON CONFLICT DO NOTHING;
497
+ actor_membership_id := scoped_compute_actor_membership(p_account_id, p_workspace_id);
498
+ SELECT rig.* INTO STRICT rig_row FROM rigs rig
499
+ WHERE rig.id = p_rig_id AND rig.account_id = p_account_id AND rig.status = 'active'
500
+ AND ((rig.authority_scope = 'organization' AND p_allow_organization)
501
+ OR (rig.authority_scope = 'workspace' AND rig.workspace_id = p_workspace_id)
502
+ OR (rig.authority_scope = 'user'
503
+ AND rig.owner_organization_membership_id = actor_membership_id))
504
+ FOR UPDATE;
505
+ IF p_operation = 'update' THEN
506
+ UPDATE rigs SET name = CASE WHEN p_name_present THEN p_name ELSE name END,
507
+ description = CASE WHEN p_description_present THEN p_description ELSE description END,
508
+ updated_at = clock_timestamp() WHERE id = p_rig_id;
509
+ ELSIF p_operation = 'revoke' THEN
510
+ SELECT count(*)::integer INTO active_sessions FROM sessions session_value
511
+ WHERE session_value.account_id = p_account_id AND session_value.rig_id = p_rig_id
512
+ AND session_value.status IN ('queued','running','requires_action','recovering','waiting_capacity');
513
+ IF active_sessions > 0 THEN
514
+ RAISE EXCEPTION 'rig remains attached to % active sessions', active_sessions
515
+ USING ERRCODE = '23503';
516
+ END IF;
517
+ UPDATE rigs SET status = 'revoked', revoked_at = clock_timestamp(),
518
+ generation = generation + 1, updated_at = clock_timestamp() WHERE id = p_rig_id;
519
+ IF rig_row.authority_scope = 'user' THEN
520
+ UPDATE organization_user_resource_authorities SET status = 'revoked',
521
+ revoked_at = clock_timestamp(), generation = generation + 1,
522
+ updated_at = clock_timestamp()
523
+ WHERE id = rig_row.authority_id AND account_id = p_account_id AND status = 'active';
524
+ UPDATE organization_user_resource_grants SET status = 'revoked',
525
+ revoked_at = clock_timestamp(), generation = generation + 1,
526
+ updated_at = clock_timestamp()
527
+ WHERE authority_id = rig_row.authority_id AND account_id = p_account_id
528
+ AND status = 'active';
529
+ END IF;
530
+ ELSE RAISE EXCEPTION 'invalid rig mutation' USING ERRCODE = '22023'; END IF;
531
+ DELETE FROM opengeni_private.scoped_compute_capabilities
532
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
533
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
534
+ AND capability_kind = 'write';
535
+ IF p_operation = 'revoke' THEN RETURN NULL; END IF;
536
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
537
+ backend_pid, transaction_id, capability_kind
538
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
539
+ ON CONFLICT DO NOTHING;
540
+ result := scoped_rig_json(p_rig_id);
541
+ DELETE FROM opengeni_private.scoped_compute_capabilities
542
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
543
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
544
+ AND capability_kind = 'read';
545
+ RETURN result;
546
+ EXCEPTION WHEN OTHERS THEN
547
+ DELETE FROM opengeni_private.scoped_compute_capabilities
548
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
549
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
550
+ RAISE;
551
+ END
552
+ $$;
553
+ REVOKE ALL ON FUNCTION mutate_scoped_rig(
554
+ uuid, uuid, uuid, text, text, boolean, text, boolean, boolean
555
+ ) FROM PUBLIC;
556
+
557
+ CREATE OR REPLACE FUNCTION finalize_scoped_enrollment(
558
+ p_account_id uuid, p_workspace_id uuid, p_scope text, p_pubkey text,
559
+ p_has_display boolean, p_allow_screen_control boolean, p_os text, p_arch text,
560
+ p_sandbox_name text, p_allow_organization boolean DEFAULT false
561
+ ) RETURNS TABLE(enrollment_id uuid, sandbox_id uuid)
562
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
563
+ DECLARE
564
+ caller_subject text := nullif(pg_catalog.current_setting('opengeni.subject_id', true), '');
565
+ owner_membership organization_memberships%ROWTYPE;
566
+ existing_enrollment enrollments%ROWTYPE;
567
+ created_enrollment_id uuid := pg_catalog.gen_random_uuid();
568
+ created_authority_id uuid;
569
+ selected_enrollment_id uuid;
570
+ selected_sandbox_id uuid;
571
+ BEGIN
572
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
573
+ backend_pid, transaction_id, capability_kind
574
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
575
+ ON CONFLICT DO NOTHING;
576
+ IF p_account_id IS DISTINCT FROM nullif(
577
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
578
+ OR p_workspace_id IS DISTINCT FROM nullif(
579
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
580
+ OR p_scope NOT IN ('organization', 'workspace', 'user')
581
+ THEN RAISE EXCEPTION 'invalid scoped enrollment request' USING ERRCODE = '42501';
582
+ END IF;
583
+ IF p_scope = 'organization' AND NOT p_allow_organization THEN
584
+ RAISE EXCEPTION 'organization machine enrollment requires account authority'
585
+ USING ERRCODE = '42501';
586
+ ELSIF p_scope = 'user' THEN
587
+ SELECT membership.* INTO STRICT owner_membership
588
+ FROM organization_memberships membership
589
+ WHERE membership.account_id = p_account_id AND membership.subject_id = caller_subject
590
+ AND membership.status = 'active' AND membership.revoked_at IS NULL FOR SHARE;
591
+ IF owner_membership.personal_workspace_id IS DISTINCT FROM p_workspace_id THEN
592
+ PERFORM 1 FROM workspace_memberships workspace_membership
593
+ WHERE workspace_membership.account_id = p_account_id
594
+ AND workspace_membership.workspace_id = p_workspace_id
595
+ AND workspace_membership.subject_id = caller_subject FOR KEY SHARE;
596
+ IF NOT FOUND THEN RAISE EXCEPTION 'machine owner lacks workspace access'
597
+ USING ERRCODE = '42501'; END IF;
598
+ END IF;
599
+ END IF;
600
+
601
+ SELECT enrollment.* INTO existing_enrollment FROM enrollments enrollment
602
+ WHERE enrollment.account_id = p_account_id
603
+ AND enrollment.authority_scope = p_scope
604
+ AND enrollment.pubkey = p_pubkey
605
+ AND ((p_scope = 'organization')
606
+ OR (p_scope = 'workspace' AND enrollment.workspace_id = p_workspace_id)
607
+ OR (p_scope = 'user'
608
+ AND enrollment.owner_organization_membership_id = owner_membership.id))
609
+ ORDER BY enrollment.created_at DESC LIMIT 1 FOR UPDATE;
610
+
611
+ IF existing_enrollment.id IS NULL THEN
612
+ IF p_scope = 'user' THEN
613
+ created_authority_id := pg_catalog.gen_random_uuid();
614
+ INSERT INTO organization_user_resource_authorities(
615
+ id, account_id, organization_membership_id, resource_kind, resource_id,
616
+ origin_workspace_id, generation, status
617
+ ) VALUES (created_authority_id, p_account_id, owner_membership.id,
618
+ 'connected_machine', created_enrollment_id, p_workspace_id, 1, 'active');
619
+ END IF;
620
+ INSERT INTO enrollments(
621
+ id, account_id, workspace_id, pubkey, exposure, has_display,
622
+ allow_screen_control, os, arch, status, authority_scope, authority_id,
623
+ owner_organization_membership_id, origin_workspace_id, generation
624
+ ) VALUES (
625
+ created_enrollment_id, p_account_id, p_workspace_id, p_pubkey,
626
+ 'whole-machine', p_has_display, p_allow_screen_control, p_os, p_arch, 'active',
627
+ p_scope, created_authority_id,
628
+ CASE WHEN p_scope = 'user' THEN owner_membership.id ELSE NULL END,
629
+ p_workspace_id, 1
630
+ );
631
+ selected_enrollment_id := created_enrollment_id;
632
+ ELSE
633
+ UPDATE enrollments SET exposure = 'whole-machine', has_display = p_has_display,
634
+ allow_screen_control = p_allow_screen_control, os = p_os, arch = p_arch,
635
+ status = 'active', revoked_at = NULL,
636
+ credential_generation = credential_generation + 1,
637
+ generation = generation + 1, connection_instance_id = NULL,
638
+ connection_lease_expires_at = NULL, updated_at = clock_timestamp()
639
+ WHERE id = existing_enrollment.id;
640
+ selected_enrollment_id := existing_enrollment.id;
641
+ END IF;
642
+
643
+ SELECT sandbox.id INTO selected_sandbox_id FROM sandboxes sandbox
644
+ WHERE sandbox.account_id = p_account_id
645
+ AND sandbox.enrollment_id = selected_enrollment_id
646
+ ORDER BY sandbox.created_at LIMIT 1 FOR SHARE;
647
+ IF selected_sandbox_id IS NULL THEN
648
+ selected_sandbox_id := pg_catalog.gen_random_uuid();
649
+ INSERT INTO sandboxes(id, account_id, workspace_id, kind, name, enrollment_id)
650
+ VALUES (selected_sandbox_id, p_account_id, p_workspace_id, 'selfhosted',
651
+ p_sandbox_name, selected_enrollment_id);
652
+ END IF;
653
+ enrollment_id := selected_enrollment_id;
654
+ sandbox_id := selected_sandbox_id;
655
+ DELETE FROM opengeni_private.scoped_compute_capabilities
656
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
657
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
658
+ AND capability_kind = 'write';
659
+ RETURN NEXT;
660
+ RETURN;
661
+ EXCEPTION WHEN OTHERS THEN
662
+ DELETE FROM opengeni_private.scoped_compute_capabilities
663
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
664
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
665
+ RAISE;
666
+ END
667
+ $$;
668
+ REVOKE ALL ON FUNCTION finalize_scoped_enrollment(
669
+ uuid, uuid, text, text, boolean, boolean, text, text, text, boolean
670
+ ) FROM PUBLIC;
671
+
672
+ CREATE OR REPLACE FUNCTION list_scoped_enrollments(
673
+ p_account_id uuid, p_workspace_id uuid, p_enrollment_id uuid DEFAULT NULL,
674
+ p_status text DEFAULT 'active'
675
+ ) RETURNS SETOF jsonb
676
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
677
+ DECLARE actor_membership_id uuid; enrollment_row record;
678
+ BEGIN
679
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
680
+ backend_pid, transaction_id, capability_kind
681
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
682
+ ON CONFLICT DO NOTHING;
683
+ actor_membership_id := scoped_compute_actor_membership(p_account_id, p_workspace_id);
684
+ IF p_status NOT IN ('active', 'revoked') THEN
685
+ RAISE EXCEPTION 'invalid enrollment status' USING ERRCODE = '22023';
686
+ END IF;
687
+ FOR enrollment_row IN
688
+ SELECT enrollment.*, sandbox.id AS sandbox_id, sandbox.name AS sandbox_name
689
+ FROM enrollments enrollment
690
+ LEFT JOIN LATERAL (
691
+ SELECT value.id, value.name FROM sandboxes value
692
+ WHERE value.enrollment_id = enrollment.id AND value.account_id = enrollment.account_id
693
+ ORDER BY value.created_at LIMIT 1
694
+ ) sandbox ON true
695
+ WHERE enrollment.account_id = p_account_id AND enrollment.status = p_status
696
+ AND (p_enrollment_id IS NULL OR enrollment.id = p_enrollment_id)
697
+ AND (enrollment.authority_scope = 'organization'
698
+ OR (enrollment.authority_scope = 'workspace'
699
+ AND enrollment.workspace_id = p_workspace_id)
700
+ OR (enrollment.authority_scope = 'user'
701
+ AND enrollment.owner_organization_membership_id = actor_membership_id))
702
+ ORDER BY enrollment.created_at DESC, enrollment.id
703
+ LOOP
704
+ RETURN NEXT pg_catalog.jsonb_build_object(
705
+ 'id', enrollment_row.id, 'accountId', enrollment_row.account_id,
706
+ 'workspaceId', enrollment_row.workspace_id, 'scope', enrollment_row.authority_scope,
707
+ 'generation', enrollment_row.generation, 'pubkey', enrollment_row.pubkey,
708
+ 'exposure', enrollment_row.exposure, 'hasDisplay', enrollment_row.has_display,
709
+ 'opStream', enrollment_row.op_stream,
710
+ 'desktopUnavailableReason', enrollment_row.desktop_unavailable_reason,
711
+ 'allowScreenControl', enrollment_row.allow_screen_control,
712
+ 'operationPolicy', pg_catalog.jsonb_build_object(
713
+ 'memoryMaxBytes', enrollment_row.operation_memory_max_bytes,
714
+ 'memoryHighBytes', enrollment_row.operation_memory_high_bytes,
715
+ 'cpuMaxMillicores', enrollment_row.operation_cpu_max_millicores,
716
+ 'revision', enrollment_row.operation_policy_revision,
717
+ 'updatedAt', enrollment_row.operation_policy_updated_at
718
+ ),
719
+ 'status', enrollment_row.status,
720
+ 'credentialGeneration', enrollment_row.credential_generation,
721
+ 'connectionInstanceId', enrollment_row.connection_instance_id,
722
+ 'connectionGeneration', enrollment_row.connection_generation,
723
+ 'connectionLeaseExpiresAt', enrollment_row.connection_lease_expires_at,
724
+ 'connectionDuplicateDeniedCount', enrollment_row.connection_duplicate_denied_count,
725
+ 'connectionDuplicateDeniedAt', enrollment_row.connection_duplicate_denied_at,
726
+ 'os', enrollment_row.os, 'arch', enrollment_row.arch,
727
+ 'lastSeenAt', enrollment_row.last_seen_at,
728
+ 'wentOfflineAt', enrollment_row.went_offline_at,
729
+ 'wentOfflineReason', enrollment_row.went_offline_reason,
730
+ 'agentVersion', enrollment_row.agent_version,
731
+ 'agentBinarySha256', enrollment_row.agent_binary_sha256,
732
+ 'agentUpdateChannel', enrollment_row.agent_update_channel,
733
+ 'agentCapabilities', enrollment_row.agent_capabilities,
734
+ 'agentUpdate', NULL, 'sandboxId', enrollment_row.sandbox_id,
735
+ 'sandboxName', enrollment_row.sandbox_name,
736
+ 'createdAt', enrollment_row.created_at, 'revokedAt', enrollment_row.revoked_at,
737
+ 'updatedAt', enrollment_row.updated_at
738
+ );
739
+ END LOOP;
740
+ DELETE FROM opengeni_private.scoped_compute_capabilities
741
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
742
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
743
+ AND capability_kind = 'read';
744
+ RETURN;
745
+ EXCEPTION WHEN OTHERS THEN
746
+ DELETE FROM opengeni_private.scoped_compute_capabilities
747
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
748
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
749
+ RAISE;
750
+ END
751
+ $$;
752
+ REVOKE ALL ON FUNCTION list_scoped_enrollments(uuid, uuid, uuid, text) FROM PUBLIC;
753
+
754
+ CREATE OR REPLACE FUNCTION get_scoped_sandbox(
755
+ p_account_id uuid, p_workspace_id uuid, p_sandbox_id uuid
756
+ ) RETURNS jsonb
757
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
758
+ DECLARE actor_membership_id uuid; result jsonb;
759
+ BEGIN
760
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
761
+ backend_pid, transaction_id, capability_kind
762
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
763
+ ON CONFLICT DO NOTHING;
764
+ actor_membership_id := scoped_compute_actor_membership(p_account_id, p_workspace_id);
765
+ SELECT pg_catalog.jsonb_build_object(
766
+ 'id', sandbox.id, 'accountId', sandbox.account_id,
767
+ 'workspaceId', sandbox.workspace_id, 'kind', sandbox.kind, 'name', sandbox.name,
768
+ 'enrollmentId', sandbox.enrollment_id, 'createdAt', sandbox.created_at,
769
+ 'updatedAt', sandbox.updated_at, 'scope', enrollment.authority_scope,
770
+ 'generation', enrollment.generation
771
+ ) INTO result
772
+ FROM sandboxes sandbox
773
+ LEFT JOIN enrollments enrollment
774
+ ON enrollment.id = sandbox.enrollment_id AND enrollment.account_id = sandbox.account_id
775
+ WHERE sandbox.id = p_sandbox_id AND sandbox.account_id = p_account_id
776
+ AND (
777
+ sandbox.kind <> 'selfhosted'
778
+ AND sandbox.workspace_id = p_workspace_id
779
+ OR sandbox.kind = 'selfhosted' AND enrollment.status = 'active' AND (
780
+ enrollment.authority_scope = 'organization'
781
+ OR (enrollment.authority_scope = 'workspace'
782
+ AND enrollment.workspace_id = p_workspace_id)
783
+ OR (enrollment.authority_scope = 'user'
784
+ AND enrollment.owner_organization_membership_id = actor_membership_id)
785
+ )
786
+ );
787
+ DELETE FROM opengeni_private.scoped_compute_capabilities
788
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
789
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
790
+ AND capability_kind = 'read';
791
+ RETURN result;
792
+ EXCEPTION WHEN OTHERS THEN
793
+ DELETE FROM opengeni_private.scoped_compute_capabilities
794
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
795
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
796
+ RAISE;
797
+ END
798
+ $$;
799
+ REVOKE ALL ON FUNCTION get_scoped_sandbox(uuid, uuid, uuid) FROM PUBLIC;
800
+
801
+ CREATE OR REPLACE FUNCTION authorize_scoped_sandbox_attach(
802
+ p_account_id uuid, p_workspace_id uuid, p_sandbox_id uuid
803
+ ) RETURNS boolean
804
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
805
+ DECLARE
806
+ actor_membership_id uuid;
807
+ sandbox_row sandboxes%ROWTYPE;
808
+ enrollment_row enrollments%ROWTYPE;
809
+ authorized boolean := false;
810
+ BEGIN
811
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
812
+ backend_pid, transaction_id, capability_kind
813
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
814
+ ON CONFLICT DO NOTHING;
815
+ actor_membership_id := scoped_compute_actor_membership(p_account_id, p_workspace_id);
816
+ SELECT sandbox.* INTO sandbox_row FROM sandboxes sandbox
817
+ WHERE sandbox.id = p_sandbox_id AND sandbox.account_id = p_account_id FOR SHARE;
818
+ IF sandbox_row.id IS NOT NULL AND sandbox_row.kind <> 'selfhosted' THEN
819
+ authorized := sandbox_row.workspace_id = p_workspace_id;
820
+ ELSIF sandbox_row.id IS NOT NULL THEN
821
+ SELECT enrollment.* INTO enrollment_row FROM enrollments enrollment
822
+ WHERE enrollment.id = sandbox_row.enrollment_id
823
+ AND enrollment.account_id = p_account_id AND enrollment.status = 'active'
824
+ FOR SHARE;
825
+ authorized := enrollment_row.id IS NOT NULL AND (
826
+ enrollment_row.authority_scope = 'organization'
827
+ OR (enrollment_row.authority_scope = 'workspace'
828
+ AND enrollment_row.workspace_id = p_workspace_id)
829
+ OR (enrollment_row.authority_scope = 'user'
830
+ AND enrollment_row.owner_organization_membership_id = actor_membership_id)
831
+ );
832
+ END IF;
833
+ DELETE FROM opengeni_private.scoped_compute_capabilities
834
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
835
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
836
+ AND capability_kind = 'read';
837
+ RETURN authorized;
838
+ EXCEPTION WHEN OTHERS THEN
839
+ DELETE FROM opengeni_private.scoped_compute_capabilities
840
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
841
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
842
+ RAISE;
843
+ END
844
+ $$;
845
+ REVOKE ALL ON FUNCTION authorize_scoped_sandbox_attach(uuid, uuid, uuid) FROM PUBLIC;
846
+
847
+ CREATE OR REPLACE FUNCTION materialize_scoped_rig_version_for_attempt(
848
+ p_account_id uuid, p_workspace_id uuid, p_session_id uuid, p_turn_id uuid,
849
+ p_attempt_id uuid, p_execution_generation integer
850
+ ) RETURNS jsonb
851
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
852
+ DECLARE
853
+ session_row sessions%ROWTYPE;
854
+ rig_row rigs%ROWTYPE;
855
+ version_row rig_versions%ROWTYPE;
856
+ initiating_subject text;
857
+ resolved_count integer;
858
+ result jsonb;
859
+ BEGIN
860
+ IF p_account_id IS DISTINCT FROM nullif(
861
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
862
+ OR p_workspace_id IS DISTINCT FROM nullif(
863
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
864
+ THEN RAISE EXCEPTION 'rig runtime scope mismatch' USING ERRCODE = '42501'; END IF;
865
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
866
+ backend_pid, transaction_id, capability_kind
867
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'runtime')
868
+ ON CONFLICT DO NOTHING;
869
+ SELECT session_value.* INTO STRICT session_row
870
+ FROM sessions session_value
871
+ JOIN session_turns turn_value ON turn_value.id = p_turn_id
872
+ AND turn_value.account_id = session_value.account_id
873
+ AND turn_value.workspace_id = session_value.workspace_id
874
+ AND turn_value.session_id = session_value.id
875
+ JOIN session_turn_attempts attempt ON attempt.id = p_attempt_id
876
+ AND attempt.account_id = session_value.account_id
877
+ AND attempt.workspace_id = session_value.workspace_id
878
+ AND attempt.session_id = session_value.id AND attempt.turn_id = turn_value.id
879
+ WHERE session_value.id = p_session_id AND session_value.account_id = p_account_id
880
+ AND session_value.workspace_id = p_workspace_id
881
+ AND session_value.active_turn_id = p_turn_id
882
+ AND turn_value.active_attempt_id = p_attempt_id
883
+ AND turn_value.execution_generation = p_execution_generation
884
+ AND turn_value.status = 'running'
885
+ AND attempt.execution_generation = p_execution_generation
886
+ AND attempt.state IN ('claimed','running') AND attempt.closed_at IS NULL
887
+ AND attempt.quiesced_at IS NULL
888
+ FOR SHARE OF session_value, turn_value, attempt;
889
+ initiating_subject := coalesce(
890
+ nullif(pg_catalog.current_setting('opengeni.initiating_human_subject_id', true), ''),
891
+ nullif(pg_catalog.current_setting('opengeni.subject_id', true), '')
892
+ );
893
+ SELECT rig.* INTO STRICT rig_row FROM rigs rig
894
+ WHERE rig.id = session_row.rig_id AND rig.account_id = p_account_id
895
+ AND rig.status = 'active' FOR SHARE;
896
+ SELECT version.* INTO STRICT version_row FROM rig_versions version
897
+ WHERE version.id = session_row.rig_version_id AND version.rig_id = rig_row.id
898
+ AND version.account_id = p_account_id FOR SHARE;
899
+ IF rig_row.authority_scope = 'workspace'
900
+ AND rig_row.workspace_id IS DISTINCT FROM p_workspace_id
901
+ THEN RAISE EXCEPTION 'workspace rig is outside the runtime workspace'
902
+ USING ERRCODE = '42501';
903
+ ELSIF rig_row.authority_scope = 'user' THEN
904
+ SELECT count(*)::integer INTO resolved_count
905
+ FROM resolve_session_attempt_personal_resources(
906
+ p_account_id, p_workspace_id, p_attempt_id
907
+ ) resolved
908
+ WHERE resolved.resource_kind = 'rig' AND resolved.resource_id = rig_row.id
909
+ AND resolved.resource_version_id = version_row.id
910
+ AND resolved.authority_id = rig_row.authority_id
911
+ AND resolved.authority_generation = rig_row.generation;
912
+ IF resolved_count <> 1 THEN
913
+ RAISE EXCEPTION 'personal rig grant is not exact or current'
914
+ USING ERRCODE = '42501';
915
+ END IF;
916
+ END IF;
917
+ result := pg_catalog.jsonb_build_object(
918
+ 'rigName', rig_row.name,
919
+ 'version', pg_catalog.jsonb_build_object(
920
+ 'id', version_row.id, 'rigId', version_row.rig_id,
921
+ 'version', version_row.version, 'image', version_row.image,
922
+ 'setupScript', version_row.setup_script, 'checks', version_row.checks,
923
+ 'credentialHooks', version_row.credential_hooks,
924
+ 'defaultVariableSetIds', version_row.default_variable_set_ids,
925
+ 'changelog', version_row.changelog, 'providerImages', version_row.provider_images,
926
+ 'createdBy', version_row.created_by, 'active', version_row.active,
927
+ 'createdAt', version_row.created_at
928
+ )
929
+ );
930
+ INSERT INTO audit_events(
931
+ account_id, workspace_id, subject_id, action, target_type, target_id,
932
+ metadata, metadata_codec_version
933
+ ) VALUES (
934
+ p_account_id, p_workspace_id, initiating_subject, 'rig.materialized', 'rig',
935
+ rig_row.id::text, pg_catalog.jsonb_build_object(
936
+ 'scope', rig_row.authority_scope, 'generation', rig_row.generation,
937
+ 'versionId', version_row.id, 'sessionId', p_session_id,
938
+ 'turnId', p_turn_id, 'attemptId', p_attempt_id,
939
+ 'executionGeneration', p_execution_generation
940
+ ), 1
941
+ );
942
+ DELETE FROM opengeni_private.scoped_compute_capabilities
943
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
944
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
945
+ AND capability_kind = 'runtime';
946
+ RETURN result;
947
+ EXCEPTION WHEN OTHERS THEN
948
+ DELETE FROM opengeni_private.scoped_compute_capabilities
949
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
950
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
951
+ RAISE;
952
+ END
953
+ $$;
954
+ REVOKE ALL ON FUNCTION materialize_scoped_rig_version_for_attempt(
955
+ uuid, uuid, uuid, uuid, uuid, integer
956
+ ) FROM PUBLIC;
957
+
958
+ CREATE TABLE session_attempt_connected_machine_authorizations (
959
+ attempt_id uuid NOT NULL REFERENCES session_turn_attempts(id) ON DELETE CASCADE,
960
+ account_id uuid NOT NULL,
961
+ workspace_id uuid NOT NULL,
962
+ session_id uuid NOT NULL,
963
+ turn_id uuid NOT NULL,
964
+ execution_generation integer NOT NULL,
965
+ initiating_human_subject_id text NOT NULL,
966
+ owner_organization_membership_id uuid NOT NULL,
967
+ membership_authorization_revision bigint NOT NULL,
968
+ enrollment_id uuid NOT NULL,
969
+ sandbox_id uuid NOT NULL,
970
+ authority_id uuid NOT NULL,
971
+ authority_generation bigint NOT NULL,
972
+ enrollment_generation bigint NOT NULL,
973
+ session_visibility text NOT NULL,
974
+ session_authority_epoch bigint NOT NULL,
975
+ grant_id uuid NOT NULL,
976
+ grant_generation bigint NOT NULL,
977
+ grant_mode text NOT NULL,
978
+ created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
979
+ PRIMARY KEY (attempt_id, enrollment_id),
980
+ CONSTRAINT session_attempt_connected_machine_attempt_fk FOREIGN KEY (
981
+ account_id, workspace_id, session_id, turn_id, attempt_id
982
+ ) REFERENCES session_turn_attempts(
983
+ account_id, workspace_id, session_id, turn_id, id
984
+ ) ON DELETE CASCADE,
985
+ CONSTRAINT session_attempt_connected_machine_owner_fk FOREIGN KEY (
986
+ owner_organization_membership_id, account_id
987
+ ) REFERENCES organization_memberships(id, account_id) ON DELETE RESTRICT,
988
+ CONSTRAINT session_attempt_connected_machine_authority_fk FOREIGN KEY (
989
+ authority_id, account_id, owner_organization_membership_id
990
+ ) REFERENCES organization_user_resource_authorities(
991
+ id, account_id, organization_membership_id
992
+ ) ON DELETE RESTRICT,
993
+ CONSTRAINT session_attempt_connected_machine_grant_fk FOREIGN KEY (
994
+ grant_id, account_id
995
+ ) REFERENCES organization_user_resource_grants(
996
+ id, account_id
997
+ ) ON DELETE RESTRICT
998
+ );
999
+ ALTER TABLE session_attempt_connected_machine_authorizations ENABLE ROW LEVEL SECURITY;
1000
+ ALTER TABLE session_attempt_connected_machine_authorizations FORCE ROW LEVEL SECURITY;
1001
+ CREATE POLICY scoped_compute_capability_read
1002
+ ON session_attempt_connected_machine_authorizations FOR SELECT
1003
+ USING (opengeni_private.scoped_compute_capability_active());
1004
+ CREATE POLICY scoped_compute_capability_insert
1005
+ ON session_attempt_connected_machine_authorizations FOR INSERT
1006
+ WITH CHECK (opengeni_private.scoped_compute_capability_active('write'));
1007
+
1008
+ CREATE OR REPLACE FUNCTION admit_session_attempt_personal_machine() RETURNS trigger
1009
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
1010
+ DECLARE
1011
+ session_row sessions%ROWTYPE;
1012
+ turn_row session_turns%ROWTYPE;
1013
+ enrollment_row enrollments%ROWTYPE;
1014
+ sandbox_row sandboxes%ROWTYPE;
1015
+ member_row organization_memberships%ROWTYPE;
1016
+ grant_row organization_user_resource_grants%ROWTYPE;
1017
+ initiating_subject text;
1018
+ affected integer;
1019
+ BEGIN
1020
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
1021
+ backend_pid, transaction_id, capability_kind
1022
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
1023
+ ON CONFLICT DO NOTHING;
1024
+ SELECT session_value.* INTO STRICT session_row FROM sessions session_value
1025
+ WHERE session_value.id = NEW.session_id AND session_value.account_id = NEW.account_id
1026
+ AND session_value.workspace_id = NEW.workspace_id FOR SHARE;
1027
+ IF session_row.active_sandbox_id IS NULL THEN
1028
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1029
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1030
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1031
+ RETURN NEW;
1032
+ END IF;
1033
+ SELECT sandbox.* INTO sandbox_row FROM sandboxes sandbox
1034
+ WHERE sandbox.id = session_row.active_sandbox_id AND sandbox.account_id = NEW.account_id
1035
+ AND sandbox.kind = 'selfhosted' FOR SHARE;
1036
+ IF sandbox_row.enrollment_id IS NULL THEN
1037
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1038
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1039
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1040
+ RETURN NEW;
1041
+ END IF;
1042
+ SELECT enrollment.* INTO enrollment_row FROM enrollments enrollment
1043
+ WHERE enrollment.id = sandbox_row.enrollment_id AND enrollment.account_id = NEW.account_id
1044
+ AND enrollment.authority_scope = 'user' AND enrollment.status = 'active' FOR SHARE;
1045
+ IF enrollment_row.id IS NULL THEN
1046
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1047
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1048
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1049
+ RETURN NEW;
1050
+ END IF;
1051
+ SELECT turn_value.* INTO STRICT turn_row FROM session_turns turn_value
1052
+ WHERE turn_value.id = NEW.turn_id AND turn_value.account_id = NEW.account_id
1053
+ AND turn_value.workspace_id = NEW.workspace_id
1054
+ AND turn_value.session_id = NEW.session_id FOR SHARE;
1055
+ IF NEW.state NOT IN ('claimed', 'running') OR NEW.closed_at IS NOT NULL
1056
+ OR NEW.quiesced_at IS NOT NULL OR session_row.active_turn_id IS DISTINCT FROM NEW.turn_id
1057
+ OR turn_row.active_attempt_id IS DISTINCT FROM NEW.id
1058
+ OR turn_row.execution_generation IS DISTINCT FROM NEW.execution_generation
1059
+ OR NEW.authority_visibility IS DISTINCT FROM session_row.visibility
1060
+ OR NEW.authority_epoch IS DISTINCT FROM session_row.authority_epoch
1061
+ THEN RAISE EXCEPTION 'personal machine requires exact current attempt authority'
1062
+ USING ERRCODE = '42501'; END IF;
1063
+ initiating_subject := coalesce(nullif(btrim(turn_row.initiating_human_subject_id), ''),
1064
+ CASE WHEN turn_row.initiator_kind = 'subject'
1065
+ THEN nullif(btrim(turn_row.initiator_subject_id), '') END);
1066
+ SELECT membership.* INTO STRICT member_row FROM organization_memberships membership
1067
+ WHERE membership.account_id = NEW.account_id AND membership.subject_id = initiating_subject
1068
+ AND membership.status = 'active' AND membership.revoked_at IS NULL FOR SHARE;
1069
+ IF member_row.id IS DISTINCT FROM enrollment_row.owner_organization_membership_id THEN
1070
+ RAISE EXCEPTION 'only the personal machine owner may attach it'
1071
+ USING ERRCODE = '42501';
1072
+ END IF;
1073
+ IF member_row.personal_workspace_id IS DISTINCT FROM NEW.workspace_id THEN
1074
+ PERFORM 1 FROM workspace_memberships workspace_membership
1075
+ WHERE workspace_membership.account_id = NEW.account_id
1076
+ AND workspace_membership.workspace_id = NEW.workspace_id
1077
+ AND workspace_membership.subject_id = initiating_subject FOR KEY SHARE;
1078
+ IF NOT FOUND THEN RAISE EXCEPTION 'machine owner lacks target workspace access'
1079
+ USING ERRCODE = '42501'; END IF;
1080
+ END IF;
1081
+ PERFORM 1 FROM organization_user_resource_authorities authority
1082
+ WHERE authority.id = enrollment_row.authority_id AND authority.account_id = NEW.account_id
1083
+ AND authority.organization_membership_id = member_row.id
1084
+ AND authority.resource_kind = 'connected_machine'
1085
+ AND authority.resource_id = enrollment_row.id
1086
+ AND authority.generation = enrollment_row.generation
1087
+ AND authority.status = 'active' AND authority.revoked_at IS NULL FOR SHARE;
1088
+ IF NOT FOUND THEN RAISE EXCEPTION 'personal machine authority is stale'
1089
+ USING ERRCODE = '42501'; END IF;
1090
+ SELECT grant_value.* INTO grant_row FROM organization_user_resource_grants grant_value
1091
+ WHERE grant_value.account_id = NEW.account_id
1092
+ AND grant_value.authority_id = enrollment_row.authority_id
1093
+ AND grant_value.owner_organization_membership_id = member_row.id
1094
+ AND grant_value.workspace_id = NEW.workspace_id
1095
+ AND grant_value.action = 'connected_machine.use'
1096
+ AND grant_value.context = session_row.visibility AND grant_value.status = 'active'
1097
+ AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
1098
+ AND ((grant_value.mode IN ('once','session')
1099
+ AND grant_value.session_id = NEW.session_id
1100
+ AND grant_value.authority_epoch = session_row.authority_epoch)
1101
+ OR (grant_value.mode = 'always' AND grant_value.session_id IS NULL
1102
+ AND grant_value.authority_epoch IS NULL))
1103
+ ORDER BY CASE grant_value.mode WHEN 'once' THEN 1 WHEN 'session' THEN 2 ELSE 3 END,
1104
+ grant_value.generation DESC, grant_value.id LIMIT 1 FOR UPDATE;
1105
+ IF NOT FOUND THEN RAISE EXCEPTION 'personal machine use requires explicit grant'
1106
+ USING ERRCODE = '42501'; END IF;
1107
+ INSERT INTO session_attempt_connected_machine_authorizations(
1108
+ attempt_id, account_id, workspace_id, session_id, turn_id, execution_generation,
1109
+ initiating_human_subject_id, owner_organization_membership_id,
1110
+ membership_authorization_revision, enrollment_id, sandbox_id, authority_id,
1111
+ authority_generation, enrollment_generation, session_visibility,
1112
+ session_authority_epoch, grant_id, grant_generation, grant_mode
1113
+ ) SELECT NEW.id, NEW.account_id, NEW.workspace_id, NEW.session_id, NEW.turn_id,
1114
+ NEW.execution_generation, initiating_subject, member_row.id,
1115
+ member_row.authorization_revision, enrollment_row.id, sandbox_row.id,
1116
+ authority.id, authority.generation, enrollment_row.generation,
1117
+ session_row.visibility, session_row.authority_epoch,
1118
+ grant_row.id, grant_row.generation, grant_row.mode
1119
+ FROM organization_user_resource_authorities authority
1120
+ WHERE authority.id = enrollment_row.authority_id AND authority.account_id = NEW.account_id;
1121
+ IF grant_row.mode = 'once' THEN
1122
+ UPDATE organization_user_resource_grants SET status = 'consumed',
1123
+ updated_at = clock_timestamp()
1124
+ WHERE id = grant_row.id AND account_id = NEW.account_id
1125
+ AND generation = grant_row.generation AND status = 'active';
1126
+ GET DIAGNOSTICS affected = ROW_COUNT;
1127
+ IF affected <> 1 THEN RAISE EXCEPTION 'once machine grant lost first-use race'
1128
+ USING ERRCODE = '40001'; END IF;
1129
+ INSERT INTO personal_resource_once_consumption_receipts(
1130
+ grant_id, account_id, attempt_id, workspace_id, session_id, turn_id,
1131
+ execution_generation, authority_id, authority_generation, grant_generation
1132
+ ) SELECT grant_row.id, NEW.account_id, NEW.id, NEW.workspace_id, NEW.session_id,
1133
+ NEW.turn_id, NEW.execution_generation, authority.id, authority.generation,
1134
+ grant_row.generation FROM organization_user_resource_authorities authority
1135
+ WHERE authority.id = enrollment_row.authority_id AND authority.account_id = NEW.account_id;
1136
+ END IF;
1137
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1138
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1139
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1140
+ AND capability_kind = 'write';
1141
+ RETURN NEW;
1142
+ EXCEPTION WHEN OTHERS THEN
1143
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1144
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1145
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1146
+ RAISE;
1147
+ END
1148
+ $$;
1149
+ DROP TRIGGER IF EXISTS z_session_attempt_personal_machine_admission ON session_turn_attempts;
1150
+ CREATE TRIGGER z_session_attempt_personal_machine_admission
1151
+ AFTER INSERT ON session_turn_attempts FOR EACH ROW
1152
+ EXECUTE FUNCTION admit_session_attempt_personal_machine();
1153
+ REVOKE ALL ON FUNCTION admit_session_attempt_personal_machine() FROM PUBLIC;
1154
+
1155
+ CREATE OR REPLACE FUNCTION authorize_session_attempt_personal_machine(
1156
+ p_account_id uuid, p_workspace_id uuid, p_session_id uuid, p_turn_id uuid,
1157
+ p_attempt_id uuid, p_execution_generation integer, p_enrollment_id uuid
1158
+ ) RETURNS boolean
1159
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
1160
+ DECLARE
1161
+ session_row sessions%ROWTYPE;
1162
+ turn_row session_turns%ROWTYPE;
1163
+ attempt_row session_turn_attempts%ROWTYPE;
1164
+ enrollment_row enrollments%ROWTYPE;
1165
+ sandbox_row sandboxes%ROWTYPE;
1166
+ member_row organization_memberships%ROWTYPE;
1167
+ authority_row organization_user_resource_authorities%ROWTYPE;
1168
+ grant_row organization_user_resource_grants%ROWTYPE;
1169
+ authorization_row session_attempt_connected_machine_authorizations%ROWTYPE;
1170
+ initiating_subject text;
1171
+ caller_subject text := coalesce(
1172
+ nullif(pg_catalog.current_setting('opengeni.initiating_human_subject_id', true), ''),
1173
+ nullif(pg_catalog.current_setting('opengeni.subject_id', true), '')
1174
+ );
1175
+ affected integer;
1176
+ BEGIN
1177
+ IF p_account_id IS DISTINCT FROM nullif(
1178
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
1179
+ OR p_workspace_id IS DISTINCT FROM nullif(
1180
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
1181
+ THEN RAISE EXCEPTION 'machine runtime scope mismatch' USING ERRCODE = '42501'; END IF;
1182
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
1183
+ backend_pid, transaction_id, capability_kind
1184
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
1185
+ ON CONFLICT DO NOTHING;
1186
+ SELECT session_value.* INTO STRICT session_row FROM sessions session_value
1187
+ WHERE session_value.id = p_session_id AND session_value.account_id = p_account_id
1188
+ AND session_value.workspace_id = p_workspace_id FOR SHARE;
1189
+ SELECT turn_value.* INTO STRICT turn_row FROM session_turns turn_value
1190
+ WHERE turn_value.id = p_turn_id AND turn_value.account_id = p_account_id
1191
+ AND turn_value.workspace_id = p_workspace_id
1192
+ AND turn_value.session_id = p_session_id FOR SHARE;
1193
+ SELECT attempt_value.* INTO STRICT attempt_row FROM session_turn_attempts attempt_value
1194
+ WHERE attempt_value.id = p_attempt_id AND attempt_value.account_id = p_account_id
1195
+ AND attempt_value.workspace_id = p_workspace_id
1196
+ AND attempt_value.session_id = p_session_id
1197
+ AND attempt_value.turn_id = p_turn_id FOR SHARE;
1198
+ IF attempt_row.execution_generation IS DISTINCT FROM p_execution_generation
1199
+ OR attempt_row.state NOT IN ('claimed', 'running')
1200
+ OR attempt_row.closed_at IS NOT NULL OR attempt_row.quiesced_at IS NOT NULL
1201
+ OR session_row.active_turn_id IS DISTINCT FROM p_turn_id
1202
+ OR turn_row.active_attempt_id IS DISTINCT FROM p_attempt_id
1203
+ OR turn_row.execution_generation IS DISTINCT FROM p_execution_generation
1204
+ OR attempt_row.authority_visibility IS DISTINCT FROM session_row.visibility
1205
+ OR attempt_row.authority_epoch IS DISTINCT FROM session_row.authority_epoch
1206
+ OR EXISTS (
1207
+ SELECT 1 FROM session_attempt_interruptions interruption
1208
+ WHERE interruption.attempt_id = p_attempt_id
1209
+ AND interruption.account_id = p_account_id
1210
+ AND interruption.workspace_id = p_workspace_id
1211
+ AND interruption.state IN ('pending', 'delivered', 'acknowledged')
1212
+ )
1213
+ THEN RAISE EXCEPTION 'personal machine requires exact current attempt authority'
1214
+ USING ERRCODE = '42501'; END IF;
1215
+ initiating_subject := coalesce(
1216
+ nullif(btrim(turn_row.initiating_human_subject_id), ''),
1217
+ CASE WHEN turn_row.initiator_kind = 'subject'
1218
+ THEN nullif(btrim(turn_row.initiator_subject_id), '') END
1219
+ );
1220
+ IF caller_subject IS DISTINCT FROM initiating_subject THEN
1221
+ RAISE EXCEPTION 'personal machine caller is not the frozen initiating human'
1222
+ USING ERRCODE = '42501';
1223
+ END IF;
1224
+ SELECT enrollment.* INTO STRICT enrollment_row FROM enrollments enrollment
1225
+ WHERE enrollment.id = p_enrollment_id AND enrollment.account_id = p_account_id
1226
+ AND enrollment.authority_scope = 'user' AND enrollment.status = 'active'
1227
+ AND enrollment.revoked_at IS NULL FOR SHARE;
1228
+ SELECT sandbox.* INTO STRICT sandbox_row FROM sandboxes sandbox
1229
+ WHERE sandbox.account_id = p_account_id AND sandbox.enrollment_id = p_enrollment_id
1230
+ AND sandbox.kind = 'selfhosted' ORDER BY sandbox.created_at, sandbox.id LIMIT 1 FOR SHARE;
1231
+ SELECT membership.* INTO STRICT member_row FROM organization_memberships membership
1232
+ WHERE membership.account_id = p_account_id AND membership.subject_id = initiating_subject
1233
+ AND membership.status = 'active' AND membership.revoked_at IS NULL FOR SHARE;
1234
+ IF member_row.id IS DISTINCT FROM enrollment_row.owner_organization_membership_id THEN
1235
+ RAISE EXCEPTION 'only the personal machine owner may use it'
1236
+ USING ERRCODE = '42501';
1237
+ END IF;
1238
+ IF member_row.personal_workspace_id IS DISTINCT FROM p_workspace_id THEN
1239
+ PERFORM 1 FROM workspace_memberships workspace_membership
1240
+ WHERE workspace_membership.account_id = p_account_id
1241
+ AND workspace_membership.workspace_id = p_workspace_id
1242
+ AND workspace_membership.subject_id = initiating_subject FOR KEY SHARE;
1243
+ IF NOT FOUND THEN RAISE EXCEPTION 'machine owner lacks target workspace access'
1244
+ USING ERRCODE = '42501'; END IF;
1245
+ END IF;
1246
+ SELECT authority.* INTO STRICT authority_row
1247
+ FROM organization_user_resource_authorities authority
1248
+ WHERE authority.id = enrollment_row.authority_id AND authority.account_id = p_account_id
1249
+ AND authority.organization_membership_id = member_row.id
1250
+ AND authority.resource_kind = 'connected_machine'
1251
+ AND authority.resource_id = enrollment_row.id
1252
+ AND authority.generation = enrollment_row.generation
1253
+ AND authority.status = 'active' AND authority.revoked_at IS NULL FOR SHARE;
1254
+ SELECT authorization_value.* INTO authorization_row
1255
+ FROM session_attempt_connected_machine_authorizations authorization_value
1256
+ WHERE authorization_value.attempt_id = p_attempt_id
1257
+ AND authorization_value.enrollment_id = p_enrollment_id FOR SHARE;
1258
+ IF authorization_row.attempt_id IS NOT NULL THEN
1259
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1260
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1261
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1262
+ AND capability_kind = 'write';
1263
+ RETURN true;
1264
+ END IF;
1265
+ SELECT grant_value.* INTO grant_row FROM organization_user_resource_grants grant_value
1266
+ WHERE grant_value.account_id = p_account_id
1267
+ AND grant_value.authority_id = authority_row.id
1268
+ AND grant_value.owner_organization_membership_id = member_row.id
1269
+ AND grant_value.workspace_id = p_workspace_id
1270
+ AND grant_value.action = 'connected_machine.use'
1271
+ AND grant_value.context = session_row.visibility AND grant_value.status = 'active'
1272
+ AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
1273
+ AND ((grant_value.mode IN ('once', 'session')
1274
+ AND grant_value.session_id = p_session_id
1275
+ AND grant_value.authority_epoch = session_row.authority_epoch)
1276
+ OR (grant_value.mode = 'always' AND grant_value.session_id IS NULL
1277
+ AND grant_value.authority_epoch IS NULL))
1278
+ ORDER BY CASE grant_value.mode WHEN 'once' THEN 1 WHEN 'session' THEN 2 ELSE 3 END,
1279
+ grant_value.generation DESC, grant_value.id LIMIT 1 FOR UPDATE;
1280
+ IF NOT FOUND THEN RAISE EXCEPTION 'personal machine use requires explicit grant'
1281
+ USING ERRCODE = '42501'; END IF;
1282
+ INSERT INTO session_attempt_connected_machine_authorizations(
1283
+ attempt_id, account_id, workspace_id, session_id, turn_id, execution_generation,
1284
+ initiating_human_subject_id, owner_organization_membership_id,
1285
+ membership_authorization_revision, enrollment_id, sandbox_id, authority_id,
1286
+ authority_generation, enrollment_generation, session_visibility,
1287
+ session_authority_epoch, grant_id, grant_generation, grant_mode
1288
+ ) VALUES (
1289
+ p_attempt_id, p_account_id, p_workspace_id, p_session_id, p_turn_id,
1290
+ p_execution_generation, initiating_subject, member_row.id,
1291
+ member_row.authorization_revision, enrollment_row.id, sandbox_row.id,
1292
+ authority_row.id, authority_row.generation, enrollment_row.generation,
1293
+ session_row.visibility, session_row.authority_epoch,
1294
+ grant_row.id, grant_row.generation, grant_row.mode
1295
+ ) ON CONFLICT (attempt_id, enrollment_id) DO NOTHING;
1296
+ GET DIAGNOSTICS affected = ROW_COUNT;
1297
+ IF affected = 1 AND grant_row.mode = 'once' THEN
1298
+ UPDATE organization_user_resource_grants SET status = 'consumed',
1299
+ updated_at = clock_timestamp()
1300
+ WHERE id = grant_row.id AND account_id = p_account_id
1301
+ AND generation = grant_row.generation AND status = 'active';
1302
+ GET DIAGNOSTICS affected = ROW_COUNT;
1303
+ IF affected <> 1 THEN RAISE EXCEPTION 'once machine grant lost first-use race'
1304
+ USING ERRCODE = '40001'; END IF;
1305
+ INSERT INTO personal_resource_once_consumption_receipts(
1306
+ grant_id, account_id, attempt_id, workspace_id, session_id, turn_id,
1307
+ execution_generation, authority_id, authority_generation, grant_generation
1308
+ ) VALUES (
1309
+ grant_row.id, p_account_id, p_attempt_id, p_workspace_id, p_session_id,
1310
+ p_turn_id, p_execution_generation, authority_row.id,
1311
+ authority_row.generation, grant_row.generation
1312
+ );
1313
+ END IF;
1314
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1315
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1316
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1317
+ AND capability_kind = 'write';
1318
+ RETURN true;
1319
+ EXCEPTION WHEN OTHERS THEN
1320
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1321
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1322
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1323
+ RAISE;
1324
+ END
1325
+ $$;
1326
+ REVOKE ALL ON FUNCTION authorize_session_attempt_personal_machine(
1327
+ uuid, uuid, uuid, uuid, uuid, integer, uuid
1328
+ ) FROM PUBLIC;
1329
+
1330
+ CREATE OR REPLACE FUNCTION assert_session_attempt_personal_machine(
1331
+ p_account_id uuid, p_workspace_id uuid, p_session_id uuid, p_turn_id uuid,
1332
+ p_attempt_id uuid, p_execution_generation integer, p_enrollment_id uuid,
1333
+ p_require_active_sandbox boolean
1334
+ ) RETURNS boolean
1335
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
1336
+ DECLARE
1337
+ authorization_row session_attempt_connected_machine_authorizations%ROWTYPE;
1338
+ caller_subject text := coalesce(
1339
+ nullif(pg_catalog.current_setting('opengeni.initiating_human_subject_id', true), ''),
1340
+ nullif(pg_catalog.current_setting('opengeni.subject_id', true), '')
1341
+ );
1342
+ BEGIN
1343
+ IF p_account_id IS DISTINCT FROM nullif(
1344
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
1345
+ OR p_workspace_id IS DISTINCT FROM nullif(
1346
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
1347
+ THEN RAISE EXCEPTION 'machine runtime scope mismatch' USING ERRCODE = '42501'; END IF;
1348
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
1349
+ backend_pid, transaction_id, capability_kind
1350
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'runtime')
1351
+ ON CONFLICT DO NOTHING;
1352
+ SELECT authorization_value.* INTO authorization_row
1353
+ FROM session_attempt_connected_machine_authorizations authorization_value
1354
+ WHERE authorization_value.attempt_id = p_attempt_id
1355
+ AND authorization_value.account_id = p_account_id
1356
+ AND authorization_value.workspace_id = p_workspace_id
1357
+ AND authorization_value.session_id = p_session_id
1358
+ AND authorization_value.turn_id = p_turn_id
1359
+ AND authorization_value.execution_generation = p_execution_generation
1360
+ AND authorization_value.enrollment_id = p_enrollment_id;
1361
+ IF authorization_row.attempt_id IS NULL THEN
1362
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1363
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1364
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1365
+ AND capability_kind = 'runtime';
1366
+ RETURN false;
1367
+ END IF;
1368
+ IF caller_subject IS DISTINCT FROM authorization_row.initiating_human_subject_id
1369
+ OR NOT EXISTS (
1370
+ SELECT 1 FROM sessions session_value
1371
+ JOIN session_turns turn_value ON turn_value.id = p_turn_id
1372
+ AND turn_value.account_id = session_value.account_id
1373
+ AND turn_value.workspace_id = session_value.workspace_id
1374
+ AND turn_value.session_id = session_value.id
1375
+ JOIN session_turn_attempts attempt ON attempt.id = p_attempt_id
1376
+ AND attempt.turn_id = turn_value.id AND attempt.session_id = session_value.id
1377
+ AND attempt.account_id = session_value.account_id
1378
+ AND attempt.workspace_id = session_value.workspace_id
1379
+ JOIN sandboxes sandbox ON sandbox.id = authorization_row.sandbox_id
1380
+ AND sandbox.account_id = session_value.account_id
1381
+ WHERE session_value.id = p_session_id AND session_value.account_id = p_account_id
1382
+ AND session_value.workspace_id = p_workspace_id
1383
+ AND session_value.active_turn_id = p_turn_id
1384
+ AND (NOT p_require_active_sandbox
1385
+ OR session_value.active_sandbox_id = authorization_row.sandbox_id)
1386
+ AND session_value.visibility = authorization_row.session_visibility
1387
+ AND session_value.authority_epoch = authorization_row.session_authority_epoch
1388
+ AND turn_value.active_attempt_id = p_attempt_id
1389
+ AND turn_value.execution_generation = p_execution_generation
1390
+ AND turn_value.status = 'running'
1391
+ AND attempt.execution_generation = p_execution_generation
1392
+ AND attempt.state IN ('claimed','running') AND attempt.closed_at IS NULL
1393
+ AND attempt.quiesced_at IS NULL
1394
+ AND sandbox.enrollment_id = p_enrollment_id
1395
+ )
1396
+ OR EXISTS (
1397
+ SELECT 1 FROM session_attempt_interruptions interruption
1398
+ WHERE interruption.attempt_id = p_attempt_id AND interruption.account_id = p_account_id
1399
+ AND interruption.workspace_id = p_workspace_id
1400
+ AND interruption.state IN ('pending','delivered','acknowledged')
1401
+ )
1402
+ OR NOT EXISTS (
1403
+ SELECT 1 FROM organization_memberships membership
1404
+ WHERE membership.id = authorization_row.owner_organization_membership_id
1405
+ AND membership.account_id = p_account_id
1406
+ AND membership.subject_id = authorization_row.initiating_human_subject_id
1407
+ AND membership.status = 'active' AND membership.revoked_at IS NULL
1408
+ AND membership.authorization_revision
1409
+ = authorization_row.membership_authorization_revision
1410
+ AND (membership.personal_workspace_id = p_workspace_id OR EXISTS (
1411
+ SELECT 1 FROM workspace_memberships workspace_membership
1412
+ WHERE workspace_membership.account_id = p_account_id
1413
+ AND workspace_membership.workspace_id = p_workspace_id
1414
+ AND workspace_membership.subject_id = membership.subject_id
1415
+ ))
1416
+ )
1417
+ OR NOT EXISTS (
1418
+ SELECT 1 FROM enrollments enrollment
1419
+ WHERE enrollment.id = p_enrollment_id AND enrollment.account_id = p_account_id
1420
+ AND enrollment.authority_scope = 'user'
1421
+ AND enrollment.owner_organization_membership_id
1422
+ = authorization_row.owner_organization_membership_id
1423
+ AND enrollment.authority_id = authorization_row.authority_id
1424
+ AND enrollment.generation = authorization_row.enrollment_generation
1425
+ AND enrollment.status = 'active' AND enrollment.revoked_at IS NULL
1426
+ )
1427
+ OR NOT EXISTS (
1428
+ SELECT 1 FROM organization_user_resource_authorities authority
1429
+ WHERE authority.id = authorization_row.authority_id
1430
+ AND authority.account_id = p_account_id
1431
+ AND authority.organization_membership_id
1432
+ = authorization_row.owner_organization_membership_id
1433
+ AND authority.resource_kind = 'connected_machine'
1434
+ AND authority.resource_id = p_enrollment_id
1435
+ AND authority.generation = authorization_row.authority_generation
1436
+ AND authority.status = 'active' AND authority.revoked_at IS NULL
1437
+ )
1438
+ OR NOT EXISTS (
1439
+ SELECT 1 FROM organization_user_resource_grants grant_value
1440
+ WHERE grant_value.id = authorization_row.grant_id
1441
+ AND grant_value.account_id = p_account_id
1442
+ AND grant_value.authority_id = authorization_row.authority_id
1443
+ AND grant_value.owner_organization_membership_id
1444
+ = authorization_row.owner_organization_membership_id
1445
+ AND grant_value.workspace_id = p_workspace_id
1446
+ AND grant_value.generation = authorization_row.grant_generation
1447
+ AND grant_value.action = 'connected_machine.use'
1448
+ AND grant_value.context = authorization_row.session_visibility
1449
+ AND grant_value.mode = authorization_row.grant_mode
1450
+ AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
1451
+ AND ((authorization_row.grant_mode = 'once' AND grant_value.status = 'consumed'
1452
+ AND grant_value.session_id = p_session_id
1453
+ AND grant_value.authority_epoch = authorization_row.session_authority_epoch
1454
+ AND EXISTS (SELECT 1 FROM personal_resource_once_consumption_receipts receipt
1455
+ WHERE receipt.grant_id = grant_value.id AND receipt.attempt_id = p_attempt_id
1456
+ AND receipt.account_id = p_account_id))
1457
+ OR (authorization_row.grant_mode = 'session' AND grant_value.status = 'active'
1458
+ AND grant_value.session_id = p_session_id
1459
+ AND grant_value.authority_epoch = authorization_row.session_authority_epoch)
1460
+ OR (authorization_row.grant_mode = 'always' AND grant_value.status = 'active'
1461
+ AND grant_value.session_id IS NULL AND grant_value.authority_epoch IS NULL))
1462
+ )
1463
+ THEN RAISE EXCEPTION 'personal machine authority is no longer live'
1464
+ USING ERRCODE = '42501'; END IF;
1465
+ INSERT INTO audit_events(
1466
+ account_id, workspace_id, subject_id, action, target_type, target_id,
1467
+ metadata, metadata_codec_version
1468
+ ) VALUES (
1469
+ p_account_id, p_workspace_id, authorization_row.initiating_human_subject_id,
1470
+ 'connected_machine.used', 'enrollment', p_enrollment_id::text,
1471
+ pg_catalog.jsonb_build_object(
1472
+ 'ownerOrganizationMembershipId', authorization_row.owner_organization_membership_id,
1473
+ 'scope', 'user', 'sessionId', p_session_id, 'turnId', p_turn_id,
1474
+ 'attemptId', p_attempt_id, 'executionGeneration', p_execution_generation,
1475
+ 'generation', authorization_row.enrollment_generation
1476
+ ), 1
1477
+ );
1478
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1479
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1480
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1481
+ AND capability_kind = 'runtime';
1482
+ RETURN true;
1483
+ EXCEPTION WHEN OTHERS THEN
1484
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1485
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1486
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1487
+ RAISE;
1488
+ END
1489
+ $$;
1490
+ REVOKE ALL ON FUNCTION assert_session_attempt_personal_machine(
1491
+ uuid, uuid, uuid, uuid, uuid, integer, uuid, boolean
1492
+ ) FROM PUBLIC;
1493
+
1494
+ CREATE OR REPLACE FUNCTION list_scoped_machine_dependent_sessions(
1495
+ p_account_id uuid,
1496
+ p_origin_workspace_id uuid,
1497
+ p_sandbox_id uuid
1498
+ ) RETURNS TABLE(
1499
+ session_id uuid,
1500
+ title text,
1501
+ active_turn_id uuid,
1502
+ sandbox_backend text
1503
+ )
1504
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
1505
+ BEGIN
1506
+ IF p_account_id IS DISTINCT FROM nullif(
1507
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
1508
+ OR p_origin_workspace_id IS DISTINCT FROM nullif(
1509
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
1510
+ THEN RAISE EXCEPTION 'machine removal scope mismatch' USING ERRCODE = '42501'; END IF;
1511
+ IF NOT EXISTS (
1512
+ SELECT 1 FROM sandboxes sandbox
1513
+ WHERE sandbox.id = p_sandbox_id AND sandbox.account_id = p_account_id
1514
+ AND sandbox.workspace_id = p_origin_workspace_id
1515
+ AND sandbox.kind = 'selfhosted' AND sandbox.enrollment_id IS NOT NULL
1516
+ ) THEN RAISE EXCEPTION 'machine sandbox not found' USING ERRCODE = '42501'; END IF;
1517
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
1518
+ backend_pid, transaction_id, capability_kind
1519
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'read')
1520
+ ON CONFLICT DO NOTHING;
1521
+ RETURN QUERY
1522
+ SELECT session.id, session.title, session.active_turn_id, session.sandbox_backend
1523
+ FROM sessions session
1524
+ WHERE session.account_id = p_account_id
1525
+ AND (session.active_sandbox_id = p_sandbox_id
1526
+ OR session.sandbox_group_id = p_sandbox_id)
1527
+ ORDER BY session.created_at, session.id
1528
+ FOR NO KEY UPDATE;
1529
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1530
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1531
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1532
+ AND capability_kind = 'read';
1533
+ EXCEPTION WHEN OTHERS THEN
1534
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1535
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1536
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1537
+ RAISE;
1538
+ END
1539
+ $$;
1540
+ REVOKE ALL ON FUNCTION list_scoped_machine_dependent_sessions(uuid, uuid, uuid)
1541
+ FROM PUBLIC;
1542
+
1543
+ CREATE OR REPLACE FUNCTION detach_scoped_machine_dependent_sessions(
1544
+ p_account_id uuid,
1545
+ p_origin_workspace_id uuid,
1546
+ p_sandbox_id uuid
1547
+ ) RETURNS TABLE(session_id uuid, title text)
1548
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
1549
+ DECLARE
1550
+ dependent_workspace_id uuid;
1551
+ detached_record record;
1552
+ next_activity_revision bigint;
1553
+ BEGIN
1554
+ IF p_account_id IS DISTINCT FROM nullif(
1555
+ pg_catalog.current_setting('opengeni.account_id', true), '')::uuid
1556
+ OR p_origin_workspace_id IS DISTINCT FROM nullif(
1557
+ pg_catalog.current_setting('opengeni.workspace_id', true), '')::uuid
1558
+ THEN RAISE EXCEPTION 'machine removal scope mismatch' USING ERRCODE = '42501'; END IF;
1559
+ IF NOT EXISTS (
1560
+ SELECT 1 FROM sandboxes sandbox
1561
+ WHERE sandbox.id = p_sandbox_id AND sandbox.account_id = p_account_id
1562
+ AND sandbox.workspace_id = p_origin_workspace_id
1563
+ AND sandbox.kind = 'selfhosted' AND sandbox.enrollment_id IS NOT NULL
1564
+ ) THEN RAISE EXCEPTION 'machine sandbox not found' USING ERRCODE = '42501'; END IF;
1565
+ INSERT INTO opengeni_private.scoped_compute_capabilities(
1566
+ backend_pid, transaction_id, capability_kind
1567
+ ) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'write')
1568
+ ON CONFLICT DO NOTHING;
1569
+ IF EXISTS (
1570
+ SELECT 1 FROM sessions session
1571
+ WHERE session.account_id = p_account_id
1572
+ AND (session.active_sandbox_id = p_sandbox_id
1573
+ OR session.sandbox_group_id = p_sandbox_id)
1574
+ AND session.active_turn_id IS NOT NULL
1575
+ ) THEN RAISE EXCEPTION 'machine still has active dependent sessions'
1576
+ USING ERRCODE = '55000'; END IF;
1577
+ FOR dependent_workspace_id IN
1578
+ SELECT DISTINCT session.workspace_id
1579
+ FROM sessions session
1580
+ WHERE session.account_id = p_account_id
1581
+ AND (session.active_sandbox_id = p_sandbox_id
1582
+ OR (session.sandbox_group_id = p_sandbox_id
1583
+ AND session.sandbox_backend = 'selfhosted'))
1584
+ ORDER BY session.workspace_id
1585
+ LOOP
1586
+ PERFORM pg_catalog.set_config('opengeni.session_activity_gate_state', 'open', true);
1587
+ PERFORM pg_catalog.set_config(
1588
+ 'opengeni.session_activity_gate_workspace_id', dependent_workspace_id::text, true
1589
+ );
1590
+ FOR detached_record IN
1591
+ UPDATE sessions session
1592
+ SET active_sandbox_id = NULL,
1593
+ active_epoch = session.active_epoch + 1,
1594
+ sandbox_backend = CASE
1595
+ WHEN session.sandbox_backend = 'selfhosted' THEN 'none'
1596
+ ELSE session.sandbox_backend
1597
+ END,
1598
+ updated_at = clock_timestamp()
1599
+ WHERE session.account_id = p_account_id
1600
+ AND session.workspace_id = dependent_workspace_id
1601
+ AND (session.active_sandbox_id = p_sandbox_id
1602
+ OR (session.sandbox_group_id = p_sandbox_id
1603
+ AND session.sandbox_backend = 'selfhosted'))
1604
+ RETURNING session.id, session.title
1605
+ LOOP
1606
+ session_id := detached_record.id;
1607
+ title := detached_record.title;
1608
+ RETURN NEXT;
1609
+ END LOOP;
1610
+ PERFORM pg_catalog.set_config('opengeni.session_activity_gate_state', 'finalizing', true);
1611
+ UPDATE workspace_session_activity_revisions counter
1612
+ SET revision = counter.revision + 1
1613
+ WHERE counter.workspace_id = dependent_workspace_id
1614
+ AND EXISTS (
1615
+ SELECT 1 FROM sessions pending
1616
+ WHERE pending.workspace_id = dependent_workspace_id
1617
+ AND pending.activity_revision_pending_xid
1618
+ = pg_catalog.pg_current_xact_id()::text::bigint
1619
+ )
1620
+ RETURNING counter.revision INTO next_activity_revision;
1621
+ UPDATE sessions pending
1622
+ SET activity_revision = next_activity_revision,
1623
+ activity_revision_pending_xid = NULL
1624
+ WHERE pending.workspace_id = dependent_workspace_id
1625
+ AND pending.activity_revision_pending_xid
1626
+ = pg_catalog.pg_current_xact_id()::text::bigint;
1627
+ END LOOP;
1628
+ PERFORM pg_catalog.set_config('opengeni.session_activity_gate_state', 'open', true);
1629
+ PERFORM pg_catalog.set_config(
1630
+ 'opengeni.session_activity_gate_workspace_id', p_origin_workspace_id::text, true
1631
+ );
1632
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1633
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1634
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
1635
+ AND capability_kind = 'write';
1636
+ EXCEPTION WHEN OTHERS THEN
1637
+ DELETE FROM opengeni_private.scoped_compute_capabilities
1638
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
1639
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
1640
+ RAISE;
1641
+ END
1642
+ $$;
1643
+ REVOKE ALL ON FUNCTION detach_scoped_machine_dependent_sessions(uuid, uuid, uuid)
1644
+ FROM PUBLIC;
1645
+
1646
+ DO $grants$
1647
+ BEGIN
1648
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
1649
+ -- Policies on ordinary runtime-visible tables call this predicate. EXECUTE
1650
+ -- only exposes false unless a same-transaction SECURITY DEFINER lifecycle
1651
+ -- inserted the unforgeable private capability row; the app role retains no
1652
+ -- table access to mint one.
1653
+ GRANT EXECUTE ON FUNCTION opengeni_private.scoped_compute_capability_active(text)
1654
+ TO opengeni_app;
1655
+ GRANT EXECUTE ON FUNCTION list_scoped_rigs(uuid, uuid, uuid, text, text)
1656
+ TO opengeni_app;
1657
+ GRANT EXECUTE ON FUNCTION count_scoped_rigs(uuid, uuid, text) TO opengeni_app;
1658
+ GRANT EXECUTE ON FUNCTION create_scoped_rig(
1659
+ uuid, uuid, text, text, text, text, jsonb, boolean
1660
+ ) TO opengeni_app;
1661
+ GRANT EXECUTE ON FUNCTION mutate_scoped_rig(
1662
+ uuid, uuid, uuid, text, text, boolean, text, boolean, boolean
1663
+ ) TO opengeni_app;
1664
+ GRANT EXECUTE ON FUNCTION finalize_scoped_enrollment(
1665
+ uuid, uuid, text, text, boolean, boolean, text, text, text, boolean
1666
+ ) TO opengeni_app;
1667
+ GRANT EXECUTE ON FUNCTION list_scoped_enrollments(uuid, uuid, uuid, text)
1668
+ TO opengeni_app;
1669
+ GRANT EXECUTE ON FUNCTION get_scoped_sandbox(uuid, uuid, uuid) TO opengeni_app;
1670
+ GRANT EXECUTE ON FUNCTION authorize_scoped_sandbox_attach(uuid, uuid, uuid)
1671
+ TO opengeni_app;
1672
+ GRANT EXECUTE ON FUNCTION materialize_scoped_rig_version_for_attempt(
1673
+ uuid, uuid, uuid, uuid, uuid, integer
1674
+ ) TO opengeni_app;
1675
+ GRANT EXECUTE ON FUNCTION authorize_session_attempt_personal_machine(
1676
+ uuid, uuid, uuid, uuid, uuid, integer, uuid
1677
+ ) TO opengeni_app;
1678
+ GRANT EXECUTE ON FUNCTION assert_session_attempt_personal_machine(
1679
+ uuid, uuid, uuid, uuid, uuid, integer, uuid, boolean
1680
+ ) TO opengeni_app;
1681
+ GRANT EXECUTE ON FUNCTION list_scoped_machine_dependent_sessions(uuid, uuid, uuid)
1682
+ TO opengeni_app;
1683
+ GRANT EXECUTE ON FUNCTION detach_scoped_machine_dependent_sessions(uuid, uuid, uuid)
1684
+ TO opengeni_app;
1685
+ REVOKE ALL ON TABLE opengeni_private.scoped_compute_capabilities FROM opengeni_app;
1686
+ REVOKE ALL ON TABLE session_attempt_connected_machine_authorizations
1687
+ FROM opengeni_app;
1688
+ REVOKE ALL ON TABLE organization_user_resource_authorities FROM opengeni_app;
1689
+ REVOKE ALL ON TABLE organization_user_resource_grants FROM opengeni_app;
1690
+ END IF;
1691
+ END
1692
+ $grants$;
1693
+
1694
+ COMMENT ON COLUMN rigs.authority_scope IS
1695
+ 'Explicit owner scope: organization, workspace, or organization+user.';
1696
+ COMMENT ON COLUMN enrollments.authority_scope IS
1697
+ 'Connected Machine owner scope; human approval defaults to organization+user.';
1698
+ COMMENT ON FUNCTION assert_session_attempt_personal_machine(
1699
+ uuid, uuid, uuid, uuid, uuid, integer, uuid, boolean
1700
+ ) IS 'Revalidates exact personal-machine owner, membership, grant, session authority, and generation immediately before machine use.';