@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.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1595 -81
- package/dist/index.js +21571 -8126
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +22 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +41 -4
- package/dist/session-realtime-ledger.d.ts +2 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +11651 -1548
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +877 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1291 -76
- package/src/schema.ts +3303 -428
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +437 -84
- package/src/session-realtime-context.ts +3 -3
- package/src/session-realtime-ledger.ts +22 -11
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
SET lock_timeout = '5s';
|
|
3
|
+
SET statement_timeout = '60s';
|
|
4
|
+
|
|
5
|
+
-- The onboarding-proposal draft validation trigger bound a knowledge-proposal
|
|
6
|
+
-- draft to `current_setting('opengeni.subject_id')`. On the real first-party
|
|
7
|
+
-- MCP path that GUC carries the *service* subject (`worker:first-party-mcp`)
|
|
8
|
+
-- while the frozen human travels in `opengeni.initiating_human_subject_id`,
|
|
9
|
+
-- so every real agent `remember` rule call failed the draft check with
|
|
10
|
+
-- SQLSTATE 23514 regardless of workspace state. Compare against the canonical
|
|
11
|
+
-- initiating-human GUC first (the pattern every authority migration since
|
|
12
|
+
-- 0225 uses), falling back to `opengeni.subject_id` for direct human writers
|
|
13
|
+
-- that never set the initiating-human GUC. The rest of the function body is
|
|
14
|
+
-- byte-identical to migration 0269.
|
|
15
|
+
|
|
16
|
+
CREATE OR REPLACE FUNCTION workspace_instruction_policy_validate_onboarding_proposal()
|
|
17
|
+
RETURNS trigger LANGUAGE plpgsql AS $$
|
|
18
|
+
BEGIN
|
|
19
|
+
IF NEW.baseline_revision_id IS NULL THEN
|
|
20
|
+
IF EXISTS (
|
|
21
|
+
SELECT 1 FROM workspace_instruction_policy_heads head
|
|
22
|
+
WHERE head.account_id = NEW.account_id
|
|
23
|
+
AND head.workspace_id = NEW.workspace_id
|
|
24
|
+
AND head.kind = NEW.kind
|
|
25
|
+
AND head.scope = NEW.scope
|
|
26
|
+
AND head.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
27
|
+
) THEN
|
|
28
|
+
RAISE EXCEPTION 'instruction-policy proposal must capture the exact active head baseline'
|
|
29
|
+
USING ERRCODE = '23514';
|
|
30
|
+
END IF;
|
|
31
|
+
IF NOT (
|
|
32
|
+
(
|
|
33
|
+
NEW.baseline_activation_version = 0
|
|
34
|
+
AND NOT EXISTS (
|
|
35
|
+
SELECT 1 FROM workspace_instruction_policy_heads head
|
|
36
|
+
WHERE head.account_id = NEW.account_id
|
|
37
|
+
AND head.workspace_id = NEW.workspace_id
|
|
38
|
+
AND head.kind = NEW.kind
|
|
39
|
+
AND head.scope = NEW.scope
|
|
40
|
+
AND head.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
41
|
+
)
|
|
42
|
+
AND NOT EXISTS (
|
|
43
|
+
SELECT 1 FROM workspace_instruction_policy_deactivation_events event
|
|
44
|
+
WHERE event.account_id = NEW.account_id
|
|
45
|
+
AND event.workspace_id = NEW.workspace_id
|
|
46
|
+
AND event.kind = NEW.kind
|
|
47
|
+
AND event.scope = NEW.scope
|
|
48
|
+
AND event.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
49
|
+
)
|
|
50
|
+
) OR (
|
|
51
|
+
NEW.baseline_activation_version > 0
|
|
52
|
+
AND NOT EXISTS (
|
|
53
|
+
SELECT 1 FROM workspace_instruction_policy_heads head
|
|
54
|
+
WHERE head.account_id = NEW.account_id
|
|
55
|
+
AND head.workspace_id = NEW.workspace_id
|
|
56
|
+
AND head.kind = NEW.kind
|
|
57
|
+
AND head.scope = NEW.scope
|
|
58
|
+
AND head.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
59
|
+
)
|
|
60
|
+
AND EXISTS (
|
|
61
|
+
SELECT 1 FROM workspace_instruction_policy_deactivation_events event
|
|
62
|
+
WHERE event.account_id = NEW.account_id
|
|
63
|
+
AND event.workspace_id = NEW.workspace_id
|
|
64
|
+
AND event.kind = NEW.kind
|
|
65
|
+
AND event.scope = NEW.scope
|
|
66
|
+
AND event.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
67
|
+
AND event.activation_version = NEW.baseline_activation_version
|
|
68
|
+
AND NOT EXISTS (
|
|
69
|
+
SELECT 1 FROM workspace_instruction_policy_deactivation_events newer
|
|
70
|
+
WHERE newer.account_id = event.account_id
|
|
71
|
+
AND newer.workspace_id = event.workspace_id
|
|
72
|
+
AND newer.kind = event.kind
|
|
73
|
+
AND newer.scope = event.scope
|
|
74
|
+
AND newer.role_key IS NOT DISTINCT FROM event.role_key
|
|
75
|
+
AND newer.activation_version > event.activation_version
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
) THEN
|
|
80
|
+
RAISE EXCEPTION 'instruction-policy proposal must capture the exact inactive boundary'
|
|
81
|
+
USING ERRCODE = '23514';
|
|
82
|
+
END IF;
|
|
83
|
+
ELSIF NOT EXISTS (
|
|
84
|
+
SELECT 1 FROM workspace_instruction_policy_heads head
|
|
85
|
+
WHERE head.account_id = NEW.account_id
|
|
86
|
+
AND head.workspace_id = NEW.workspace_id
|
|
87
|
+
AND head.kind = NEW.kind
|
|
88
|
+
AND head.scope = NEW.scope
|
|
89
|
+
AND head.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
90
|
+
AND head.revision_id = NEW.baseline_revision_id
|
|
91
|
+
AND head.revision = NEW.baseline_revision
|
|
92
|
+
AND head.content_hash = NEW.baseline_content_hash
|
|
93
|
+
AND head.activation_version = NEW.baseline_activation_version
|
|
94
|
+
AND head.activated_at = NEW.baseline_activated_at
|
|
95
|
+
) THEN
|
|
96
|
+
RAISE EXCEPTION 'instruction-policy proposal must capture the exact active head baseline'
|
|
97
|
+
USING ERRCODE = '23514';
|
|
98
|
+
END IF;
|
|
99
|
+
|
|
100
|
+
IF NEW.baseline_revision_id IS NOT NULL AND NOT EXISTS (
|
|
101
|
+
SELECT 1 FROM workspace_instruction_policy_revisions revision
|
|
102
|
+
WHERE revision.id = NEW.baseline_revision_id
|
|
103
|
+
AND revision.account_id = NEW.account_id
|
|
104
|
+
AND revision.workspace_id = NEW.workspace_id
|
|
105
|
+
AND revision.kind = NEW.kind
|
|
106
|
+
AND revision.scope = NEW.scope
|
|
107
|
+
AND revision.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
108
|
+
AND revision.revision = NEW.baseline_revision
|
|
109
|
+
AND revision.content_hash = NEW.baseline_content_hash
|
|
110
|
+
) THEN
|
|
111
|
+
RAISE EXCEPTION 'instruction-policy proposal has an invalid baseline revision'
|
|
112
|
+
USING ERRCODE = '23514';
|
|
113
|
+
END IF;
|
|
114
|
+
|
|
115
|
+
IF NOT EXISTS (
|
|
116
|
+
SELECT 1 FROM workspace_instruction_policy_revisions revision
|
|
117
|
+
WHERE revision.id = NEW.draft_revision_id
|
|
118
|
+
AND revision.operation_id = NEW.operation_id
|
|
119
|
+
AND revision.request_fingerprint = NEW.request_fingerprint
|
|
120
|
+
AND revision.account_id = NEW.account_id
|
|
121
|
+
AND revision.workspace_id = NEW.workspace_id
|
|
122
|
+
AND revision.kind = NEW.kind
|
|
123
|
+
AND revision.scope = NEW.scope
|
|
124
|
+
AND revision.role_key IS NOT DISTINCT FROM NEW.role_key
|
|
125
|
+
AND revision.revision = NEW.draft_revision
|
|
126
|
+
AND revision.content_hash = NEW.draft_content_hash
|
|
127
|
+
AND revision.supersedes_revision_id IS NOT DISTINCT FROM NEW.baseline_revision_id
|
|
128
|
+
AND revision.created_by_subject_id = NEW.created_by_subject_id
|
|
129
|
+
AND (
|
|
130
|
+
(
|
|
131
|
+
revision.provenance_source = 'onboarding'
|
|
132
|
+
AND revision.provenance_source_id = NEW.id::text
|
|
133
|
+
) OR (
|
|
134
|
+
revision.provenance_source = 'knowledge_proposal'
|
|
135
|
+
AND revision.provenance_source_id = NEW.source_id
|
|
136
|
+
AND NEW.source_version = NEW.draft_content_hash
|
|
137
|
+
AND EXISTS (
|
|
138
|
+
SELECT 1 FROM knowledge_change_proposals proposal
|
|
139
|
+
WHERE proposal.id::text = NEW.source_id
|
|
140
|
+
AND proposal.account_id = NEW.account_id
|
|
141
|
+
AND proposal.scope_kind = 'workspace'
|
|
142
|
+
AND proposal.scope_workspace_id = NEW.workspace_id
|
|
143
|
+
AND proposal.scope_subject_id IS NULL
|
|
144
|
+
AND proposal.target_kind = 'instruction_policy'
|
|
145
|
+
AND proposal.target_scope = NEW.scope
|
|
146
|
+
AND proposal.target_key IS NOT DISTINCT FROM CASE
|
|
147
|
+
WHEN NEW.scope = 'role' THEN NEW.role_key
|
|
148
|
+
ELSE NULL
|
|
149
|
+
END
|
|
150
|
+
AND proposal.content_hash = NEW.source_version
|
|
151
|
+
AND proposal.actor_kind = 'service'
|
|
152
|
+
AND proposal.actor_subject_id = NEW.created_by_subject_id
|
|
153
|
+
AND proposal.initiating_human_subject_id = COALESCE(
|
|
154
|
+
NULLIF(current_setting('opengeni.initiating_human_subject_id', true), ''),
|
|
155
|
+
current_setting('opengeni.subject_id', true)
|
|
156
|
+
)
|
|
157
|
+
AND proposal.claim_id IS NOT NULL
|
|
158
|
+
AND proposal.evidence_id IS NOT NULL
|
|
159
|
+
AND proposal.status = 'proposed'
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
) THEN
|
|
164
|
+
RAISE EXCEPTION 'instruction-policy proposal must identify its exact inactive draft'
|
|
165
|
+
USING ERRCODE = '23514';
|
|
166
|
+
END IF;
|
|
167
|
+
|
|
168
|
+
IF EXISTS (
|
|
169
|
+
SELECT 1 FROM workspace_instruction_policy_heads head
|
|
170
|
+
WHERE head.account_id = NEW.account_id
|
|
171
|
+
AND head.workspace_id = NEW.workspace_id
|
|
172
|
+
AND head.revision_id = NEW.draft_revision_id
|
|
173
|
+
) OR EXISTS (
|
|
174
|
+
SELECT 1 FROM workspace_instruction_policy_activation_events event
|
|
175
|
+
WHERE event.account_id = NEW.account_id
|
|
176
|
+
AND event.workspace_id = NEW.workspace_id
|
|
177
|
+
AND event.new_revision_id = NEW.draft_revision_id
|
|
178
|
+
) THEN
|
|
179
|
+
RAISE EXCEPTION 'instruction-policy proposal must identify a never-activated inactive draft'
|
|
180
|
+
USING ERRCODE = '23514';
|
|
181
|
+
END IF;
|
|
182
|
+
RETURN NEW;
|
|
183
|
+
END;
|
|
184
|
+
$$;
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
-- CREATE OR REPLACE drops the previously pinned search_path; re-pin it exactly
|
|
188
|
+
-- as migration 0269's hardening block did.
|
|
189
|
+
DO $onboarding_proposal_initiating_human_hardening$
|
|
190
|
+
DECLARE
|
|
191
|
+
target_schema text := current_schema();
|
|
192
|
+
BEGIN
|
|
193
|
+
EXECUTE format(
|
|
194
|
+
'ALTER FUNCTION %I.workspace_instruction_policy_validate_onboarding_proposal() '
|
|
195
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
|
|
196
|
+
);
|
|
197
|
+
END
|
|
198
|
+
$onboarding_proposal_initiating_human_hardening$;
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Migration 0277: every admitted persistable /workspace writer and every
|
|
3
|
+
-- retained process carries its own exact authority tuple - causal initiator,
|
|
4
|
+
-- initiating human, organization membership (the grant identity) and its
|
|
5
|
+
-- observed authorization revision, plus the session tenancy epoch/visibility
|
|
6
|
+
-- and owning membership that were true when the operation was admitted.
|
|
7
|
+
--
|
|
8
|
+
-- Before this migration only the `turn` actor was attributable, through
|
|
9
|
+
-- `session_turn_attempts` (migration 0222) and `session_turns` (migration
|
|
10
|
+
-- 0096). `direct` (API request) and `process` (retained yielded shell) actors
|
|
11
|
+
-- recorded no owner, no causal initiator, no visibility and no authority
|
|
12
|
+
-- epoch, so organization offboarding (migration 0263) had nothing to fence and
|
|
13
|
+
-- `docs/organization-tenancy.md` explicitly deferred that attribution. This is
|
|
14
|
+
-- that attribution; it stores identities and epochs only and never a secret
|
|
15
|
+
-- value.
|
|
16
|
+
--
|
|
17
|
+
-- Rolling window. The new columns are nullable or carry the explicit
|
|
18
|
+
-- `legacy_unattributed` / `unattributed-legacy` sentinels, so a pre-0277
|
|
19
|
+
-- API/worker image keeps inserting admissions and retained processes exactly
|
|
20
|
+
-- as before and lands on the sentinel. Historical and mixed-window rows are
|
|
21
|
+
-- promoted only where the frozen turn/attempt snapshot makes the authority
|
|
22
|
+
-- unambiguous; nothing is inferred for a `direct` or `process` row that never
|
|
23
|
+
-- had an owner. A sentinel row is refused by NEW admission in the post-0277
|
|
24
|
+
-- application (fail closed), but the live provider process behind it is never
|
|
25
|
+
-- terminated by this migration or by that refusal - the user sees a typed
|
|
26
|
+
-- rejection on the next write instead of losing a running shell.
|
|
27
|
+
|
|
28
|
+
SET LOCAL lock_timeout = '5s';
|
|
29
|
+
SET LOCAL statement_timeout = '10min';
|
|
30
|
+
|
|
31
|
+
-- 1. Expand -------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
34
|
+
ADD COLUMN IF NOT EXISTS "initiator_kind" text NOT NULL DEFAULT 'legacy_unattributed',
|
|
35
|
+
ADD COLUMN IF NOT EXISTS "initiator_subject_id" text NOT NULL DEFAULT 'unattributed-legacy',
|
|
36
|
+
ADD COLUMN IF NOT EXISTS "initiating_human_subject_id" text,
|
|
37
|
+
ADD COLUMN IF NOT EXISTS "initiator_organization_membership_id" uuid,
|
|
38
|
+
ADD COLUMN IF NOT EXISTS "initiator_authorization_revision" bigint,
|
|
39
|
+
ADD COLUMN IF NOT EXISTS "authority_epoch" integer,
|
|
40
|
+
ADD COLUMN IF NOT EXISTS "authority_visibility" text,
|
|
41
|
+
ADD COLUMN IF NOT EXISTS "authority_owner_organization_membership_id" uuid;
|
|
42
|
+
|
|
43
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
44
|
+
ADD COLUMN IF NOT EXISTS "initiator_kind" text NOT NULL DEFAULT 'legacy_unattributed',
|
|
45
|
+
ADD COLUMN IF NOT EXISTS "initiator_subject_id" text NOT NULL DEFAULT 'unattributed-legacy',
|
|
46
|
+
ADD COLUMN IF NOT EXISTS "initiating_human_subject_id" text,
|
|
47
|
+
ADD COLUMN IF NOT EXISTS "initiator_organization_membership_id" uuid,
|
|
48
|
+
ADD COLUMN IF NOT EXISTS "initiator_authorization_revision" bigint,
|
|
49
|
+
ADD COLUMN IF NOT EXISTS "authority_epoch" integer,
|
|
50
|
+
ADD COLUMN IF NOT EXISTS "authority_visibility" text,
|
|
51
|
+
ADD COLUMN IF NOT EXISTS "authority_owner_organization_membership_id" uuid;
|
|
52
|
+
|
|
53
|
+
-- The grant identity is a real membership row in the same organization. It is
|
|
54
|
+
-- never cascaded away: offboarding keeps the membership row (revoked) exactly
|
|
55
|
+
-- so retained authority evidence stays resolvable.
|
|
56
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
57
|
+
ADD CONSTRAINT "sandbox_workspace_mutation_admissions_initiator_membership_fk"
|
|
58
|
+
FOREIGN KEY ("initiator_organization_membership_id", "account_id")
|
|
59
|
+
REFERENCES "organization_memberships" ("id", "account_id")
|
|
60
|
+
ON DELETE RESTRICT NOT VALID,
|
|
61
|
+
ADD CONSTRAINT "sandbox_workspace_mutation_admissions_authority_owner_fk"
|
|
62
|
+
FOREIGN KEY ("authority_owner_organization_membership_id", "account_id")
|
|
63
|
+
REFERENCES "organization_memberships" ("id", "account_id")
|
|
64
|
+
ON DELETE RESTRICT NOT VALID;
|
|
65
|
+
|
|
66
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
67
|
+
ADD CONSTRAINT "sandbox_retained_processes_initiator_membership_fk"
|
|
68
|
+
FOREIGN KEY ("initiator_organization_membership_id", "account_id")
|
|
69
|
+
REFERENCES "organization_memberships" ("id", "account_id")
|
|
70
|
+
ON DELETE RESTRICT NOT VALID,
|
|
71
|
+
ADD CONSTRAINT "sandbox_retained_processes_authority_owner_fk"
|
|
72
|
+
FOREIGN KEY ("authority_owner_organization_membership_id", "account_id")
|
|
73
|
+
REFERENCES "organization_memberships" ("id", "account_id")
|
|
74
|
+
ON DELETE RESTRICT NOT VALID;
|
|
75
|
+
|
|
76
|
+
-- 2. Promote the unambiguous historical rows ----------------------------
|
|
77
|
+
-- The retained-process identity constraint trigger is INITIALLY DEFERRED.
|
|
78
|
+
-- Fire it per-row instead: a deferred event queued by this backfill would
|
|
79
|
+
-- otherwise make the later ALTER TABLE in this same transaction fail with
|
|
80
|
+
-- "pending trigger events" on any deployment that has retained processes.
|
|
81
|
+
-- The promotion touches only the new authority columns, so the re-validation
|
|
82
|
+
-- against the parent admission is unaffected.
|
|
83
|
+
SET CONSTRAINTS "sandbox_retained_processes_identity_v2" IMMEDIATE;
|
|
84
|
+
|
|
85
|
+
-- Only a `turn` actor has a frozen accepted-authority snapshot. Copy it
|
|
86
|
+
-- verbatim: the attempt owns tenancy epoch/visibility/owner membership and the
|
|
87
|
+
-- logical turn owns the causal initiator. Nothing here derives an owner from
|
|
88
|
+
-- the session creator, the current membership table, or provenance metadata.
|
|
89
|
+
|
|
90
|
+
UPDATE "sandbox_workspace_mutation_admissions" AS admission
|
|
91
|
+
SET
|
|
92
|
+
-- A turn frozen before migration 0096 carries the subject sentinel with the
|
|
93
|
+
-- default 'service' kind. That is honestly "no causal initiator", so it is
|
|
94
|
+
-- normalized to the explicit sentinel rather than promoted to a real one.
|
|
95
|
+
"initiator_kind" = CASE
|
|
96
|
+
WHEN turn."initiator_subject_id" = 'unattributed-legacy' THEN 'legacy_unattributed'
|
|
97
|
+
ELSE turn."initiator_kind"
|
|
98
|
+
END,
|
|
99
|
+
"initiator_subject_id" = turn."initiator_subject_id",
|
|
100
|
+
"initiating_human_subject_id" = CASE
|
|
101
|
+
WHEN turn."initiator_subject_id" = 'unattributed-legacy' THEN NULL
|
|
102
|
+
ELSE turn."initiating_human_subject_id"
|
|
103
|
+
END,
|
|
104
|
+
"authority_epoch" = attempt."authority_epoch",
|
|
105
|
+
"authority_visibility" = attempt."authority_visibility",
|
|
106
|
+
"authority_owner_organization_membership_id" =
|
|
107
|
+
attempt."authority_owner_organization_membership_id"
|
|
108
|
+
FROM "session_turn_attempts" AS attempt
|
|
109
|
+
JOIN "session_turns" AS turn
|
|
110
|
+
ON turn."workspace_id" = attempt."workspace_id"
|
|
111
|
+
AND turn."id" = attempt."turn_id"
|
|
112
|
+
WHERE admission."actor_kind" = 'turn'
|
|
113
|
+
AND admission."initiator_kind" = 'legacy_unattributed'
|
|
114
|
+
AND attempt."workspace_id" = admission."workspace_id"
|
|
115
|
+
AND attempt."id" = admission."attempt_id"
|
|
116
|
+
AND admission."turn_id" = attempt."turn_id";
|
|
117
|
+
|
|
118
|
+
UPDATE "sandbox_retained_processes" AS process
|
|
119
|
+
SET
|
|
120
|
+
-- A turn frozen before migration 0096 carries the subject sentinel with the
|
|
121
|
+
-- default 'service' kind. That is honestly "no causal initiator", so it is
|
|
122
|
+
-- normalized to the explicit sentinel rather than promoted to a real one.
|
|
123
|
+
"initiator_kind" = CASE
|
|
124
|
+
WHEN turn."initiator_subject_id" = 'unattributed-legacy' THEN 'legacy_unattributed'
|
|
125
|
+
ELSE turn."initiator_kind"
|
|
126
|
+
END,
|
|
127
|
+
"initiator_subject_id" = turn."initiator_subject_id",
|
|
128
|
+
"initiating_human_subject_id" = CASE
|
|
129
|
+
WHEN turn."initiator_subject_id" = 'unattributed-legacy' THEN NULL
|
|
130
|
+
ELSE turn."initiating_human_subject_id"
|
|
131
|
+
END,
|
|
132
|
+
"authority_epoch" = attempt."authority_epoch",
|
|
133
|
+
"authority_visibility" = attempt."authority_visibility",
|
|
134
|
+
"authority_owner_organization_membership_id" =
|
|
135
|
+
attempt."authority_owner_organization_membership_id"
|
|
136
|
+
FROM "session_turn_attempts" AS attempt
|
|
137
|
+
JOIN "session_turns" AS turn
|
|
138
|
+
ON turn."workspace_id" = attempt."workspace_id"
|
|
139
|
+
AND turn."id" = attempt."turn_id"
|
|
140
|
+
WHERE process."owner_actor_kind" = 'turn'
|
|
141
|
+
AND process."initiator_kind" = 'legacy_unattributed'
|
|
142
|
+
AND attempt."workspace_id" = process."workspace_id"
|
|
143
|
+
AND attempt."id" = process."owner_attempt_id"
|
|
144
|
+
AND process."owner_turn_id" = attempt."turn_id";
|
|
145
|
+
|
|
146
|
+
-- The membership that authorized a promoted human initiator is resolvable
|
|
147
|
+
-- exactly (organization_memberships is unique per account+subject). A machine
|
|
148
|
+
-- or service initiator legitimately has none. The observed revision is audit
|
|
149
|
+
-- evidence, not a fence: a role change is not a revocation.
|
|
150
|
+
UPDATE "sandbox_workspace_mutation_admissions" AS admission
|
|
151
|
+
SET
|
|
152
|
+
"initiator_organization_membership_id" = membership."id",
|
|
153
|
+
"initiator_authorization_revision" = membership."authorization_revision"
|
|
154
|
+
FROM "organization_memberships" AS membership
|
|
155
|
+
WHERE admission."initiator_kind" <> 'legacy_unattributed'
|
|
156
|
+
AND admission."initiating_human_subject_id" IS NOT NULL
|
|
157
|
+
AND admission."initiator_organization_membership_id" IS NULL
|
|
158
|
+
AND membership."account_id" = admission."account_id"
|
|
159
|
+
AND membership."subject_id" = admission."initiating_human_subject_id";
|
|
160
|
+
|
|
161
|
+
UPDATE "sandbox_retained_processes" AS process
|
|
162
|
+
SET
|
|
163
|
+
"initiator_organization_membership_id" = membership."id",
|
|
164
|
+
"initiator_authorization_revision" = membership."authorization_revision"
|
|
165
|
+
FROM "organization_memberships" AS membership
|
|
166
|
+
WHERE process."initiator_kind" <> 'legacy_unattributed'
|
|
167
|
+
AND process."initiating_human_subject_id" IS NOT NULL
|
|
168
|
+
AND process."initiator_organization_membership_id" IS NULL
|
|
169
|
+
AND membership."account_id" = process."account_id"
|
|
170
|
+
AND membership."subject_id" = process."initiating_human_subject_id";
|
|
171
|
+
|
|
172
|
+
-- 3. Contract -----------------------------------------------------------
|
|
173
|
+
-- The tuple has two INDEPENDENT halves, and conflating them was the mistake
|
|
174
|
+
-- worth avoiding: a legacy turn frozen before migration 0096 legitimately has
|
|
175
|
+
-- no causal initiator while still having a perfectly real session tenancy
|
|
176
|
+
-- epoch and visibility. Requiring both together would fence ordinary turn work
|
|
177
|
+
-- on those sessions for no security gain.
|
|
178
|
+
--
|
|
179
|
+
-- * initiator half - either a complete causal identity, or the explicit
|
|
180
|
+
-- `legacy_unattributed` sentinel with every initiator field absent, so a
|
|
181
|
+
-- partial grant can never masquerade as a whole one.
|
|
182
|
+
-- * tenancy half - either the complete epoch/visibility/owner triple, or
|
|
183
|
+
-- nothing. A private session must name its owning membership, exactly as
|
|
184
|
+
-- `sessions` and `session_turn_attempts` already require.
|
|
185
|
+
--
|
|
186
|
+
-- Only a MISSING TENANCY HALF marks a row as predating this migration, and
|
|
187
|
+
-- that is the condition that fails a retained process closed.
|
|
188
|
+
|
|
189
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
190
|
+
ADD CONSTRAINT "sandbox_workspace_mutation_admissions_initiator_check"
|
|
191
|
+
CHECK (
|
|
192
|
+
(
|
|
193
|
+
"initiator_kind" = 'legacy_unattributed'
|
|
194
|
+
AND "initiator_subject_id" = 'unattributed-legacy'
|
|
195
|
+
AND "initiating_human_subject_id" IS NULL
|
|
196
|
+
AND "initiator_organization_membership_id" IS NULL
|
|
197
|
+
AND "initiator_authorization_revision" IS NULL
|
|
198
|
+
) OR (
|
|
199
|
+
"initiator_kind" IN ('subject', 'service')
|
|
200
|
+
AND length(btrim("initiator_subject_id")) BETWEEN 1 AND 1024
|
|
201
|
+
AND "initiator_subject_id" <> 'unattributed-legacy'
|
|
202
|
+
AND (
|
|
203
|
+
"initiating_human_subject_id" IS NULL
|
|
204
|
+
OR length(btrim("initiating_human_subject_id")) BETWEEN 1 AND 1024
|
|
205
|
+
)
|
|
206
|
+
AND (
|
|
207
|
+
"initiator_organization_membership_id" IS NULL
|
|
208
|
+
OR "initiating_human_subject_id" IS NOT NULL
|
|
209
|
+
)
|
|
210
|
+
AND (
|
|
211
|
+
"initiator_authorization_revision" IS NULL
|
|
212
|
+
OR (
|
|
213
|
+
"initiator_authorization_revision" > 0
|
|
214
|
+
AND "initiator_organization_membership_id" IS NOT NULL
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
) NOT VALID;
|
|
219
|
+
|
|
220
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
221
|
+
ADD CONSTRAINT "sandbox_workspace_mutation_admissions_tenancy_check"
|
|
222
|
+
CHECK (
|
|
223
|
+
(
|
|
224
|
+
"authority_epoch" IS NULL
|
|
225
|
+
AND "authority_visibility" IS NULL
|
|
226
|
+
AND "authority_owner_organization_membership_id" IS NULL
|
|
227
|
+
) OR (
|
|
228
|
+
"authority_epoch" IS NOT NULL
|
|
229
|
+
AND "authority_visibility" IS NOT NULL
|
|
230
|
+
AND "authority_epoch" > 0
|
|
231
|
+
AND "authority_visibility" IN ('user_private', 'workspace_shared')
|
|
232
|
+
AND (
|
|
233
|
+
"authority_visibility" <> 'user_private'
|
|
234
|
+
OR "authority_owner_organization_membership_id" IS NOT NULL
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
) NOT VALID;
|
|
238
|
+
|
|
239
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
240
|
+
ADD CONSTRAINT "sandbox_retained_processes_initiator_check"
|
|
241
|
+
CHECK (
|
|
242
|
+
(
|
|
243
|
+
"initiator_kind" = 'legacy_unattributed'
|
|
244
|
+
AND "initiator_subject_id" = 'unattributed-legacy'
|
|
245
|
+
AND "initiating_human_subject_id" IS NULL
|
|
246
|
+
AND "initiator_organization_membership_id" IS NULL
|
|
247
|
+
AND "initiator_authorization_revision" IS NULL
|
|
248
|
+
) OR (
|
|
249
|
+
"initiator_kind" IN ('subject', 'service')
|
|
250
|
+
AND length(btrim("initiator_subject_id")) BETWEEN 1 AND 1024
|
|
251
|
+
AND "initiator_subject_id" <> 'unattributed-legacy'
|
|
252
|
+
AND (
|
|
253
|
+
"initiating_human_subject_id" IS NULL
|
|
254
|
+
OR length(btrim("initiating_human_subject_id")) BETWEEN 1 AND 1024
|
|
255
|
+
)
|
|
256
|
+
AND (
|
|
257
|
+
"initiator_organization_membership_id" IS NULL
|
|
258
|
+
OR "initiating_human_subject_id" IS NOT NULL
|
|
259
|
+
)
|
|
260
|
+
AND (
|
|
261
|
+
"initiator_authorization_revision" IS NULL
|
|
262
|
+
OR (
|
|
263
|
+
"initiator_authorization_revision" > 0
|
|
264
|
+
AND "initiator_organization_membership_id" IS NOT NULL
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
) NOT VALID;
|
|
269
|
+
|
|
270
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
271
|
+
ADD CONSTRAINT "sandbox_retained_processes_tenancy_check"
|
|
272
|
+
CHECK (
|
|
273
|
+
(
|
|
274
|
+
"authority_epoch" IS NULL
|
|
275
|
+
AND "authority_visibility" IS NULL
|
|
276
|
+
AND "authority_owner_organization_membership_id" IS NULL
|
|
277
|
+
) OR (
|
|
278
|
+
"authority_epoch" IS NOT NULL
|
|
279
|
+
AND "authority_visibility" IS NOT NULL
|
|
280
|
+
AND "authority_epoch" > 0
|
|
281
|
+
AND "authority_visibility" IN ('user_private', 'workspace_shared')
|
|
282
|
+
AND (
|
|
283
|
+
"authority_visibility" <> 'user_private'
|
|
284
|
+
OR "authority_owner_organization_membership_id" IS NOT NULL
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
) NOT VALID;
|
|
288
|
+
|
|
289
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
290
|
+
VALIDATE CONSTRAINT "sandbox_workspace_mutation_admissions_initiator_check";
|
|
291
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
292
|
+
VALIDATE CONSTRAINT "sandbox_workspace_mutation_admissions_tenancy_check";
|
|
293
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
294
|
+
VALIDATE CONSTRAINT "sandbox_retained_processes_initiator_check";
|
|
295
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
296
|
+
VALIDATE CONSTRAINT "sandbox_retained_processes_tenancy_check";
|
|
297
|
+
|
|
298
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
299
|
+
VALIDATE CONSTRAINT "sandbox_workspace_mutation_admissions_initiator_membership_fk";
|
|
300
|
+
ALTER TABLE "sandbox_workspace_mutation_admissions"
|
|
301
|
+
VALIDATE CONSTRAINT "sandbox_workspace_mutation_admissions_authority_owner_fk";
|
|
302
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
303
|
+
VALIDATE CONSTRAINT "sandbox_retained_processes_initiator_membership_fk";
|
|
304
|
+
ALTER TABLE "sandbox_retained_processes"
|
|
305
|
+
VALIDATE CONSTRAINT "sandbox_retained_processes_authority_owner_fk";
|
|
306
|
+
|
|
307
|
+
-- 4. The grant-identity seam --------------------------------------------
|
|
308
|
+
-- `organization_memberships` stays FORCE-RLS with no runtime DML and no
|
|
309
|
+
-- runtime SELECT (migration 0263). Admission still has to answer one narrow
|
|
310
|
+
-- question inside its own transaction - "is this exact subject's grant still
|
|
311
|
+
-- live, and which membership is it?" - so expose exactly that and nothing
|
|
312
|
+
-- more: an id, a lifecycle status and an authorization revision, fenced to the
|
|
313
|
+
-- caller's own tenant. No role, no personal workspace, no retention state, no
|
|
314
|
+
-- other subject's row, and never a secret value.
|
|
315
|
+
--
|
|
316
|
+
-- The existing 0263 assertion cannot serve this: it is scoped to the CURRENT
|
|
317
|
+
-- RLS subject, while a workspace writer must resolve the initiator frozen on
|
|
318
|
+
-- an accepted turn - a subject the worker's transaction is not acting as.
|
|
319
|
+
|
|
320
|
+
CREATE FUNCTION resolve_workspace_writer_grant_identity(
|
|
321
|
+
p_account_id uuid,
|
|
322
|
+
p_subject_id text
|
|
323
|
+
) RETURNS TABLE(membership_id uuid, membership_status text, authorization_revision bigint)
|
|
324
|
+
LANGUAGE sql
|
|
325
|
+
STABLE
|
|
326
|
+
SECURITY DEFINER
|
|
327
|
+
SET search_path FROM CURRENT
|
|
328
|
+
AS $body$
|
|
329
|
+
SELECT membership.id, membership.status, membership.authorization_revision
|
|
330
|
+
FROM organization_memberships membership
|
|
331
|
+
WHERE p_account_id IS NOT NULL
|
|
332
|
+
AND p_subject_id IS NOT NULL
|
|
333
|
+
AND membership.account_id = p_account_id
|
|
334
|
+
-- Tenant fence: a caller can only resolve grants inside the organization
|
|
335
|
+
-- its own RLS context already establishes.
|
|
336
|
+
AND membership.account_id = opengeni_private.current_account_id()
|
|
337
|
+
AND membership.subject_id = p_subject_id
|
|
338
|
+
$body$;
|
|
339
|
+
|
|
340
|
+
REVOKE ALL ON FUNCTION resolve_workspace_writer_grant_identity(uuid,text) FROM PUBLIC;
|
|
341
|
+
|
|
342
|
+
DO $workspace_writer_grant_seam$
|
|
343
|
+
DECLARE
|
|
344
|
+
data_schema text := pg_catalog.current_schema();
|
|
345
|
+
BEGIN
|
|
346
|
+
EXECUTE format(
|
|
347
|
+
'ALTER FUNCTION %I.resolve_workspace_writer_grant_identity(uuid,text) '
|
|
348
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
349
|
+
data_schema, data_schema
|
|
350
|
+
);
|
|
351
|
+
-- Same conditional grant as migration 0263: dedicated-schema/embedded
|
|
352
|
+
-- deployments replay this ledger without the opengeni_app role because the
|
|
353
|
+
-- host runs OpenGeni's queries over its own role and never uses it.
|
|
354
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
355
|
+
EXECUTE format(
|
|
356
|
+
'GRANT EXECUTE ON FUNCTION %I.resolve_workspace_writer_grant_identity(uuid,text) '
|
|
357
|
+
|| 'TO opengeni_app',
|
|
358
|
+
data_schema
|
|
359
|
+
);
|
|
360
|
+
END IF;
|
|
361
|
+
END
|
|
362
|
+
$workspace_writer_grant_seam$;
|
|
363
|
+
|
|
364
|
+
-- 5. Revocation sweep support -------------------------------------------
|
|
365
|
+
-- Membership revocation and workspace-membership removal need the live
|
|
366
|
+
-- unsettled writers owned by one exact grant identity, cheaply.
|
|
367
|
+
|
|
368
|
+
CREATE INDEX IF NOT EXISTS "sandbox_workspace_mutation_admissions_initiator_idx"
|
|
369
|
+
ON "sandbox_workspace_mutation_admissions"
|
|
370
|
+
("account_id", "initiator_organization_membership_id")
|
|
371
|
+
WHERE "settled_at" IS NULL;
|
|
372
|
+
|
|
373
|
+
CREATE INDEX IF NOT EXISTS "sandbox_retained_processes_initiator_idx"
|
|
374
|
+
ON "sandbox_retained_processes"
|
|
375
|
+
("account_id", "initiator_organization_membership_id")
|
|
376
|
+
WHERE "state" = 'active';
|
|
377
|
+
|
|
378
|
+
CREATE INDEX IF NOT EXISTS "sandbox_retained_processes_initiating_human_idx"
|
|
379
|
+
ON "sandbox_retained_processes" ("account_id", "initiating_human_subject_id")
|
|
380
|
+
WHERE "state" = 'active';
|
|
381
|
+
|
|
382
|
+
COMMENT ON COLUMN "sandbox_workspace_mutation_admissions"."initiator_kind" IS
|
|
383
|
+
'Causal principal kind for this admission: subject | service, or the '
|
|
384
|
+
'legacy_unattributed sentinel for a pre-0277 row whose authority was never '
|
|
385
|
+
'recorded. The sentinel is refused by new admission and is never written by '
|
|
386
|
+
'a post-0277 writer.';
|
|
387
|
+
COMMENT ON COLUMN "sandbox_workspace_mutation_admissions"."authority_epoch" IS
|
|
388
|
+
'Session tenancy authority epoch observed when the operation was admitted. '
|
|
389
|
+
'Identity and epoch only - never a secret value.';
|
|
390
|
+
COMMENT ON COLUMN "sandbox_retained_processes"."initiator_kind" IS
|
|
391
|
+
'Causal principal kind frozen when the process was retained. A '
|
|
392
|
+
'legacy_unattributed process keeps running; only its next workspace mutation '
|
|
393
|
+
'is refused.';
|