thinkwork-cli 0.13.1 → 0.13.2
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/{api-client-4DZ266J5.js → api-client-JYZXYQJR.js} +3 -1
- package/dist/bowser-6Z6JFAIR.js +2822 -0
- package/dist/chunk-DV3MRBR5.js +43 -0
- package/dist/chunk-DVPBI5HM.js +1728 -0
- package/dist/chunk-EI5ZX7CN.js +522 -0
- package/dist/chunk-EUJ7X6YD.js +126 -0
- package/dist/chunk-GG5WKBOX.js +436 -0
- package/dist/chunk-I64EZSU5.js +358 -0
- package/dist/chunk-ILZKG5WV.js +6151 -0
- package/dist/chunk-KMIJ5KGT.js +128 -0
- package/dist/chunk-OCPHU34C.js +996 -0
- package/dist/chunk-QOERT44A.js +3286 -0
- package/dist/chunk-XYM7PE2A.js +2264 -0
- package/dist/{chunk-Y2WHJECR.js → chunk-Z2F6RJFR.js} +1 -39
- package/dist/cli.js +43080 -13150
- package/dist/dist-es-CRV46D5B.js +315 -0
- package/dist/dist-es-DCY4IC3Y.js +2000 -0
- package/dist/dist-es-ENBUC3JP.js +14726 -0
- package/dist/dist-es-G54LBMRN.js +48 -0
- package/dist/dist-es-G7PTG75M.js +176 -0
- package/dist/dist-es-GKDBE7E6.js +88 -0
- package/dist/dist-es-HY3535OI.js +70 -0
- package/dist/dist-es-KL7Y5NB2.js +377 -0
- package/dist/dist-es-LDE437AM.js +170 -0
- package/dist/dist-es-P26FWFLT.js +485 -0
- package/dist/dist-es-Q57SZXZ7.js +70 -0
- package/dist/dist-es-R7FGL2IB.js +88 -0
- package/dist/dist-es-USORBXZ5.js +318 -0
- package/dist/dist-es-ZKC2QKD2.js +377 -0
- package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
- package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
- package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
- package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
- package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
- package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
- package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
- package/dist/drizzle/0227_analyst_reader_role.sql +6 -0
- package/dist/drizzle/0230_analyst_rls.sql +9 -0
- package/dist/drizzle/0250_kg_schema_extraction.sql +27 -0
- package/dist/drizzle/0251_brain_stragglers.sql +21 -0
- package/dist/drizzle/0253_drop_brain_substrate.sql +2 -0
- package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
- package/dist/drizzle/0261_managed_harness_multiplayer.sql +313 -0
- package/dist/drizzle/0261_native_auth_control_plane.sql +328 -0
- package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
- package/dist/drizzle/0262_parallel_harness_threads.sql +9 -0
- package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
- package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
- package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
- package/dist/drizzle/0264_harness_tool_execution_ledger.sql +113 -0
- package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
- package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
- package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
- package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
- package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
- package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
- package/dist/drizzle/0269_ontology_system_map.sql +31 -0
- package/dist/event-streams-7JNLJEEL.js +894 -0
- package/dist/event-streams-KDF5RWR6.js +1515 -0
- package/dist/loadSso-DJVB7BXE.js +576 -0
- package/dist/loadSso-JAMNA3WY.js +583 -0
- package/dist/plugins/catalog/package.json +1 -2
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +0 -69
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
- package/dist/plugins/catalog/src/contracts.ts +2 -179
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
- package/dist/signin-UGDWTGS4.js +821 -0
- package/dist/sso-oidc-Q4VXANXY.js +837 -0
- package/dist/sts-CWLSE3GH.js +1241 -0
- package/dist/sts-DNZLNVOV.js +1245 -0
- package/dist/terraform/examples/greenfield/main.tf +223 -66
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +15 -1
- package/dist/terraform/modules/app/agentcore-gateway/main.tf +305 -0
- package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +44 -0
- package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +93 -0
- package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +33 -0
- package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +543 -0
- package/dist/terraform/modules/app/agentcore-gateway/variables.tf +70 -0
- package/dist/terraform/modules/app/agentcore-harness/main.tf +237 -8
- package/dist/terraform/modules/app/agentcore-harness/outputs.tf +63 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +5 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +430 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +180 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +193 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +5 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +5 -0
- package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +5 -0
- package/dist/terraform/modules/app/agentcore-harness/variables.tf +72 -1
- package/dist/terraform/modules/app/agentcore-identity/main.tf +173 -0
- package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +122 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +25 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +40 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +74 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +61 -0
- package/dist/terraform/modules/app/agentcore-identity/variables.tf +54 -0
- package/dist/terraform/modules/app/appsync-subscriptions/main.tf +127 -21
- package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
- package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
- package/dist/terraform/modules/app/deployment-control-plane/runner.py +966 -27
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +581 -22
- package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +88 -0
- package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +91 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +298 -74
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +99 -54
- package/dist/terraform/modules/app/lambda-api/main.tf +24 -0
- package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +181 -4
- package/dist/terraform/modules/app/lambda-api/outputs.tf +35 -0
- package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
- package/dist/terraform/modules/app/lambda-api/variables.tf +109 -16
- package/dist/terraform/modules/foundation/cognito/main.tf +294 -72
- package/dist/terraform/modules/foundation/cognito/outputs.tf +42 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +114 -4
- package/dist/terraform/modules/thinkwork/main.tf +269 -46
- package/dist/terraform/modules/thinkwork/outputs.tf +136 -8
- package/dist/terraform/modules/thinkwork/variables.tf +124 -17
- package/dist/terraform/schema.graphql +69 -36
- package/dist/workspace-defaults/files/USER.md +1 -0
- package/package.json +3 -2
- package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +0 -11
- package/dist/plugins/workos-auth/package.json +0 -22
- package/dist/plugins/workos-auth/src/index.ts +0 -30
- package/dist/plugins/workos-auth/src/manifest.ts +0 -40
- package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
- package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
- package/dist/plugins/workos-auth/tsconfig.json +0 -8
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
-- creates: public.harness_managed_thread_enrollments
|
|
2
|
+
-- creates: public.thread_public_events
|
|
3
|
+
-- creates: public.harness_participant_sessions
|
|
4
|
+
-- creates: public.harness_participant_session_events
|
|
5
|
+
-- creates: public.harness_governed_tool_executions
|
|
6
|
+
-- creates: public.harness_disclosure_decisions
|
|
7
|
+
-- creates-function: public.capture_harness_message_public_event
|
|
8
|
+
-- creates-function: public.capture_harness_artifact_public_event
|
|
9
|
+
-- creates-trigger: public.messages.trg_capture_harness_message_public_event
|
|
10
|
+
-- creates-trigger: public.message_artifacts.trg_capture_harness_artifact_public_event
|
|
11
|
+
-- Managed multiplayer Harness proof ledger. Hand-rolled because this migration
|
|
12
|
+
-- also installs admission triggers over existing canonical message tables.
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS public.harness_managed_thread_enrollments (
|
|
15
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
16
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
17
|
+
thread_id uuid NOT NULL REFERENCES public.threads(id) ON DELETE CASCADE,
|
|
18
|
+
logical_agent_id uuid NOT NULL REFERENCES public.agents(id),
|
|
19
|
+
trust_profile text NOT NULL,
|
|
20
|
+
harness_arn text NOT NULL,
|
|
21
|
+
qualifier text NOT NULL,
|
|
22
|
+
resolved_version text NOT NULL,
|
|
23
|
+
session_strategy text NOT NULL DEFAULT 'fresh'
|
|
24
|
+
CHECK (session_strategy = 'fresh'),
|
|
25
|
+
prior_runtime text NOT NULL,
|
|
26
|
+
status text NOT NULL DEFAULT 'active'
|
|
27
|
+
CHECK (status IN ('active', 'restoring', 'restored', 'failed')),
|
|
28
|
+
enrolled_by_user_id uuid NOT NULL REFERENCES public.users(id),
|
|
29
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
30
|
+
restored_at timestamptz,
|
|
31
|
+
UNIQUE (tenant_id, thread_id)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_harness_enrollment_active_profile
|
|
35
|
+
ON public.harness_managed_thread_enrollments (tenant_id, trust_profile)
|
|
36
|
+
WHERE status = 'active';
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS public.thread_public_events (
|
|
39
|
+
id bigserial PRIMARY KEY,
|
|
40
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
41
|
+
thread_id uuid NOT NULL REFERENCES public.threads(id) ON DELETE CASCADE,
|
|
42
|
+
source_kind text NOT NULL CHECK (source_kind IN ('message', 'message_artifact')),
|
|
43
|
+
source_id uuid NOT NULL,
|
|
44
|
+
source_version text NOT NULL,
|
|
45
|
+
event_kind text NOT NULL CHECK (event_kind IN ('insert', 'invalidate')),
|
|
46
|
+
canonical_digest text NOT NULL,
|
|
47
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
48
|
+
UNIQUE (tenant_id, thread_id, source_kind, source_id, source_version)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_thread_public_events_prefix
|
|
52
|
+
ON public.thread_public_events (tenant_id, thread_id, id);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS public.harness_participant_sessions (
|
|
55
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
56
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
57
|
+
enrollment_id uuid NOT NULL REFERENCES public.harness_managed_thread_enrollments(id) ON DELETE CASCADE,
|
|
58
|
+
thread_id uuid NOT NULL REFERENCES public.threads(id) ON DELETE CASCADE,
|
|
59
|
+
participant_user_id uuid NOT NULL REFERENCES public.users(id),
|
|
60
|
+
turn_id uuid NOT NULL REFERENCES public.thread_turns(id) ON DELETE CASCADE,
|
|
61
|
+
runtime_session_id text NOT NULL UNIQUE,
|
|
62
|
+
generation integer NOT NULL DEFAULT 1 CHECK (generation = 1),
|
|
63
|
+
captured_high_water bigint NOT NULL,
|
|
64
|
+
applied_high_water bigint,
|
|
65
|
+
qualifier text NOT NULL,
|
|
66
|
+
resolved_version text NOT NULL,
|
|
67
|
+
base_fingerprint text NOT NULL,
|
|
68
|
+
participant_fingerprint text NOT NULL,
|
|
69
|
+
state text NOT NULL DEFAULT 'allocated'
|
|
70
|
+
CHECK (state IN ('allocated', 'running', 'finalizing', 'completed', 'abandoned')),
|
|
71
|
+
failure_reason text,
|
|
72
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
73
|
+
started_at timestamptz,
|
|
74
|
+
finished_at timestamptz,
|
|
75
|
+
UNIQUE (tenant_id, turn_id),
|
|
76
|
+
CHECK (
|
|
77
|
+
(state IN ('completed', 'abandoned') AND finished_at IS NOT NULL)
|
|
78
|
+
OR (state NOT IN ('completed', 'abandoned') AND finished_at IS NULL)
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CREATE INDEX IF NOT EXISTS idx_harness_participant_sessions_thread
|
|
83
|
+
ON public.harness_participant_sessions
|
|
84
|
+
(tenant_id, thread_id, participant_user_id, created_at);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS public.harness_participant_session_events (
|
|
87
|
+
id bigserial PRIMARY KEY,
|
|
88
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
89
|
+
session_id uuid NOT NULL REFERENCES public.harness_participant_sessions(id) ON DELETE CASCADE,
|
|
90
|
+
turn_id uuid NOT NULL REFERENCES public.thread_turns(id) ON DELETE CASCADE,
|
|
91
|
+
event_type text NOT NULL,
|
|
92
|
+
from_state text,
|
|
93
|
+
to_state text NOT NULL
|
|
94
|
+
CHECK (to_state IN ('allocated', 'running', 'finalizing', 'completed', 'abandoned')),
|
|
95
|
+
reason_code text,
|
|
96
|
+
applied_high_water bigint,
|
|
97
|
+
evidence jsonb,
|
|
98
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
CREATE INDEX IF NOT EXISTS idx_harness_session_events_session
|
|
102
|
+
ON public.harness_participant_session_events (session_id, id);
|
|
103
|
+
|
|
104
|
+
CREATE TABLE IF NOT EXISTS public.harness_governed_tool_executions (
|
|
105
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
106
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
107
|
+
turn_id uuid NOT NULL REFERENCES public.thread_turns(id) ON DELETE CASCADE,
|
|
108
|
+
participant_user_id uuid NOT NULL REFERENCES public.users(id),
|
|
109
|
+
session_id uuid NOT NULL REFERENCES public.harness_participant_sessions(id) ON DELETE CASCADE,
|
|
110
|
+
idempotency_key text NOT NULL,
|
|
111
|
+
audience text NOT NULL,
|
|
112
|
+
operation text NOT NULL,
|
|
113
|
+
tool_use_id text NOT NULL,
|
|
114
|
+
input_digest text NOT NULL,
|
|
115
|
+
state text NOT NULL DEFAULT 'claimed'
|
|
116
|
+
CHECK (state IN ('claimed', 'completed', 'failed', 'ambiguous')),
|
|
117
|
+
policy_decision_id text,
|
|
118
|
+
credential_owner_alias text,
|
|
119
|
+
sanitized_result jsonb,
|
|
120
|
+
failure_reason text,
|
|
121
|
+
claimed_at timestamptz NOT NULL DEFAULT now(),
|
|
122
|
+
completed_at timestamptz,
|
|
123
|
+
UNIQUE (tenant_id, idempotency_key)
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_harness_governed_tool_turn
|
|
127
|
+
ON public.harness_governed_tool_executions (turn_id);
|
|
128
|
+
|
|
129
|
+
CREATE TABLE IF NOT EXISTS public.harness_disclosure_decisions (
|
|
130
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
131
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
132
|
+
participant_user_id uuid NOT NULL REFERENCES public.users(id),
|
|
133
|
+
thread_id uuid NOT NULL REFERENCES public.threads(id) ON DELETE CASCADE,
|
|
134
|
+
turn_id uuid NOT NULL REFERENCES public.thread_turns(id) ON DELETE CASCADE,
|
|
135
|
+
session_id uuid NOT NULL REFERENCES public.harness_participant_sessions(id) ON DELETE CASCADE,
|
|
136
|
+
operation text NOT NULL,
|
|
137
|
+
projection_digest text NOT NULL,
|
|
138
|
+
status text NOT NULL
|
|
139
|
+
CHECK (status IN ('published', 'withheld', 'confirmation_required')),
|
|
140
|
+
reason_code text NOT NULL,
|
|
141
|
+
expires_at timestamptz NOT NULL,
|
|
142
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
CREATE INDEX IF NOT EXISTS idx_harness_disclosure_turn
|
|
146
|
+
ON public.harness_disclosure_decisions (turn_id);
|
|
147
|
+
|
|
148
|
+
CREATE OR REPLACE FUNCTION public.capture_harness_message_public_event()
|
|
149
|
+
RETURNS trigger
|
|
150
|
+
LANGUAGE plpgsql
|
|
151
|
+
AS $$
|
|
152
|
+
DECLARE
|
|
153
|
+
digest_value text;
|
|
154
|
+
version_value text;
|
|
155
|
+
event_value text;
|
|
156
|
+
admitted_before boolean;
|
|
157
|
+
admitted_now boolean;
|
|
158
|
+
BEGIN
|
|
159
|
+
IF TG_OP = 'DELETE' THEN
|
|
160
|
+
IF NOT EXISTS (
|
|
161
|
+
SELECT 1 FROM public.thread_public_events e
|
|
162
|
+
WHERE e.tenant_id = OLD.tenant_id
|
|
163
|
+
AND e.thread_id = OLD.thread_id
|
|
164
|
+
AND e.source_kind = 'message'
|
|
165
|
+
AND e.source_id = OLD.id
|
|
166
|
+
) THEN
|
|
167
|
+
RETURN OLD;
|
|
168
|
+
END IF;
|
|
169
|
+
digest_value := encode(digest(concat_ws('|', OLD.role, coalesce(OLD.content, ''), coalesce(OLD.parts::text, ''), coalesce(OLD.metadata::text, '')), 'sha256'), 'hex');
|
|
170
|
+
version_value := 'delete:' || digest_value;
|
|
171
|
+
event_value := 'invalidate';
|
|
172
|
+
INSERT INTO public.thread_public_events (
|
|
173
|
+
tenant_id, thread_id, source_kind, source_id, source_version,
|
|
174
|
+
event_kind, canonical_digest
|
|
175
|
+
) VALUES (
|
|
176
|
+
OLD.tenant_id, OLD.thread_id, 'message', OLD.id,
|
|
177
|
+
version_value, event_value, digest_value
|
|
178
|
+
) ON CONFLICT DO NOTHING;
|
|
179
|
+
RETURN OLD;
|
|
180
|
+
END IF;
|
|
181
|
+
|
|
182
|
+
admitted_now := NEW.role IN ('user', 'assistant')
|
|
183
|
+
AND coalesce(NEW.metadata->>'visibility', 'public') = 'public'
|
|
184
|
+
AND coalesce(NEW.metadata->>'disclosure_status', 'published') NOT IN ('withheld', 'confirmation_required')
|
|
185
|
+
AND EXISTS (
|
|
186
|
+
SELECT 1 FROM public.harness_managed_thread_enrollments e
|
|
187
|
+
WHERE e.tenant_id = NEW.tenant_id
|
|
188
|
+
AND e.thread_id = NEW.thread_id
|
|
189
|
+
AND e.status = 'active'
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
IF TG_OP = 'UPDATE' THEN
|
|
193
|
+
admitted_before := EXISTS (
|
|
194
|
+
SELECT 1 FROM public.thread_public_events e
|
|
195
|
+
WHERE e.tenant_id = OLD.tenant_id
|
|
196
|
+
AND e.thread_id = OLD.thread_id
|
|
197
|
+
AND e.source_kind = 'message'
|
|
198
|
+
AND e.source_id = OLD.id
|
|
199
|
+
);
|
|
200
|
+
IF admitted_before THEN
|
|
201
|
+
digest_value := encode(digest(concat_ws('|', OLD.role, coalesce(OLD.content, ''), coalesce(OLD.parts::text, ''), coalesce(OLD.metadata::text, '')), 'sha256'), 'hex');
|
|
202
|
+
version_value := 'invalidate:update:' || encode(digest(concat_ws('|', NEW.role, coalesce(NEW.content, ''), coalesce(NEW.parts::text, ''), coalesce(NEW.metadata::text, '')), 'sha256'), 'hex');
|
|
203
|
+
INSERT INTO public.thread_public_events (
|
|
204
|
+
tenant_id, thread_id, source_kind, source_id, source_version,
|
|
205
|
+
event_kind, canonical_digest
|
|
206
|
+
) VALUES (
|
|
207
|
+
OLD.tenant_id, OLD.thread_id, 'message', OLD.id,
|
|
208
|
+
version_value, 'invalidate', digest_value
|
|
209
|
+
) ON CONFLICT DO NOTHING;
|
|
210
|
+
END IF;
|
|
211
|
+
END IF;
|
|
212
|
+
|
|
213
|
+
IF NOT admitted_now THEN
|
|
214
|
+
RETURN NEW;
|
|
215
|
+
END IF;
|
|
216
|
+
|
|
217
|
+
digest_value := encode(digest(concat_ws('|', NEW.role, coalesce(NEW.content, ''), coalesce(NEW.parts::text, '')), 'sha256'), 'hex');
|
|
218
|
+
version_value := lower(TG_OP) || ':' || digest_value || ':' || encode(digest(coalesce(NEW.metadata::text, ''), 'sha256'), 'hex');
|
|
219
|
+
INSERT INTO public.thread_public_events (
|
|
220
|
+
tenant_id, thread_id, source_kind, source_id, source_version,
|
|
221
|
+
event_kind, canonical_digest
|
|
222
|
+
) VALUES (
|
|
223
|
+
NEW.tenant_id, NEW.thread_id, 'message', NEW.id,
|
|
224
|
+
version_value, 'insert', digest_value
|
|
225
|
+
) ON CONFLICT DO NOTHING;
|
|
226
|
+
RETURN NEW;
|
|
227
|
+
END;
|
|
228
|
+
$$;
|
|
229
|
+
|
|
230
|
+
DROP TRIGGER IF EXISTS trg_capture_harness_message_public_event ON public.messages;
|
|
231
|
+
CREATE TRIGGER trg_capture_harness_message_public_event
|
|
232
|
+
AFTER INSERT OR UPDATE OR DELETE ON public.messages
|
|
233
|
+
FOR EACH ROW EXECUTE FUNCTION public.capture_harness_message_public_event();
|
|
234
|
+
|
|
235
|
+
CREATE OR REPLACE FUNCTION public.capture_harness_artifact_public_event()
|
|
236
|
+
RETURNS trigger
|
|
237
|
+
LANGUAGE plpgsql
|
|
238
|
+
AS $$
|
|
239
|
+
DECLARE
|
|
240
|
+
digest_value text;
|
|
241
|
+
version_value text;
|
|
242
|
+
admitted_before boolean;
|
|
243
|
+
admitted_now boolean;
|
|
244
|
+
BEGIN
|
|
245
|
+
IF TG_OP = 'DELETE' THEN
|
|
246
|
+
IF NOT EXISTS (
|
|
247
|
+
SELECT 1 FROM public.thread_public_events e
|
|
248
|
+
WHERE e.tenant_id = OLD.tenant_id
|
|
249
|
+
AND e.thread_id = OLD.thread_id
|
|
250
|
+
AND e.source_kind = 'message_artifact'
|
|
251
|
+
AND e.source_id = OLD.id
|
|
252
|
+
) THEN
|
|
253
|
+
RETURN OLD;
|
|
254
|
+
END IF;
|
|
255
|
+
digest_value := encode(digest(concat_ws('|', OLD.artifact_type, coalesce(OLD.name, ''), coalesce(OLD.artifact_id::text, ''), coalesce(OLD.metadata::text, '')), 'sha256'), 'hex');
|
|
256
|
+
INSERT INTO public.thread_public_events (
|
|
257
|
+
tenant_id, thread_id, source_kind, source_id, source_version,
|
|
258
|
+
event_kind, canonical_digest
|
|
259
|
+
) VALUES (
|
|
260
|
+
OLD.tenant_id, OLD.thread_id, 'message_artifact', OLD.id,
|
|
261
|
+
'delete:' || digest_value, 'invalidate', digest_value
|
|
262
|
+
) ON CONFLICT DO NOTHING;
|
|
263
|
+
RETURN OLD;
|
|
264
|
+
END IF;
|
|
265
|
+
|
|
266
|
+
admitted_now := EXISTS (
|
|
267
|
+
SELECT 1 FROM public.harness_managed_thread_enrollments e
|
|
268
|
+
WHERE e.tenant_id = NEW.tenant_id
|
|
269
|
+
AND e.thread_id = NEW.thread_id
|
|
270
|
+
AND e.status = 'active'
|
|
271
|
+
) AND coalesce(NEW.metadata->>'access_state', 'public') = 'public';
|
|
272
|
+
|
|
273
|
+
IF TG_OP = 'UPDATE' THEN
|
|
274
|
+
admitted_before := EXISTS (
|
|
275
|
+
SELECT 1 FROM public.thread_public_events e
|
|
276
|
+
WHERE e.tenant_id = OLD.tenant_id
|
|
277
|
+
AND e.thread_id = OLD.thread_id
|
|
278
|
+
AND e.source_kind = 'message_artifact'
|
|
279
|
+
AND e.source_id = OLD.id
|
|
280
|
+
);
|
|
281
|
+
IF admitted_before THEN
|
|
282
|
+
digest_value := encode(digest(concat_ws('|', OLD.artifact_type, coalesce(OLD.name, ''), coalesce(OLD.artifact_id::text, ''), coalesce(OLD.metadata::text, '')), 'sha256'), 'hex');
|
|
283
|
+
version_value := 'invalidate:update:' || encode(digest(concat_ws('|', NEW.artifact_type, coalesce(NEW.name, ''), coalesce(NEW.artifact_id::text, ''), coalesce(NEW.metadata::text, '')), 'sha256'), 'hex');
|
|
284
|
+
INSERT INTO public.thread_public_events (
|
|
285
|
+
tenant_id, thread_id, source_kind, source_id, source_version,
|
|
286
|
+
event_kind, canonical_digest
|
|
287
|
+
) VALUES (
|
|
288
|
+
OLD.tenant_id, OLD.thread_id, 'message_artifact', OLD.id,
|
|
289
|
+
version_value, 'invalidate', digest_value
|
|
290
|
+
) ON CONFLICT DO NOTHING;
|
|
291
|
+
END IF;
|
|
292
|
+
END IF;
|
|
293
|
+
|
|
294
|
+
IF NOT admitted_now THEN
|
|
295
|
+
RETURN NEW;
|
|
296
|
+
END IF;
|
|
297
|
+
|
|
298
|
+
digest_value := encode(digest(concat_ws('|', NEW.artifact_type, coalesce(NEW.name, ''), coalesce(NEW.artifact_id::text, ''), coalesce(NEW.metadata::text, '')), 'sha256'), 'hex');
|
|
299
|
+
INSERT INTO public.thread_public_events (
|
|
300
|
+
tenant_id, thread_id, source_kind, source_id, source_version,
|
|
301
|
+
event_kind, canonical_digest
|
|
302
|
+
) VALUES (
|
|
303
|
+
NEW.tenant_id, NEW.thread_id, 'message_artifact', NEW.id,
|
|
304
|
+
lower(TG_OP) || ':' || digest_value, 'insert', digest_value
|
|
305
|
+
) ON CONFLICT DO NOTHING;
|
|
306
|
+
RETURN NEW;
|
|
307
|
+
END;
|
|
308
|
+
$$;
|
|
309
|
+
|
|
310
|
+
DROP TRIGGER IF EXISTS trg_capture_harness_artifact_public_event ON public.message_artifacts;
|
|
311
|
+
CREATE TRIGGER trg_capture_harness_artifact_public_event
|
|
312
|
+
AFTER INSERT OR UPDATE OR DELETE ON public.message_artifacts
|
|
313
|
+
FOR EACH ROW EXECUTE FUNCTION public.capture_harness_artifact_public_event();
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
-- Provider-neutral authentication control plane (plan 2026-07-18-001 U11).
|
|
2
|
+
-- creates-column: public.auth_provider_resources.connection_key
|
|
3
|
+
-- creates-column: public.auth_provider_resources.provider_kind
|
|
4
|
+
-- creates-column: public.auth_provider_resources.lifecycle_state
|
|
5
|
+
-- creates-column: public.auth_provider_resources.resource_arn
|
|
6
|
+
-- creates-column: public.auth_provider_resources.aws_account_id
|
|
7
|
+
-- creates-column: public.auth_provider_resources.aws_region
|
|
8
|
+
-- creates-column: public.auth_provider_resources.desired_revision
|
|
9
|
+
-- creates-column: public.tenant_auth_provider_references.desired_revision
|
|
10
|
+
-- creates: public.uq_auth_provider_resources_connection_key
|
|
11
|
+
-- creates: public.uq_tenant_auth_provider_references_tenant_resource
|
|
12
|
+
-- creates: public.tenant_auth_policies
|
|
13
|
+
-- creates: public.uq_tenant_auth_policies_tenant
|
|
14
|
+
-- creates-function: public.ensure_default_tenant_auth_policy
|
|
15
|
+
-- creates-trigger: public.tenants.trg_tenants_default_auth_policy
|
|
16
|
+
-- creates: public.tenant_auth_hosts
|
|
17
|
+
-- creates: public.uq_tenant_auth_hosts_hostname
|
|
18
|
+
-- creates: public.idx_tenant_auth_hosts_tenant_status
|
|
19
|
+
-- creates: public.auth_route_clients
|
|
20
|
+
-- creates: public.uq_auth_route_clients_route_family
|
|
21
|
+
-- creates: public.uq_auth_route_clients_app_client
|
|
22
|
+
-- creates: public.idx_auth_route_clients_lifecycle_status
|
|
23
|
+
-- creates: public.user_auth_identities
|
|
24
|
+
-- creates: public.uq_user_auth_identities_cognito_sub
|
|
25
|
+
-- creates: public.uq_user_auth_identities_provider_subject
|
|
26
|
+
-- creates: public.idx_user_auth_identities_user_status
|
|
27
|
+
-- creates: public.idx_user_auth_identities_tenant_status
|
|
28
|
+
-- creates: public.auth_identity_enrollments
|
|
29
|
+
-- creates: public.uq_auth_identity_enrollments_nonce
|
|
30
|
+
-- creates: public.uq_auth_identity_enrollments_challenge
|
|
31
|
+
-- creates: public.idx_auth_identity_enrollments_pending
|
|
32
|
+
-- creates: public.auth_reconciliation_sets
|
|
33
|
+
-- creates: public.uq_auth_reconciliation_sets_stage_revision
|
|
34
|
+
-- creates: public.uq_auth_reconciliation_sets_idempotency
|
|
35
|
+
-- creates: public.auth_cutover_runs
|
|
36
|
+
-- creates: public.uq_auth_cutover_runs_stage_inventory
|
|
37
|
+
-- creates: public.idx_auth_cutover_runs_status
|
|
38
|
+
-- creates: public.auth_identity_proofs
|
|
39
|
+
-- creates: public.uq_auth_identity_proofs_digest
|
|
40
|
+
-- creates: public.idx_auth_identity_proofs_identity
|
|
41
|
+
-- drops: public.uq_tenant_auth_provider_references_install_resource
|
|
42
|
+
|
|
43
|
+
ALTER TABLE public.auth_provider_resources
|
|
44
|
+
ADD COLUMN IF NOT EXISTS connection_key text,
|
|
45
|
+
ADD COLUMN IF NOT EXISTS provider_kind text,
|
|
46
|
+
ADD COLUMN IF NOT EXISTS lifecycle_state text,
|
|
47
|
+
ADD COLUMN IF NOT EXISTS resource_arn text,
|
|
48
|
+
ADD COLUMN IF NOT EXISTS aws_account_id text,
|
|
49
|
+
ADD COLUMN IF NOT EXISTS aws_region text,
|
|
50
|
+
ADD COLUMN IF NOT EXISTS desired_revision integer;
|
|
51
|
+
|
|
52
|
+
UPDATE public.auth_provider_resources
|
|
53
|
+
SET connection_key = COALESCE(connection_key, provider_key || ':' || id::text),
|
|
54
|
+
provider_kind = COALESCE(provider_kind, CASE WHEN provider_key = 'workos' THEN 'legacy_workos' ELSE provider_key END),
|
|
55
|
+
lifecycle_state = COALESCE(lifecycle_state, 'coexistence'),
|
|
56
|
+
desired_revision = COALESCE(desired_revision, 1);
|
|
57
|
+
|
|
58
|
+
ALTER TABLE public.auth_provider_resources
|
|
59
|
+
ALTER COLUMN connection_key SET DEFAULT 'legacy',
|
|
60
|
+
ALTER COLUMN connection_key SET NOT NULL,
|
|
61
|
+
ALTER COLUMN provider_kind SET DEFAULT 'legacy_workos',
|
|
62
|
+
ALTER COLUMN provider_kind SET NOT NULL,
|
|
63
|
+
ALTER COLUMN lifecycle_state SET DEFAULT 'coexistence',
|
|
64
|
+
ALTER COLUMN lifecycle_state SET NOT NULL,
|
|
65
|
+
ALTER COLUMN desired_revision SET DEFAULT 1,
|
|
66
|
+
ALTER COLUMN desired_revision SET NOT NULL,
|
|
67
|
+
ALTER COLUMN issuer_url DROP NOT NULL,
|
|
68
|
+
ALTER COLUMN client_id DROP NOT NULL,
|
|
69
|
+
ALTER COLUMN client_secret_ref DROP NOT NULL;
|
|
70
|
+
|
|
71
|
+
ALTER TABLE public.auth_provider_resources
|
|
72
|
+
DROP CONSTRAINT IF EXISTS auth_provider_resources_no_public_without_valid,
|
|
73
|
+
DROP CONSTRAINT IF EXISTS auth_provider_resources_lifecycle_state_allowed,
|
|
74
|
+
ADD CONSTRAINT auth_provider_resources_lifecycle_state_allowed
|
|
75
|
+
CHECK (lifecycle_state IN ('coexistence', 'native', 'denied')),
|
|
76
|
+
ADD CONSTRAINT auth_provider_resources_no_public_without_valid
|
|
77
|
+
CHECK (
|
|
78
|
+
public_options_published = false OR
|
|
79
|
+
(validation_status IN ('valid', 'partially_valid') AND lifecycle_state <> 'denied')
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_provider_resources_connection_key
|
|
83
|
+
ON public.auth_provider_resources (cognito_user_pool_id, connection_key);
|
|
84
|
+
|
|
85
|
+
ALTER TABLE public.tenant_auth_provider_references
|
|
86
|
+
ADD COLUMN IF NOT EXISTS desired_revision integer;
|
|
87
|
+
|
|
88
|
+
UPDATE public.tenant_auth_provider_references
|
|
89
|
+
SET desired_revision = COALESCE(desired_revision, 1);
|
|
90
|
+
|
|
91
|
+
ALTER TABLE public.tenant_auth_provider_references
|
|
92
|
+
ALTER COLUMN desired_revision SET DEFAULT 1,
|
|
93
|
+
ALTER COLUMN desired_revision SET NOT NULL,
|
|
94
|
+
ALTER COLUMN plugin_install_id DROP NOT NULL,
|
|
95
|
+
DROP CONSTRAINT IF EXISTS tenant_auth_provider_references_tenant_id_fkey,
|
|
96
|
+
DROP CONSTRAINT IF EXISTS tenant_auth_provider_references_plugin_install_id_fkey,
|
|
97
|
+
DROP CONSTRAINT IF EXISTS tenant_auth_provider_references_auth_provider_resource_id_fkey,
|
|
98
|
+
ADD CONSTRAINT tenant_auth_provider_references_tenant_id_fkey
|
|
99
|
+
FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE RESTRICT,
|
|
100
|
+
ADD CONSTRAINT tenant_auth_provider_references_plugin_install_id_fkey
|
|
101
|
+
FOREIGN KEY (plugin_install_id) REFERENCES public.plugin_installs(id) ON DELETE SET NULL,
|
|
102
|
+
ADD CONSTRAINT tenant_auth_provider_references_auth_provider_resource_id_fkey
|
|
103
|
+
FOREIGN KEY (auth_provider_resource_id) REFERENCES public.auth_provider_resources(id) ON DELETE RESTRICT;
|
|
104
|
+
|
|
105
|
+
DROP INDEX IF EXISTS public.uq_tenant_auth_provider_references_install_resource;
|
|
106
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_tenant_auth_provider_references_tenant_resource
|
|
107
|
+
ON public.tenant_auth_provider_references (tenant_id, auth_provider_resource_id);
|
|
108
|
+
|
|
109
|
+
CREATE TABLE IF NOT EXISTS public.tenant_auth_policies (
|
|
110
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
111
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE RESTRICT,
|
|
112
|
+
local_password_enabled boolean NOT NULL DEFAULT true,
|
|
113
|
+
status text NOT NULL DEFAULT 'draft',
|
|
114
|
+
revision integer NOT NULL DEFAULT 1,
|
|
115
|
+
catalog_revision text,
|
|
116
|
+
updated_by uuid REFERENCES public.users(id) ON DELETE SET NULL,
|
|
117
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
118
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
119
|
+
CONSTRAINT tenant_auth_policies_status_allowed
|
|
120
|
+
CHECK (status IN ('draft', 'active', 'disabled'))
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_tenant_auth_policies_tenant
|
|
124
|
+
ON public.tenant_auth_policies (tenant_id);
|
|
125
|
+
|
|
126
|
+
-- Preserve the pre-native-auth behavior for existing tenants explicitly:
|
|
127
|
+
-- local password is enabled and validated global Google/Microsoft routes are
|
|
128
|
+
-- permitted by an active policy. Tenant-specific Entra references can then
|
|
129
|
+
-- replace general Microsoft without leaving older tenants policy-less.
|
|
130
|
+
INSERT INTO public.tenant_auth_policies (
|
|
131
|
+
tenant_id,
|
|
132
|
+
local_password_enabled,
|
|
133
|
+
status,
|
|
134
|
+
revision,
|
|
135
|
+
created_at,
|
|
136
|
+
updated_at
|
|
137
|
+
)
|
|
138
|
+
SELECT id, true, 'active', 1, now(), now()
|
|
139
|
+
FROM public.tenants
|
|
140
|
+
ON CONFLICT (tenant_id) DO NOTHING;
|
|
141
|
+
|
|
142
|
+
CREATE OR REPLACE FUNCTION public.ensure_default_tenant_auth_policy()
|
|
143
|
+
RETURNS trigger
|
|
144
|
+
LANGUAGE plpgsql
|
|
145
|
+
AS $$
|
|
146
|
+
BEGIN
|
|
147
|
+
INSERT INTO public.tenant_auth_policies (
|
|
148
|
+
tenant_id,
|
|
149
|
+
local_password_enabled,
|
|
150
|
+
status,
|
|
151
|
+
revision,
|
|
152
|
+
created_at,
|
|
153
|
+
updated_at
|
|
154
|
+
) VALUES (NEW.id, true, 'active', 1, now(), now())
|
|
155
|
+
ON CONFLICT (tenant_id) DO NOTHING;
|
|
156
|
+
RETURN NEW;
|
|
157
|
+
END;
|
|
158
|
+
$$;
|
|
159
|
+
|
|
160
|
+
DROP TRIGGER IF EXISTS trg_tenants_default_auth_policy ON public.tenants;
|
|
161
|
+
CREATE TRIGGER trg_tenants_default_auth_policy
|
|
162
|
+
AFTER INSERT ON public.tenants
|
|
163
|
+
FOR EACH ROW
|
|
164
|
+
EXECUTE FUNCTION public.ensure_default_tenant_auth_policy();
|
|
165
|
+
|
|
166
|
+
CREATE TABLE IF NOT EXISTS public.tenant_auth_hosts (
|
|
167
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
168
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE RESTRICT,
|
|
169
|
+
hostname text NOT NULL,
|
|
170
|
+
status text NOT NULL DEFAULT 'pending',
|
|
171
|
+
verified_at timestamptz,
|
|
172
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
173
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
174
|
+
CONSTRAINT tenant_auth_hosts_status_allowed
|
|
175
|
+
CHECK (status IN ('pending', 'verified', 'disabled'))
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_tenant_auth_hosts_hostname
|
|
179
|
+
ON public.tenant_auth_hosts (hostname);
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_tenant_auth_hosts_tenant_status
|
|
181
|
+
ON public.tenant_auth_hosts (tenant_id, status);
|
|
182
|
+
|
|
183
|
+
CREATE TABLE IF NOT EXISTS public.auth_route_clients (
|
|
184
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
185
|
+
route_key text NOT NULL,
|
|
186
|
+
client_family text NOT NULL,
|
|
187
|
+
cognito_user_pool_id text NOT NULL,
|
|
188
|
+
cognito_app_client_id text NOT NULL,
|
|
189
|
+
provider_names jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
190
|
+
explicit_auth_flows jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
191
|
+
redirect_uris jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
192
|
+
logout_uris jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
193
|
+
lifecycle_state text NOT NULL DEFAULT 'native',
|
|
194
|
+
validation_status text NOT NULL DEFAULT 'unconfigured',
|
|
195
|
+
desired_revision integer NOT NULL DEFAULT 1,
|
|
196
|
+
resource_arn text,
|
|
197
|
+
diagnostics jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
198
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
199
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
200
|
+
CONSTRAINT auth_route_clients_lifecycle_allowed
|
|
201
|
+
CHECK (lifecycle_state IN ('coexistence', 'native', 'denied')),
|
|
202
|
+
CONSTRAINT auth_route_clients_validation_allowed
|
|
203
|
+
CHECK (validation_status IN ('unconfigured', 'validating', 'valid', 'partially_valid', 'invalid', 'rotating_secret', 'disabled'))
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_route_clients_route_family
|
|
207
|
+
ON public.auth_route_clients (route_key, client_family);
|
|
208
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_route_clients_app_client
|
|
209
|
+
ON public.auth_route_clients (cognito_app_client_id);
|
|
210
|
+
CREATE INDEX IF NOT EXISTS idx_auth_route_clients_lifecycle_status
|
|
211
|
+
ON public.auth_route_clients (lifecycle_state, validation_status);
|
|
212
|
+
|
|
213
|
+
CREATE TABLE IF NOT EXISTS public.user_auth_identities (
|
|
214
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
215
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE RESTRICT,
|
|
216
|
+
user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE RESTRICT,
|
|
217
|
+
auth_provider_resource_id uuid REFERENCES public.auth_provider_resources(id) ON DELETE RESTRICT,
|
|
218
|
+
cognito_issuer text NOT NULL,
|
|
219
|
+
cognito_sub text NOT NULL,
|
|
220
|
+
provider_issuer text NOT NULL,
|
|
221
|
+
provider_subject text NOT NULL,
|
|
222
|
+
status text NOT NULL DEFAULT 'pending_proof',
|
|
223
|
+
proof_kind text NOT NULL,
|
|
224
|
+
evidence jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
225
|
+
activated_at timestamptz,
|
|
226
|
+
quarantined_at timestamptz,
|
|
227
|
+
revoked_at timestamptz,
|
|
228
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
229
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
230
|
+
CONSTRAINT user_auth_identities_status_allowed
|
|
231
|
+
CHECK (status IN ('pending_proof', 'active', 'quarantined', 'revoked')),
|
|
232
|
+
CONSTRAINT user_auth_identities_active_has_resource
|
|
233
|
+
CHECK (status = 'quarantined' OR auth_provider_resource_id IS NOT NULL)
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_user_auth_identities_cognito_sub
|
|
237
|
+
ON public.user_auth_identities (cognito_issuer, cognito_sub);
|
|
238
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_user_auth_identities_provider_subject
|
|
239
|
+
ON public.user_auth_identities (auth_provider_resource_id, provider_issuer, provider_subject);
|
|
240
|
+
CREATE INDEX IF NOT EXISTS idx_user_auth_identities_user_status
|
|
241
|
+
ON public.user_auth_identities (user_id, status);
|
|
242
|
+
CREATE INDEX IF NOT EXISTS idx_user_auth_identities_tenant_status
|
|
243
|
+
ON public.user_auth_identities (tenant_id, status);
|
|
244
|
+
|
|
245
|
+
CREATE TABLE IF NOT EXISTS public.auth_identity_enrollments (
|
|
246
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
247
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE RESTRICT,
|
|
248
|
+
intended_user_id uuid REFERENCES public.users(id) ON DELETE RESTRICT,
|
|
249
|
+
recipient_grant_kind text NOT NULL,
|
|
250
|
+
recipient_grant_id uuid NOT NULL,
|
|
251
|
+
auth_provider_resource_id uuid NOT NULL REFERENCES public.auth_provider_resources(id) ON DELETE RESTRICT,
|
|
252
|
+
auth_route_client_id uuid NOT NULL REFERENCES public.auth_route_clients(id) ON DELETE RESTRICT,
|
|
253
|
+
redirect_uri text NOT NULL,
|
|
254
|
+
nonce_digest text NOT NULL,
|
|
255
|
+
recipient_challenge_digest text NOT NULL,
|
|
256
|
+
status text NOT NULL DEFAULT 'pending',
|
|
257
|
+
expires_at timestamptz NOT NULL,
|
|
258
|
+
consumed_at timestamptz,
|
|
259
|
+
proof jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
260
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
261
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
262
|
+
CONSTRAINT auth_identity_enrollments_status_allowed
|
|
263
|
+
CHECK (status IN ('pending', 'consumed', 'expired', 'revoked')),
|
|
264
|
+
CONSTRAINT auth_identity_enrollments_grant_kind_allowed
|
|
265
|
+
CHECK (recipient_grant_kind IN ('membership', 'pending_owner'))
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_enrollments_nonce
|
|
269
|
+
ON public.auth_identity_enrollments (nonce_digest);
|
|
270
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_enrollments_challenge
|
|
271
|
+
ON public.auth_identity_enrollments (recipient_challenge_digest);
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_auth_identity_enrollments_pending
|
|
273
|
+
ON public.auth_identity_enrollments (tenant_id, status, expires_at);
|
|
274
|
+
|
|
275
|
+
CREATE TABLE IF NOT EXISTS public.auth_reconciliation_sets (
|
|
276
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
277
|
+
stage text NOT NULL,
|
|
278
|
+
revision integer NOT NULL,
|
|
279
|
+
idempotency_key text NOT NULL,
|
|
280
|
+
manifest_fingerprint text NOT NULL,
|
|
281
|
+
desired_connections jsonb NOT NULL,
|
|
282
|
+
status text NOT NULL DEFAULT 'pending',
|
|
283
|
+
applied_at timestamptz,
|
|
284
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
285
|
+
CONSTRAINT auth_reconciliation_sets_status_allowed
|
|
286
|
+
CHECK (status IN ('pending', 'applied', 'rejected'))
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_reconciliation_sets_stage_revision
|
|
290
|
+
ON public.auth_reconciliation_sets (stage, revision);
|
|
291
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_reconciliation_sets_idempotency
|
|
292
|
+
ON public.auth_reconciliation_sets (idempotency_key);
|
|
293
|
+
|
|
294
|
+
CREATE TABLE IF NOT EXISTS public.auth_cutover_runs (
|
|
295
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
296
|
+
stage text NOT NULL,
|
|
297
|
+
tenant_id uuid,
|
|
298
|
+
inventory_fingerprint text NOT NULL,
|
|
299
|
+
status text NOT NULL DEFAULT 'inventory',
|
|
300
|
+
terminal_dispositions jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
301
|
+
client_shutdown_evidence jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
302
|
+
drain_evidence jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
303
|
+
started_at timestamptz NOT NULL DEFAULT now(),
|
|
304
|
+
completed_at timestamptz,
|
|
305
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
306
|
+
CONSTRAINT auth_cutover_runs_status_allowed
|
|
307
|
+
CHECK (status IN ('inventory', 'ready', 'cutting_over', 'soaking', 'rollback_required', 'complete', 'failed'))
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_cutover_runs_stage_inventory
|
|
311
|
+
ON public.auth_cutover_runs (stage, inventory_fingerprint);
|
|
312
|
+
CREATE INDEX IF NOT EXISTS idx_auth_cutover_runs_status
|
|
313
|
+
ON public.auth_cutover_runs (stage, status);
|
|
314
|
+
|
|
315
|
+
CREATE TABLE IF NOT EXISTS public.auth_identity_proofs (
|
|
316
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
317
|
+
user_auth_identity_id uuid NOT NULL REFERENCES public.user_auth_identities(id) ON DELETE RESTRICT,
|
|
318
|
+
proof_digest text NOT NULL,
|
|
319
|
+
proof_kind text NOT NULL,
|
|
320
|
+
evidence jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
321
|
+
observed_at timestamptz NOT NULL DEFAULT now(),
|
|
322
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_proofs_digest
|
|
326
|
+
ON public.auth_identity_proofs (proof_digest);
|
|
327
|
+
CREATE INDEX IF NOT EXISTS idx_auth_identity_proofs_identity
|
|
328
|
+
ON public.auth_identity_proofs (user_auth_identity_id);
|