@opengeni/db 0.12.6 → 0.13.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/{chunk-XRVNI2GB.js → chunk-ELMUCYZF.js} +12 -2
- package/dist/chunk-ELMUCYZF.js.map +1 -0
- package/dist/chunk-N4WTE6SB.js +4033 -0
- package/dist/chunk-N4WTE6SB.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2499 -1865
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.d.ts +155 -12
- package/dist/provision-roles.js +1 -1
- package/dist/{schema-CeXWlYcG.d.ts → schema-Bh1Hr7xY.d.ts} +2052 -944
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +9 -1
- package/drizzle/0130_workspace_instruction_policies.sql +330 -0
- package/drizzle/0131_slack_bot_install_and_post_idempotency.sql +133 -0
- package/package.json +3 -3
- package/src/index.ts +356 -6
- package/src/runtime-posture.ts +12 -2
- package/src/schema.ts +87 -0
- package/src/workspace-instruction-policies-schema.ts +140 -0
- package/src/workspace-instruction-policies.ts +624 -0
- package/dist/chunk-CYA6BHV6.js +0 -3822
- package/dist/chunk-CYA6BHV6.js.map +0 -1
- package/dist/chunk-XRVNI2GB.js.map +0 -1
package/dist/schema.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'drizzle-orm';
|
|
2
2
|
import 'drizzle-orm/pg-core';
|
|
3
|
-
export { o as agentRunStates, p as apiKeys, q as auditEvents, r as billingCustomers, t as capabilityCatalogItems, u as capabilityInstallations, v as codexCapacityWaiters, x as codexCredentialLeases, y as codexResetRedemptionAttempts, z as codexRotationSettings, A as codexSubscriptionCredentials, d as composerDrafts, B as connections, C as creditLedgerEntries, D as deviceEnrollmentRequests, h as deviceEnrollmentStatusValues, E as documentBases, F as documentChunks, G as documents, g as enrollmentExposureValues, f as enrollmentOsValues, i as enrollmentStatusValues, H as enrollments, I as fileUploads, J as files, K as githubInstallationRepositories, L as githubInstallations, M as hostExportConfig, N as hostExportConsumers, O as hostExportCursorState, P as hostExportDeadLetters, Q as hostExportOutbox, R as importBatches, S as integrationOauthClients, T as integrationOauthStateNonces, U as knowledgeMemories, V as machineMetricsLatest, W as machineMetricsSeries, X as managedAccounts, Y as nestedAgentDepthConfiguration, n as newSessionDrafts, Z as packInstallations, _ as rigChanges, $ as rigVersions, a0 as rigs, j as sandboxKindValues, a1 as sandboxLeaseHolders, a2 as sandboxLeaseLivenessValues, a3 as sandboxLeases, a4 as sandboxPtySessions, a5 as sandboxRetainedProcessStateValues, a6 as sandboxRetainedProcesses, a7 as sandboxSessionEnvelopes, a8 as sandboxWorkspaceMutationActorKindValues, a9 as sandboxWorkspaceMutationAdmissions, aa as sandboxWorkspaceMutationHolderKindValues, ab as sandboxes, ac as scheduledTaskRuns, ad as scheduledTasks, ae as sessionAttemptInterruptions, s as sessionCommandReceipts, af as sessionEvents, ag as sessionGoals, ah as sessionHistoryItems, ai as sessionHumanInputRequests, aj as sessionListSnapshots, ak as sessionMcpServers, al as sessionPendingToolCalls, am as sessionPins, k as sessionRecordingCodecValues, l as sessionRecordingModeValues, m as sessionRecordingStateValues, an as sessionRecordings, ao as sessionSpawnDenials, ap as sessionSystemUpdateOutbox, aq as sessionSystemUpdates, c as sessionTurnAttempts, b as sessionTurns, ar as sessionWorkflowWakeOutbox, a as sessions, as as
|
|
3
|
+
export { o as agentRunStates, p as apiKeys, q as auditEvents, r as billingCustomers, t as capabilityCatalogItems, u as capabilityInstallations, v as codexCapacityWaiters, x as codexCredentialLeases, y as codexResetRedemptionAttempts, z as codexRotationSettings, A as codexSubscriptionCredentials, d as composerDrafts, B as connections, C as creditLedgerEntries, D as deviceEnrollmentRequests, h as deviceEnrollmentStatusValues, E as documentBases, F as documentChunks, G as documents, g as enrollmentExposureValues, f as enrollmentOsValues, i as enrollmentStatusValues, H as enrollments, I as fileUploads, J as files, K as githubInstallationRepositories, L as githubInstallations, M as hostExportConfig, N as hostExportConsumers, O as hostExportCursorState, P as hostExportDeadLetters, Q as hostExportOutbox, R as importBatches, S as integrationOauthClients, T as integrationOauthStateNonces, U as knowledgeMemories, V as machineMetricsLatest, W as machineMetricsSeries, X as managedAccounts, Y as nestedAgentDepthConfiguration, n as newSessionDrafts, Z as packInstallations, _ as rigChanges, $ as rigVersions, a0 as rigs, j as sandboxKindValues, a1 as sandboxLeaseHolders, a2 as sandboxLeaseLivenessValues, a3 as sandboxLeases, a4 as sandboxPtySessions, a5 as sandboxRetainedProcessStateValues, a6 as sandboxRetainedProcesses, a7 as sandboxSessionEnvelopes, a8 as sandboxWorkspaceMutationActorKindValues, a9 as sandboxWorkspaceMutationAdmissions, aa as sandboxWorkspaceMutationHolderKindValues, ab as sandboxes, ac as scheduledTaskRuns, ad as scheduledTasks, ae as sessionAttemptInterruptions, s as sessionCommandReceipts, af as sessionEvents, ag as sessionGoals, ah as sessionHistoryItems, ai as sessionHumanInputRequests, aj as sessionListSnapshots, ak as sessionMcpServers, al as sessionPendingToolCalls, am as sessionPins, k as sessionRecordingCodecValues, l as sessionRecordingModeValues, m as sessionRecordingStateValues, an as sessionRecordings, ao as sessionSpawnDenials, ap as sessionSystemUpdateOutbox, aq as sessionSystemUpdates, c as sessionTurnAttempts, b as sessionTurns, ar as sessionWorkflowWakeOutbox, a as sessions, as as slackBotPostOperations, at as socialConnections, au as socialPosts, av as stripeWebhookEvents, aw as usageEvents, ax as workspaceCaptures, ay as workspaceControlEvents, az as workspaceInferenceControls, aA as workspaceInstructionPolicyActivationEvents, aB as workspaceInstructionPolicyHeads, aC as workspaceInstructionPolicyRevisions, aD as workspaceMemberships, aE as workspaceModelPolicies, aF as workspacePacks, aG as workspaceSessionActivityRevisions, aH as workspaceVariableSetVariables, aI as workspaceVariableSets, w as workspaces } from './schema-Bh1Hr7xY.js';
|
package/dist/schema.js
CHANGED
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
sessionTurns,
|
|
80
80
|
sessionWorkflowWakeOutbox,
|
|
81
81
|
sessions,
|
|
82
|
+
slackBotPostOperations,
|
|
82
83
|
socialConnections,
|
|
83
84
|
socialPosts,
|
|
84
85
|
stripeWebhookEvents,
|
|
@@ -86,6 +87,9 @@ import {
|
|
|
86
87
|
workspaceCaptures,
|
|
87
88
|
workspaceControlEvents,
|
|
88
89
|
workspaceInferenceControls,
|
|
90
|
+
workspaceInstructionPolicyActivationEvents,
|
|
91
|
+
workspaceInstructionPolicyHeads,
|
|
92
|
+
workspaceInstructionPolicyRevisions,
|
|
89
93
|
workspaceMemberships,
|
|
90
94
|
workspaceModelPolicies,
|
|
91
95
|
workspacePacks,
|
|
@@ -93,7 +97,7 @@ import {
|
|
|
93
97
|
workspaceVariableSetVariables,
|
|
94
98
|
workspaceVariableSets,
|
|
95
99
|
workspaces
|
|
96
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-N4WTE6SB.js";
|
|
97
101
|
import "./chunk-PZ5AY32C.js";
|
|
98
102
|
export {
|
|
99
103
|
agentRunStates,
|
|
@@ -176,6 +180,7 @@ export {
|
|
|
176
180
|
sessionTurns,
|
|
177
181
|
sessionWorkflowWakeOutbox,
|
|
178
182
|
sessions,
|
|
183
|
+
slackBotPostOperations,
|
|
179
184
|
socialConnections,
|
|
180
185
|
socialPosts,
|
|
181
186
|
stripeWebhookEvents,
|
|
@@ -183,6 +188,9 @@ export {
|
|
|
183
188
|
workspaceCaptures,
|
|
184
189
|
workspaceControlEvents,
|
|
185
190
|
workspaceInferenceControls,
|
|
191
|
+
workspaceInstructionPolicyActivationEvents,
|
|
192
|
+
workspaceInstructionPolicyHeads,
|
|
193
|
+
workspaceInstructionPolicyRevisions,
|
|
186
194
|
workspaceMemberships,
|
|
187
195
|
workspaceModelPolicies,
|
|
188
196
|
workspacePacks,
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Backend-only workspace instruction-policy foundation. This migration performs no
|
|
3
|
+
-- backfill: workspaces without an activation head retain legacy behavior.
|
|
4
|
+
|
|
5
|
+
CREATE SEQUENCE IF NOT EXISTS "workspace_instruction_policy_revision_seq" AS bigint;
|
|
6
|
+
|
|
7
|
+
CREATE TABLE IF NOT EXISTS "workspace_instruction_policy_revisions" (
|
|
8
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
9
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
10
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
11
|
+
"revision" bigint NOT NULL DEFAULT nextval('workspace_instruction_policy_revision_seq'),
|
|
12
|
+
"kind" text NOT NULL,
|
|
13
|
+
"scope" text NOT NULL,
|
|
14
|
+
"role_key" text,
|
|
15
|
+
"content" text NOT NULL,
|
|
16
|
+
"content_hash" text NOT NULL,
|
|
17
|
+
"provenance_source" text NOT NULL,
|
|
18
|
+
"provenance_source_id" text,
|
|
19
|
+
"supersedes_revision_id" uuid REFERENCES "workspace_instruction_policy_revisions"("id") ON DELETE RESTRICT,
|
|
20
|
+
"created_by_subject_id" text NOT NULL,
|
|
21
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
22
|
+
CONSTRAINT "workspace_instruction_policy_revisions_revision_chk" CHECK ("revision" > 0),
|
|
23
|
+
CONSTRAINT "workspace_instruction_policy_revisions_target_chk" CHECK (
|
|
24
|
+
("kind" = 'charter' AND "scope" = 'global' AND "role_key" IS NULL)
|
|
25
|
+
OR ("kind" = 'policy' AND "scope" = 'global' AND "role_key" IS NULL)
|
|
26
|
+
OR ("kind" = 'policy' AND "scope" = 'role' AND "role_key" IS NOT NULL)
|
|
27
|
+
),
|
|
28
|
+
CONSTRAINT "workspace_instruction_policy_revisions_role_key_chk" CHECK (
|
|
29
|
+
"role_key" IS NULL
|
|
30
|
+
OR (
|
|
31
|
+
"role_key" = lower(btrim("role_key"))
|
|
32
|
+
AND length("role_key") BETWEEN 1 AND 64
|
|
33
|
+
AND "role_key" ~ '^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$'
|
|
34
|
+
AND "role_key" !~ '--'
|
|
35
|
+
)
|
|
36
|
+
),
|
|
37
|
+
CONSTRAINT "workspace_instruction_policy_revisions_content_chk" CHECK (
|
|
38
|
+
length(btrim("content")) > 0 AND octet_length("content") <= 1048576
|
|
39
|
+
),
|
|
40
|
+
CONSTRAINT "workspace_instruction_policy_revisions_hash_chk" CHECK (
|
|
41
|
+
"content_hash" ~ '^[0-9a-f]{64}$'
|
|
42
|
+
AND "content_hash" = encode(sha256(convert_to("content", 'UTF8')), 'hex')
|
|
43
|
+
),
|
|
44
|
+
CONSTRAINT "workspace_instruction_policy_revisions_provenance_chk" CHECK (
|
|
45
|
+
"provenance_source" IN ('human', 'onboarding', 'knowledge_proposal', 'legacy_import')
|
|
46
|
+
AND ("provenance_source_id" IS NULL OR length("provenance_source_id") BETWEEN 1 AND 512)
|
|
47
|
+
),
|
|
48
|
+
CONSTRAINT "workspace_instruction_policy_revisions_actor_chk" CHECK (
|
|
49
|
+
length(btrim("created_by_subject_id")) BETWEEN 1 AND 1024
|
|
50
|
+
),
|
|
51
|
+
CONSTRAINT "workspace_instruction_policy_revisions_workspace_revision_uq"
|
|
52
|
+
UNIQUE ("workspace_id", "revision")
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
CREATE INDEX IF NOT EXISTS "workspace_instruction_policy_revisions_workspace_history_idx"
|
|
56
|
+
ON "workspace_instruction_policy_revisions"
|
|
57
|
+
("workspace_id", "kind", "scope", "role_key", "revision" DESC);
|
|
58
|
+
|
|
59
|
+
CREATE TABLE IF NOT EXISTS "workspace_instruction_policy_heads" (
|
|
60
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
61
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
62
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
63
|
+
"kind" text NOT NULL,
|
|
64
|
+
"scope" text NOT NULL,
|
|
65
|
+
"role_key" text,
|
|
66
|
+
"revision_id" uuid NOT NULL REFERENCES "workspace_instruction_policy_revisions"("id") ON DELETE RESTRICT,
|
|
67
|
+
"revision" bigint NOT NULL,
|
|
68
|
+
"content_hash" text NOT NULL,
|
|
69
|
+
"activation_version" bigint NOT NULL,
|
|
70
|
+
"activated_at" timestamptz NOT NULL DEFAULT now(),
|
|
71
|
+
CONSTRAINT "workspace_instruction_policy_heads_target_chk" CHECK (
|
|
72
|
+
("kind" = 'charter' AND "scope" = 'global' AND "role_key" IS NULL)
|
|
73
|
+
OR ("kind" = 'policy' AND "scope" = 'global' AND "role_key" IS NULL)
|
|
74
|
+
OR ("kind" = 'policy' AND "scope" = 'role' AND "role_key" IS NOT NULL)
|
|
75
|
+
),
|
|
76
|
+
CONSTRAINT "workspace_instruction_policy_heads_role_key_chk" CHECK (
|
|
77
|
+
"role_key" IS NULL
|
|
78
|
+
OR (
|
|
79
|
+
"role_key" = lower(btrim("role_key"))
|
|
80
|
+
AND length("role_key") BETWEEN 1 AND 64
|
|
81
|
+
AND "role_key" ~ '^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$'
|
|
82
|
+
AND "role_key" !~ '--'
|
|
83
|
+
)
|
|
84
|
+
),
|
|
85
|
+
CONSTRAINT "workspace_instruction_policy_heads_revision_chk" CHECK (
|
|
86
|
+
"revision" > 0 AND "activation_version" > 0 AND "content_hash" ~ '^[0-9a-f]{64}$'
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "workspace_instruction_policy_heads_charter_uq"
|
|
91
|
+
ON "workspace_instruction_policy_heads" ("workspace_id")
|
|
92
|
+
WHERE "kind" = 'charter';
|
|
93
|
+
|
|
94
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "workspace_instruction_policy_heads_global_policy_uq"
|
|
95
|
+
ON "workspace_instruction_policy_heads" ("workspace_id")
|
|
96
|
+
WHERE "kind" = 'policy' AND "scope" = 'global';
|
|
97
|
+
|
|
98
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "workspace_instruction_policy_heads_role_policy_uq"
|
|
99
|
+
ON "workspace_instruction_policy_heads" ("workspace_id", "role_key")
|
|
100
|
+
WHERE "kind" = 'policy' AND "scope" = 'role';
|
|
101
|
+
|
|
102
|
+
CREATE TABLE IF NOT EXISTS "workspace_instruction_policy_activation_events" (
|
|
103
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
104
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
105
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
106
|
+
"kind" text NOT NULL,
|
|
107
|
+
"scope" text NOT NULL,
|
|
108
|
+
"role_key" text,
|
|
109
|
+
"type" text NOT NULL,
|
|
110
|
+
"activation_version" bigint NOT NULL,
|
|
111
|
+
"old_revision_id" uuid REFERENCES "workspace_instruction_policy_revisions"("id") ON DELETE RESTRICT,
|
|
112
|
+
"old_revision" bigint,
|
|
113
|
+
"old_content_hash" text,
|
|
114
|
+
"new_revision_id" uuid NOT NULL REFERENCES "workspace_instruction_policy_revisions"("id") ON DELETE RESTRICT,
|
|
115
|
+
"new_revision" bigint NOT NULL,
|
|
116
|
+
"new_content_hash" text NOT NULL,
|
|
117
|
+
"actor_subject_id" text NOT NULL,
|
|
118
|
+
"reason" text NOT NULL,
|
|
119
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
120
|
+
CONSTRAINT "workspace_instruction_policy_activation_events_type_chk" CHECK (
|
|
121
|
+
"type" IN ('activate', 'rollback')
|
|
122
|
+
),
|
|
123
|
+
CONSTRAINT "workspace_instruction_policy_activation_events_target_chk" CHECK (
|
|
124
|
+
("kind" = 'charter' AND "scope" = 'global' AND "role_key" IS NULL)
|
|
125
|
+
OR ("kind" = 'policy' AND "scope" = 'global' AND "role_key" IS NULL)
|
|
126
|
+
OR ("kind" = 'policy' AND "scope" = 'role' AND "role_key" IS NOT NULL)
|
|
127
|
+
),
|
|
128
|
+
CONSTRAINT "workspace_instruction_policy_activation_events_role_key_chk" CHECK (
|
|
129
|
+
"role_key" IS NULL
|
|
130
|
+
OR (
|
|
131
|
+
"role_key" = lower(btrim("role_key"))
|
|
132
|
+
AND length("role_key") BETWEEN 1 AND 64
|
|
133
|
+
AND "role_key" ~ '^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$'
|
|
134
|
+
AND "role_key" !~ '--'
|
|
135
|
+
)
|
|
136
|
+
),
|
|
137
|
+
CONSTRAINT "workspace_instruction_policy_activation_events_old_revision_chk" CHECK (
|
|
138
|
+
("old_revision_id" IS NULL AND "old_revision" IS NULL AND "old_content_hash" IS NULL)
|
|
139
|
+
OR (
|
|
140
|
+
"old_revision_id" IS NOT NULL
|
|
141
|
+
AND "old_revision" > 0
|
|
142
|
+
AND "old_content_hash" ~ '^[0-9a-f]{64}$'
|
|
143
|
+
)
|
|
144
|
+
),
|
|
145
|
+
CONSTRAINT "workspace_instruction_policy_activation_events_new_revision_chk" CHECK (
|
|
146
|
+
"new_revision" > 0
|
|
147
|
+
AND "new_content_hash" ~ '^[0-9a-f]{64}$'
|
|
148
|
+
AND "activation_version" > 0
|
|
149
|
+
),
|
|
150
|
+
CONSTRAINT "workspace_instruction_policy_activation_events_audit_chk" CHECK (
|
|
151
|
+
length(btrim("actor_subject_id")) BETWEEN 1 AND 1024
|
|
152
|
+
AND length(btrim("reason")) BETWEEN 1 AND 4096
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "workspace_instruction_policy_events_target_version_uq"
|
|
157
|
+
ON "workspace_instruction_policy_activation_events"
|
|
158
|
+
("workspace_id", "kind", "scope", coalesce("role_key", ''), "activation_version");
|
|
159
|
+
|
|
160
|
+
CREATE INDEX IF NOT EXISTS "workspace_instruction_policy_events_workspace_time_idx"
|
|
161
|
+
ON "workspace_instruction_policy_activation_events" ("workspace_id", "created_at" DESC, "id");
|
|
162
|
+
|
|
163
|
+
CREATE OR REPLACE FUNCTION workspace_instruction_policy_validate_revision_link()
|
|
164
|
+
RETURNS trigger
|
|
165
|
+
LANGUAGE plpgsql
|
|
166
|
+
AS $$
|
|
167
|
+
DECLARE
|
|
168
|
+
target_workspace_id uuid;
|
|
169
|
+
target_kind text;
|
|
170
|
+
target_scope text;
|
|
171
|
+
target_role_key text;
|
|
172
|
+
BEGIN
|
|
173
|
+
IF NEW."supersedes_revision_id" IS NULL THEN
|
|
174
|
+
RETURN NEW;
|
|
175
|
+
END IF;
|
|
176
|
+
SELECT "workspace_id", "kind", "scope", "role_key"
|
|
177
|
+
INTO target_workspace_id, target_kind, target_scope, target_role_key
|
|
178
|
+
FROM "workspace_instruction_policy_revisions"
|
|
179
|
+
WHERE "id" = NEW."supersedes_revision_id";
|
|
180
|
+
IF NOT FOUND
|
|
181
|
+
OR target_workspace_id IS DISTINCT FROM NEW."workspace_id"
|
|
182
|
+
OR target_kind IS DISTINCT FROM NEW."kind"
|
|
183
|
+
OR target_scope IS DISTINCT FROM NEW."scope"
|
|
184
|
+
OR target_role_key IS DISTINCT FROM NEW."role_key"
|
|
185
|
+
THEN
|
|
186
|
+
RAISE EXCEPTION 'superseded instruction-policy revision must use the same workspace and target'
|
|
187
|
+
USING ERRCODE = '23514';
|
|
188
|
+
END IF;
|
|
189
|
+
RETURN NEW;
|
|
190
|
+
END;
|
|
191
|
+
$$;
|
|
192
|
+
|
|
193
|
+
DROP TRIGGER IF EXISTS workspace_instruction_policy_revisions_validate_link
|
|
194
|
+
ON "workspace_instruction_policy_revisions";
|
|
195
|
+
CREATE TRIGGER workspace_instruction_policy_revisions_validate_link
|
|
196
|
+
BEFORE INSERT OR UPDATE ON "workspace_instruction_policy_revisions"
|
|
197
|
+
FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_validate_revision_link();
|
|
198
|
+
|
|
199
|
+
CREATE OR REPLACE FUNCTION workspace_instruction_policy_validate_head()
|
|
200
|
+
RETURNS trigger
|
|
201
|
+
LANGUAGE plpgsql
|
|
202
|
+
AS $$
|
|
203
|
+
BEGIN
|
|
204
|
+
IF NOT EXISTS (
|
|
205
|
+
SELECT 1
|
|
206
|
+
FROM "workspace_instruction_policy_revisions" revision
|
|
207
|
+
WHERE revision."id" = NEW."revision_id"
|
|
208
|
+
AND revision."account_id" = NEW."account_id"
|
|
209
|
+
AND revision."workspace_id" = NEW."workspace_id"
|
|
210
|
+
AND revision."kind" = NEW."kind"
|
|
211
|
+
AND revision."scope" = NEW."scope"
|
|
212
|
+
AND revision."role_key" IS NOT DISTINCT FROM NEW."role_key"
|
|
213
|
+
AND revision."revision" = NEW."revision"
|
|
214
|
+
AND revision."content_hash" = NEW."content_hash"
|
|
215
|
+
) THEN
|
|
216
|
+
RAISE EXCEPTION 'instruction-policy head must identify an exact target revision'
|
|
217
|
+
USING ERRCODE = '23514';
|
|
218
|
+
END IF;
|
|
219
|
+
RETURN NEW;
|
|
220
|
+
END;
|
|
221
|
+
$$;
|
|
222
|
+
|
|
223
|
+
DROP TRIGGER IF EXISTS workspace_instruction_policy_heads_validate_revision
|
|
224
|
+
ON "workspace_instruction_policy_heads";
|
|
225
|
+
CREATE TRIGGER workspace_instruction_policy_heads_validate_revision
|
|
226
|
+
BEFORE INSERT OR UPDATE ON "workspace_instruction_policy_heads"
|
|
227
|
+
FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_validate_head();
|
|
228
|
+
|
|
229
|
+
CREATE OR REPLACE FUNCTION workspace_instruction_policy_validate_event()
|
|
230
|
+
RETURNS trigger
|
|
231
|
+
LANGUAGE plpgsql
|
|
232
|
+
AS $$
|
|
233
|
+
BEGIN
|
|
234
|
+
IF NOT EXISTS (
|
|
235
|
+
SELECT 1
|
|
236
|
+
FROM "workspace_instruction_policy_revisions" revision
|
|
237
|
+
WHERE revision."id" = NEW."new_revision_id"
|
|
238
|
+
AND revision."account_id" = NEW."account_id"
|
|
239
|
+
AND revision."workspace_id" = NEW."workspace_id"
|
|
240
|
+
AND revision."kind" = NEW."kind"
|
|
241
|
+
AND revision."scope" = NEW."scope"
|
|
242
|
+
AND revision."role_key" IS NOT DISTINCT FROM NEW."role_key"
|
|
243
|
+
AND revision."revision" = NEW."new_revision"
|
|
244
|
+
AND revision."content_hash" = NEW."new_content_hash"
|
|
245
|
+
) THEN
|
|
246
|
+
RAISE EXCEPTION 'instruction-policy activation event has an invalid new revision'
|
|
247
|
+
USING ERRCODE = '23514';
|
|
248
|
+
END IF;
|
|
249
|
+
IF NEW."old_revision_id" IS NOT NULL AND NOT EXISTS (
|
|
250
|
+
SELECT 1
|
|
251
|
+
FROM "workspace_instruction_policy_revisions" revision
|
|
252
|
+
WHERE revision."id" = NEW."old_revision_id"
|
|
253
|
+
AND revision."account_id" = NEW."account_id"
|
|
254
|
+
AND revision."workspace_id" = NEW."workspace_id"
|
|
255
|
+
AND revision."kind" = NEW."kind"
|
|
256
|
+
AND revision."scope" = NEW."scope"
|
|
257
|
+
AND revision."role_key" IS NOT DISTINCT FROM NEW."role_key"
|
|
258
|
+
AND revision."revision" = NEW."old_revision"
|
|
259
|
+
AND revision."content_hash" = NEW."old_content_hash"
|
|
260
|
+
) THEN
|
|
261
|
+
RAISE EXCEPTION 'instruction-policy activation event has an invalid old revision'
|
|
262
|
+
USING ERRCODE = '23514';
|
|
263
|
+
END IF;
|
|
264
|
+
RETURN NEW;
|
|
265
|
+
END;
|
|
266
|
+
$$;
|
|
267
|
+
|
|
268
|
+
DROP TRIGGER IF EXISTS workspace_instruction_policy_events_validate_revisions
|
|
269
|
+
ON "workspace_instruction_policy_activation_events";
|
|
270
|
+
CREATE TRIGGER workspace_instruction_policy_events_validate_revisions
|
|
271
|
+
BEFORE INSERT OR UPDATE ON "workspace_instruction_policy_activation_events"
|
|
272
|
+
FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_validate_event();
|
|
273
|
+
|
|
274
|
+
CREATE OR REPLACE FUNCTION workspace_instruction_policy_reject_mutation()
|
|
275
|
+
RETURNS trigger
|
|
276
|
+
LANGUAGE plpgsql
|
|
277
|
+
AS $$
|
|
278
|
+
BEGIN
|
|
279
|
+
-- PostgreSQL implements ON DELETE CASCADE with a parent constraint trigger.
|
|
280
|
+
-- By the time that trigger deletes child rows, the workspace is absent and
|
|
281
|
+
-- this child trigger is nested. Require both facts so direct history deletes
|
|
282
|
+
-- and deletes issued by unrelated triggers remain fail-closed.
|
|
283
|
+
IF TG_OP = 'DELETE'
|
|
284
|
+
AND pg_trigger_depth() > 1
|
|
285
|
+
AND NOT EXISTS (
|
|
286
|
+
SELECT 1
|
|
287
|
+
FROM "workspaces" workspace
|
|
288
|
+
WHERE workspace."id" = OLD."workspace_id"
|
|
289
|
+
)
|
|
290
|
+
THEN
|
|
291
|
+
RETURN OLD;
|
|
292
|
+
END IF;
|
|
293
|
+
RAISE EXCEPTION 'workspace instruction-policy history is immutable'
|
|
294
|
+
USING ERRCODE = '55000';
|
|
295
|
+
END;
|
|
296
|
+
$$;
|
|
297
|
+
|
|
298
|
+
DROP TRIGGER IF EXISTS workspace_instruction_policy_revisions_immutable
|
|
299
|
+
ON "workspace_instruction_policy_revisions";
|
|
300
|
+
CREATE TRIGGER workspace_instruction_policy_revisions_immutable
|
|
301
|
+
BEFORE UPDATE OR DELETE ON "workspace_instruction_policy_revisions"
|
|
302
|
+
FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_reject_mutation();
|
|
303
|
+
|
|
304
|
+
DROP TRIGGER IF EXISTS workspace_instruction_policy_events_immutable
|
|
305
|
+
ON "workspace_instruction_policy_activation_events";
|
|
306
|
+
CREATE TRIGGER workspace_instruction_policy_events_immutable
|
|
307
|
+
BEFORE UPDATE OR DELETE ON "workspace_instruction_policy_activation_events"
|
|
308
|
+
FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_reject_mutation();
|
|
309
|
+
|
|
310
|
+
ALTER TABLE "workspace_instruction_policy_revisions" ENABLE ROW LEVEL SECURITY;
|
|
311
|
+
ALTER TABLE "workspace_instruction_policy_revisions" FORCE ROW LEVEL SECURITY;
|
|
312
|
+
ALTER TABLE "workspace_instruction_policy_heads" ENABLE ROW LEVEL SECURITY;
|
|
313
|
+
ALTER TABLE "workspace_instruction_policy_heads" FORCE ROW LEVEL SECURITY;
|
|
314
|
+
ALTER TABLE "workspace_instruction_policy_activation_events" ENABLE ROW LEVEL SECURITY;
|
|
315
|
+
ALTER TABLE "workspace_instruction_policy_activation_events" FORCE ROW LEVEL SECURITY;
|
|
316
|
+
|
|
317
|
+
DROP POLICY IF EXISTS workspace_isolation ON "workspace_instruction_policy_revisions";
|
|
318
|
+
CREATE POLICY workspace_isolation ON "workspace_instruction_policy_revisions"
|
|
319
|
+
USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
|
|
320
|
+
WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
|
|
321
|
+
|
|
322
|
+
DROP POLICY IF EXISTS workspace_isolation ON "workspace_instruction_policy_heads";
|
|
323
|
+
CREATE POLICY workspace_isolation ON "workspace_instruction_policy_heads"
|
|
324
|
+
USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
|
|
325
|
+
WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
|
|
326
|
+
|
|
327
|
+
DROP POLICY IF EXISTS workspace_isolation ON "workspace_instruction_policy_activation_events";
|
|
328
|
+
CREATE POLICY workspace_isolation ON "workspace_instruction_policy_activation_events"
|
|
329
|
+
USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
|
|
330
|
+
WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- New readers trust only dedicated-route Slack installs whose server marker
|
|
3
|
+
-- matches the exact credential version. Existing rows and rows created by old
|
|
4
|
+
-- generic API pods remain markerless and fail closed. The trigger also clears a
|
|
5
|
+
-- marker if a rolling old writer mutates credential/identity fields without a
|
|
6
|
+
-- fresh verification in that same statement.
|
|
7
|
+
|
|
8
|
+
ALTER TABLE "connections"
|
|
9
|
+
ADD COLUMN IF NOT EXISTS "verified_install_at" timestamptz,
|
|
10
|
+
ADD COLUMN IF NOT EXISTS "verified_install_version" integer;
|
|
11
|
+
|
|
12
|
+
DO $constraint$
|
|
13
|
+
BEGIN
|
|
14
|
+
IF NOT EXISTS (
|
|
15
|
+
SELECT 1
|
|
16
|
+
FROM pg_constraint
|
|
17
|
+
WHERE conname = 'connections_verified_install_check'
|
|
18
|
+
AND conrelid = 'connections'::regclass
|
|
19
|
+
) THEN
|
|
20
|
+
ALTER TABLE "connections"
|
|
21
|
+
ADD CONSTRAINT "connections_verified_install_check"
|
|
22
|
+
CHECK (
|
|
23
|
+
(
|
|
24
|
+
"verified_install_at" IS NULL
|
|
25
|
+
AND "verified_install_version" IS NULL
|
|
26
|
+
)
|
|
27
|
+
OR (
|
|
28
|
+
"verified_install_at" IS NOT NULL
|
|
29
|
+
AND "verified_install_version" = "version"
|
|
30
|
+
AND "verified_install_version" > 0
|
|
31
|
+
AND "subject_id" IS NULL
|
|
32
|
+
AND "provider_domain" = 'slack.com'
|
|
33
|
+
AND "kind" = 'app_install'
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
END IF;
|
|
37
|
+
END $constraint$;
|
|
38
|
+
|
|
39
|
+
CREATE OR REPLACE FUNCTION opengeni_private.invalidate_stale_verified_install()
|
|
40
|
+
RETURNS trigger
|
|
41
|
+
LANGUAGE plpgsql
|
|
42
|
+
AS $function$
|
|
43
|
+
BEGIN
|
|
44
|
+
IF (
|
|
45
|
+
NEW."subject_id" IS DISTINCT FROM OLD."subject_id"
|
|
46
|
+
OR NEW."provider_domain" IS DISTINCT FROM OLD."provider_domain"
|
|
47
|
+
OR NEW."kind" IS DISTINCT FROM OLD."kind"
|
|
48
|
+
OR NEW."credential_encrypted" IS DISTINCT FROM OLD."credential_encrypted"
|
|
49
|
+
OR NEW."granted_scopes" IS DISTINCT FROM OLD."granted_scopes"
|
|
50
|
+
OR NEW."metadata" IS DISTINCT FROM OLD."metadata"
|
|
51
|
+
OR NEW."version" IS DISTINCT FROM OLD."version"
|
|
52
|
+
)
|
|
53
|
+
AND NEW."verified_install_at" IS NOT DISTINCT FROM OLD."verified_install_at"
|
|
54
|
+
AND NEW."verified_install_version" IS NOT DISTINCT FROM OLD."verified_install_version"
|
|
55
|
+
THEN
|
|
56
|
+
NEW."verified_install_at" := NULL;
|
|
57
|
+
NEW."verified_install_version" := NULL;
|
|
58
|
+
END IF;
|
|
59
|
+
RETURN NEW;
|
|
60
|
+
END;
|
|
61
|
+
$function$;
|
|
62
|
+
|
|
63
|
+
DROP TRIGGER IF EXISTS connections_invalidate_stale_verified_install ON "connections";
|
|
64
|
+
CREATE TRIGGER connections_invalidate_stale_verified_install
|
|
65
|
+
BEFORE UPDATE OF
|
|
66
|
+
"subject_id", "provider_domain", "kind", "credential_encrypted",
|
|
67
|
+
"granted_scopes", "metadata", "version",
|
|
68
|
+
"verified_install_at", "verified_install_version"
|
|
69
|
+
ON "connections"
|
|
70
|
+
FOR EACH ROW
|
|
71
|
+
EXECUTE FUNCTION opengeni_private.invalidate_stale_verified_install();
|
|
72
|
+
|
|
73
|
+
CREATE TABLE "slack_bot_post_operations" (
|
|
74
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
75
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
76
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
77
|
+
"connection_id" uuid NOT NULL REFERENCES "connections"("id") ON DELETE CASCADE,
|
|
78
|
+
"operation_id" uuid NOT NULL,
|
|
79
|
+
"client_message_id" uuid NOT NULL,
|
|
80
|
+
"target_kind" text NOT NULL,
|
|
81
|
+
"target_id" text NOT NULL,
|
|
82
|
+
"request_digest" text NOT NULL,
|
|
83
|
+
"status" text NOT NULL,
|
|
84
|
+
"claim_holder_id" uuid,
|
|
85
|
+
"claim_expires_at" timestamptz,
|
|
86
|
+
"attempt_count" integer NOT NULL DEFAULT 0,
|
|
87
|
+
"last_failure_code" text,
|
|
88
|
+
"slack_channel_id" text,
|
|
89
|
+
"slack_message_timestamp" text,
|
|
90
|
+
"completed_at" timestamptz,
|
|
91
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
92
|
+
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
93
|
+
CONSTRAINT "slack_bot_post_operations_workspace_operation_uq"
|
|
94
|
+
UNIQUE ("workspace_id", "connection_id", "operation_id"),
|
|
95
|
+
CONSTRAINT "slack_bot_post_operations_target_kind_check"
|
|
96
|
+
CHECK ("target_kind" IN ('channel', 'user')),
|
|
97
|
+
CONSTRAINT "slack_bot_post_operations_status_check"
|
|
98
|
+
CHECK ("status" IN ('provider_started', 'completed')),
|
|
99
|
+
CONSTRAINT "slack_bot_post_operations_identity_check"
|
|
100
|
+
CHECK (
|
|
101
|
+
"client_message_id" = "operation_id"
|
|
102
|
+
AND length("target_id") BETWEEN 1 AND 64
|
|
103
|
+
AND "request_digest" ~ '^[0-9a-f]{64}$'
|
|
104
|
+
AND "attempt_count" > 0
|
|
105
|
+
AND (("claim_holder_id" IS NULL) = ("claim_expires_at" IS NULL))
|
|
106
|
+
),
|
|
107
|
+
CONSTRAINT "slack_bot_post_operations_completion_check"
|
|
108
|
+
CHECK (
|
|
109
|
+
(
|
|
110
|
+
"status" = 'provider_started'
|
|
111
|
+
AND "slack_channel_id" IS NULL
|
|
112
|
+
AND "slack_message_timestamp" IS NULL
|
|
113
|
+
AND "completed_at" IS NULL
|
|
114
|
+
)
|
|
115
|
+
OR (
|
|
116
|
+
"status" = 'completed'
|
|
117
|
+
AND "claim_holder_id" IS NULL
|
|
118
|
+
AND "claim_expires_at" IS NULL
|
|
119
|
+
AND "slack_channel_id" IS NOT NULL
|
|
120
|
+
AND "slack_message_timestamp" IS NOT NULL
|
|
121
|
+
AND "completed_at" IS NOT NULL
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
CREATE INDEX "slack_bot_post_operations_workspace_status_idx"
|
|
127
|
+
ON "slack_bot_post_operations" ("workspace_id", "status", "updated_at");
|
|
128
|
+
|
|
129
|
+
ALTER TABLE "slack_bot_post_operations" ENABLE ROW LEVEL SECURITY;
|
|
130
|
+
ALTER TABLE "slack_bot_post_operations" FORCE ROW LEVEL SECURITY;
|
|
131
|
+
CREATE POLICY workspace_isolation ON "slack_bot_post_operations"
|
|
132
|
+
USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
|
|
133
|
+
WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@opengeni/codex": "^0.2.7",
|
|
54
|
-
"@opengeni/config": "^0.7.
|
|
55
|
-
"@opengeni/contracts": "^0.
|
|
54
|
+
"@opengeni/config": "^0.7.7",
|
|
55
|
+
"@opengeni/contracts": "^0.20.0",
|
|
56
56
|
"@opengeni/network": "^0.1.1",
|
|
57
57
|
"drizzle-orm": "^0.45.2",
|
|
58
58
|
"postgres": "^3.4.7"
|