create-safest-tools 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +9 -3
  2. package/package.json +1 -1
  3. package/src/cli.mjs +45 -2
  4. package/src/config.mjs +4 -3
  5. package/src/scaffold.mjs +4 -1
  6. package/src/update.mjs +265 -0
  7. package/template/README.md +5 -3
  8. package/template/console/shell/WorkspaceShell.tsx +0 -4
  9. package/template/migrations/0001_safest_resolve_baseline.sql +2446 -0
  10. package/template/package.json +4 -3
  11. package/template/public/console/auth-shell.js +3 -9
  12. package/template/public/styles.css +2 -15
  13. package/template/scripts/reports-owner-setup.mjs +22 -7
  14. package/template/scripts/reports-plan.mjs +1 -1
  15. package/template/migrations/0001_reports_foundation.sql +0 -444
  16. package/template/migrations/0002_human_report_loop.sql +0 -65
  17. package/template/migrations/0003_delivery_reliability.sql +0 -18
  18. package/template/migrations/0004_public_intake.sql +0 -14
  19. package/template/migrations/0005_operations_visibility.sql +0 -24
  20. package/template/migrations/0006_ai_governance.sql +0 -198
  21. package/template/migrations/0007_ai_release_gates.sql +0 -6
  22. package/template/migrations/0008_retention_analytics_exports.sql +0 -52
  23. package/template/migrations/0009_retention_derived_copies.sql +0 -14
  24. package/template/migrations/0010_ai_quality_controls.sql +0 -26
  25. package/template/migrations/0011_analyst_presence.sql +0 -28
  26. package/template/migrations/0012_queue_policies.sql +0 -87
  27. package/template/migrations/0013_routing_agents.sql +0 -63
  28. package/template/migrations/0014_webhook_enrichments.sql +0 -122
  29. package/template/migrations/0015_queue_owned_ai.sql +0 -55
  30. package/template/migrations/0016_operator_accounts.sql +0 -67
  31. package/template/migrations/0017_operator_profiles_and_recovery.sql +0 -33
  32. package/template/migrations/0018_platform_configuration.sql +0 -382
  33. package/template/migrations/0019_workflow_authoring_runtime.sql +0 -372
  34. package/template/migrations/0020_tasks_findings_assistant_budgets.sql +0 -427
  35. package/template/migrations/0021_abuse_evidence_operations.sql +0 -324
  36. package/template/migrations/0022_workflow_dispatch_operations.sql +0 -42
  37. package/template/migrations/0023_component_connection_execution.sql +0 -75
  38. package/template/migrations/0024_access_runtime_integrity.sql +0 -72
  39. package/template/migrations/0025_installation_timezone.sql +0 -11
  40. package/template/migrations/0026_ai_and_egress_execution_controls.sql +0 -49
  41. package/template/migrations/0027_prompt_and_ai_registry.sql +0 -37
  42. package/template/migrations/0028_step_attempt_ai_provenance.sql +0 -13
  43. package/template/migrations/0029_evidence_fetch_transport.sql +0 -5
  44. package/template/migrations/0030_evidence_dlq_incidents.sql +0 -45
  45. package/template/migrations/0031_shadow_quality_integrity.sql +0 -7
  46. package/template/migrations/0032_action_delivery_outbox.sql +0 -55
  47. package/template/migrations/0033_configuration_and_assistant_drafts.sql +0 -43
  48. package/template/migrations/0034_installation_integrations.sql +0 -31
  49. package/template/migrations/0035_workspace_governance.sql +0 -21
  50. package/template/migrations/0036_published_routing_baseline.sql +0 -15
  51. package/template/migrations/0037_builtin_phishing_specialist.sql +0 -71
  52. package/template/migrations/0038_remove_deprecated_enrichment_runtime.sql +0 -228
  53. package/template/migrations/0039_secure_reporting_channels.sql +0 -45
  54. package/template/migrations/0040_notification_only_reporting.sql +0 -24
  55. package/template/migrations/0041_better_auth_credentials.sql +0 -17
  56. package/template/migrations/0042_owner_setup_tokens.sql +0 -29
@@ -1,122 +0,0 @@
1
- PRAGMA foreign_keys = ON;
2
-
3
- ALTER TABLE dead_letter_items RENAME TO dead_letter_items_v2;
4
-
5
- CREATE TABLE dead_letter_items (
6
- id TEXT PRIMARY KEY,
7
- fingerprint TEXT NOT NULL UNIQUE,
8
- source_queue TEXT NOT NULL,
9
- source_message_id TEXT NOT NULL,
10
- job_type TEXT NOT NULL CHECK (job_type IN ('route_report', 'process_ai_report', 'run_enrichment', 'deliver_action', 'deliver_notification', 'invalid')),
11
- job_id TEXT,
12
- report_id TEXT REFERENCES reports(id),
13
- outbox_id TEXT,
14
- state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open', 'acknowledged', 'resolved')),
15
- error_code TEXT NOT NULL,
16
- occurrence_count INTEGER NOT NULL DEFAULT 1 CHECK (occurrence_count > 0),
17
- details_json TEXT NOT NULL DEFAULT '{}',
18
- first_seen_at TEXT NOT NULL,
19
- last_seen_at TEXT NOT NULL,
20
- resolved_at TEXT
21
- );
22
-
23
- INSERT INTO dead_letter_items (
24
- id, fingerprint, source_queue, source_message_id, job_type, job_id, report_id, outbox_id,
25
- state, error_code, occurrence_count, details_json, first_seen_at, last_seen_at, resolved_at
26
- )
27
- SELECT id, fingerprint, source_queue, source_message_id, job_type, job_id, report_id, outbox_id,
28
- state, error_code, occurrence_count, details_json, first_seen_at, last_seen_at, resolved_at
29
- FROM dead_letter_items_v2;
30
-
31
- DROP TABLE dead_letter_items_v2;
32
-
33
- CREATE INDEX dead_letter_items_state_idx
34
- ON dead_letter_items(state, last_seen_at DESC, id DESC);
35
- CREATE INDEX dead_letter_items_report_idx
36
- ON dead_letter_items(report_id, last_seen_at DESC, id DESC);
37
-
38
- CREATE TABLE enrichment_definitions (
39
- id TEXT PRIMARY KEY,
40
- installation_id TEXT NOT NULL REFERENCES installations(id),
41
- enrichment_key TEXT NOT NULL,
42
- name TEXT NOT NULL,
43
- description TEXT NOT NULL DEFAULT '',
44
- status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'paused', 'archived')),
45
- active_version_id TEXT,
46
- created_by TEXT NOT NULL,
47
- created_at TEXT NOT NULL,
48
- updated_at TEXT NOT NULL,
49
- UNIQUE (installation_id, enrichment_key)
50
- );
51
-
52
- CREATE TABLE enrichment_definition_versions (
53
- id TEXT PRIMARY KEY,
54
- definition_id TEXT NOT NULL REFERENCES enrichment_definitions(id) ON DELETE CASCADE,
55
- version INTEGER NOT NULL CHECK (version > 0),
56
- webhook_url TEXT NOT NULL,
57
- secret_binding TEXT NOT NULL,
58
- timeout_ms INTEGER NOT NULL CHECK (timeout_ms BETWEEN 250 AND 30000),
59
- max_attempts INTEGER NOT NULL CHECK (max_attempts BETWEEN 1 AND 10),
60
- ttl_seconds INTEGER NOT NULL CHECK (ttl_seconds BETWEEN 60 AND 2592000),
61
- input_fields_json TEXT NOT NULL,
62
- output_schema_json TEXT NOT NULL,
63
- published_at TEXT NOT NULL,
64
- created_by TEXT NOT NULL,
65
- created_at TEXT NOT NULL,
66
- UNIQUE (definition_id, version)
67
- );
68
-
69
- CREATE INDEX enrichment_definitions_admin_idx
70
- ON enrichment_definitions(status, name, id);
71
- CREATE INDEX enrichment_definition_versions_idx
72
- ON enrichment_definition_versions(definition_id, version DESC, id);
73
-
74
- CREATE TABLE enrichment_requests (
75
- id TEXT PRIMARY KEY,
76
- report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
77
- queue_version_id TEXT NOT NULL REFERENCES queue_versions(id),
78
- definition_version_id TEXT NOT NULL REFERENCES enrichment_definition_versions(id),
79
- enrichment_key TEXT NOT NULL,
80
- status TEXT NOT NULL CHECK (status IN ('pending', 'processing', 'retrying', 'complete', 'failed')),
81
- attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
82
- next_attempt_at TEXT NOT NULL,
83
- last_error_code TEXT,
84
- created_at TEXT NOT NULL,
85
- updated_at TEXT NOT NULL,
86
- completed_at TEXT,
87
- UNIQUE (report_id, queue_version_id, enrichment_key)
88
- );
89
-
90
- CREATE INDEX enrichment_requests_due_idx
91
- ON enrichment_requests(status, next_attempt_at, id);
92
- CREATE INDEX enrichment_requests_report_idx
93
- ON enrichment_requests(report_id, status, id);
94
-
95
- CREATE TABLE enrichment_attempts (
96
- id TEXT PRIMARY KEY,
97
- enrichment_request_id TEXT NOT NULL REFERENCES enrichment_requests(id) ON DELETE CASCADE,
98
- attempt_number INTEGER NOT NULL CHECK (attempt_number > 0),
99
- request_nonce TEXT NOT NULL,
100
- response_status INTEGER,
101
- response_class TEXT NOT NULL,
102
- error_code TEXT,
103
- started_at TEXT NOT NULL,
104
- finished_at TEXT NOT NULL,
105
- UNIQUE (enrichment_request_id, attempt_number)
106
- );
107
-
108
- CREATE TABLE enrichment_results (
109
- id TEXT PRIMARY KEY,
110
- enrichment_request_id TEXT NOT NULL UNIQUE REFERENCES enrichment_requests(id) ON DELETE CASCADE,
111
- report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
112
- enrichment_key TEXT NOT NULL,
113
- subject_reference TEXT NOT NULL,
114
- data_json TEXT NOT NULL,
115
- response_sha256 TEXT NOT NULL,
116
- definition_version_id TEXT NOT NULL REFERENCES enrichment_definition_versions(id),
117
- fetched_at TEXT NOT NULL,
118
- expires_at TEXT NOT NULL
119
- );
120
-
121
- CREATE INDEX enrichment_results_report_idx
122
- ON enrichment_results(report_id, enrichment_key, expires_at DESC, id);
@@ -1,55 +0,0 @@
1
- PRAGMA foreign_keys = ON;
2
-
3
- ALTER TABLE ai_queue_config_versions
4
- ADD COLUMN queue_version_id TEXT REFERENCES queue_versions(id);
5
-
6
- CREATE UNIQUE INDEX ai_queue_config_queue_version_idx
7
- ON ai_queue_config_versions(queue_version_id)
8
- WHERE queue_version_id IS NOT NULL;
9
-
10
- UPDATE ai_queue_config_versions
11
- SET queue_version_id = (
12
- SELECT q.active_version_id FROM queue_definitions q
13
- WHERE q.id = ai_queue_config_versions.queue_id
14
- )
15
- WHERE id IN (
16
- SELECT c.id FROM ai_queue_config_versions c
17
- WHERE c.id = (
18
- SELECT latest.id FROM ai_queue_config_versions latest
19
- WHERE latest.queue_id = c.queue_id
20
- ORDER BY latest.version DESC, latest.created_at DESC, latest.id DESC LIMIT 1
21
- )
22
- );
23
-
24
- INSERT INTO ai_queue_config_versions (
25
- id, queue_id, queue_version_id, version, mode, enabled, provider, model,
26
- input_cost_microusd_per_million, output_cost_microusd_per_million,
27
- prompt_version, policy_version_id, eligible_form_versions_json, eligible_sources_json,
28
- eligible_reason_codes_json, eligible_target_types_json, allowed_fields_json, allowed_outcomes_json,
29
- allowed_action_codes_json, allowed_template_ids_json, confidence_threshold, max_follow_up_turns,
30
- max_wait_hours, daily_report_budget, daily_cost_budget_microusd, quality_sample_rate,
31
- minimum_shadow_runs, minimum_quality_reviews, maximum_failure_rate, maximum_disagreement_rate,
32
- fallback_queue_id, pause_state, published_at, created_by, created_at, idempotency_key
33
- )
34
- SELECT
35
- qv.id || '-ai-runtime', qv.queue_id, qv.id, qv.version, qv.mode, 1, 'workers_ai',
36
- '@cf/meta/llama-3.1-8b-instruct-fast', 0, 0, 'queue-policy-' || qv.id, 'general-policy-v1',
37
- '["general-v1"]', '["signed_widget","public_page","server_api"]',
38
- '["harassment","spam","other"]', '["message","profile","listing","account","file","transaction","service"]',
39
- '["target.type","answers.details","evidence.references","enrichments"]',
40
- CASE
41
- WHEN json_array_length(COALESCE(json_extract(qv.ai_bounds_json, '$.allowedOutcomes'), json_extract(qv.ai_bounds_json, '$.allowed_outcomes'), '[]')) > 0
42
- THEN COALESCE(json_extract(qv.ai_bounds_json, '$.allowedOutcomes'), json_extract(qv.ai_bounds_json, '$.allowed_outcomes'))
43
- ELSE '["no_violation_found","out_of_scope","insufficient_information","escalate_to_human"]'
44
- END,
45
- '[]', '["ai-more-context-v1","ai-no-violation-v1","ai-out-of-scope-v1"]',
46
- COALESCE(json_extract(qv.ai_bounds_json, '$.confidenceThreshold'), json_extract(qv.ai_bounds_json, '$.confidence_threshold'), 0.85),
47
- COALESCE(json_extract(qv.ai_bounds_json, '$.maxFollowUpTurns'), json_extract(qv.ai_bounds_json, '$.max_follow_up_turns'), 0),
48
- 168, 100, 1000000, 0.1, 25, 10, 0.1, 0.1, qv.fallback_queue_id,
49
- 'ready', qv.published_at, qv.created_by, qv.created_at, 'queue-policy-runtime:' || qv.id
50
- FROM queue_versions qv
51
- WHERE qv.management_mode IN ('ai', 'hybrid') AND qv.mode != 'off'
52
- AND qv.fallback_queue_id IS NOT NULL
53
- AND NOT EXISTS (
54
- SELECT 1 FROM ai_queue_config_versions c WHERE c.queue_version_id = qv.id
55
- );
@@ -1,67 +0,0 @@
1
- PRAGMA foreign_keys = ON;
2
-
3
- CREATE TABLE operator_users (
4
- id TEXT PRIMARY KEY,
5
- email TEXT NOT NULL COLLATE NOCASE UNIQUE,
6
- display_name TEXT NOT NULL,
7
- avatar_url TEXT,
8
- roles_json TEXT NOT NULL,
9
- status TEXT NOT NULL DEFAULT 'active'
10
- CHECK (status IN ('active', 'suspended')),
11
- password_hash TEXT,
12
- password_salt TEXT,
13
- password_iterations INTEGER,
14
- invited_by TEXT,
15
- accepted_at TEXT,
16
- last_login_at TEXT,
17
- created_at TEXT NOT NULL,
18
- updated_at TEXT NOT NULL
19
- );
20
-
21
- CREATE INDEX operator_users_status_idx
22
- ON operator_users(status, email, id);
23
-
24
- CREATE TABLE operator_invitations (
25
- id TEXT PRIMARY KEY,
26
- email TEXT NOT NULL COLLATE NOCASE,
27
- role TEXT NOT NULL
28
- CHECK (role IN ('administrator', 'reviewer', 'appeal_reviewer', 'auditor')),
29
- token_hash TEXT NOT NULL UNIQUE,
30
- status TEXT NOT NULL DEFAULT 'pending'
31
- CHECK (status IN ('pending', 'accepted', 'revoked')),
32
- invited_by TEXT NOT NULL,
33
- accepted_by TEXT REFERENCES operator_users(id),
34
- expires_at TEXT NOT NULL,
35
- accepted_at TEXT,
36
- revoked_at TEXT,
37
- created_at TEXT NOT NULL
38
- );
39
-
40
- CREATE UNIQUE INDEX operator_invitations_pending_email_idx
41
- ON operator_invitations(email)
42
- WHERE status = 'pending';
43
- CREATE INDEX operator_invitations_status_idx
44
- ON operator_invitations(status, expires_at, created_at DESC, id);
45
-
46
- CREATE TABLE operator_sessions (
47
- id TEXT PRIMARY KEY,
48
- user_id TEXT NOT NULL REFERENCES operator_users(id) ON DELETE CASCADE,
49
- token_hash TEXT NOT NULL UNIQUE,
50
- csrf_hash TEXT NOT NULL,
51
- expires_at TEXT NOT NULL,
52
- last_seen_at TEXT NOT NULL,
53
- revoked_at TEXT,
54
- created_at TEXT NOT NULL
55
- );
56
-
57
- CREATE INDEX operator_sessions_user_idx
58
- ON operator_sessions(user_id, revoked_at, expires_at DESC, id);
59
- CREATE INDEX operator_sessions_expiry_idx
60
- ON operator_sessions(revoked_at, expires_at, id);
61
-
62
- CREATE TABLE operator_login_limits (
63
- key_hash TEXT PRIMARY KEY,
64
- attempt_count INTEGER NOT NULL CHECK (attempt_count >= 0),
65
- window_started_at TEXT NOT NULL,
66
- updated_at TEXT NOT NULL
67
- );
@@ -1,33 +0,0 @@
1
- PRAGMA foreign_keys = ON;
2
-
3
- ALTER TABLE operator_users ADD COLUMN avatar_object_key TEXT;
4
- ALTER TABLE operator_users ADD COLUMN avatar_uploaded_at TEXT;
5
-
6
- CREATE TABLE operator_password_resets (
7
- id TEXT PRIMARY KEY,
8
- user_id TEXT NOT NULL REFERENCES operator_users(id) ON DELETE CASCADE,
9
- token_hash TEXT NOT NULL UNIQUE,
10
- status TEXT NOT NULL DEFAULT 'pending'
11
- CHECK (status IN ('pending', 'used', 'revoked')),
12
- delivery_status TEXT NOT NULL DEFAULT 'pending'
13
- CHECK (delivery_status IN ('pending', 'delivered', 'failed', 'unavailable', 'manual')),
14
- requested_by TEXT NOT NULL,
15
- expires_at TEXT NOT NULL,
16
- delivered_at TEXT,
17
- used_at TEXT,
18
- revoked_at TEXT,
19
- created_at TEXT NOT NULL,
20
- updated_at TEXT NOT NULL
21
- );
22
-
23
- CREATE INDEX operator_password_resets_user_idx
24
- ON operator_password_resets(user_id, status, expires_at DESC, id);
25
- CREATE INDEX operator_password_resets_expiry_idx
26
- ON operator_password_resets(status, expires_at, id);
27
-
28
- CREATE TABLE operator_password_reset_limits (
29
- key_hash TEXT PRIMARY KEY,
30
- attempt_count INTEGER NOT NULL CHECK (attempt_count >= 0),
31
- window_started_at TEXT NOT NULL,
32
- updated_at TEXT NOT NULL
33
- );
@@ -1,382 +0,0 @@
1
- PRAGMA foreign_keys = ON;
2
-
3
- CREATE TABLE installation_features (
4
- installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
5
- feature_key TEXT NOT NULL,
6
- state TEXT NOT NULL CHECK (state IN ('unavailable', 'disabled', 'setup_required', 'ready', 'degraded', 'paused')),
7
- capability_json TEXT NOT NULL DEFAULT '{}',
8
- checked_at TEXT NOT NULL,
9
- error_code TEXT,
10
- revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
11
- PRIMARY KEY (installation_id, feature_key)
12
- );
13
-
14
- CREATE INDEX installation_features_state_idx
15
- ON installation_features(installation_id, state, feature_key);
16
-
17
- CREATE TABLE service_identities (
18
- id TEXT PRIMARY KEY,
19
- installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
20
- identity_key TEXT NOT NULL,
21
- display_name TEXT NOT NULL,
22
- identity_kind TEXT NOT NULL CHECK (identity_kind IN (
23
- 'integration', 'workflow_runtime', 'assistant', 'specialist_agent', 'delivery', 'maintenance'
24
- )),
25
- status TEXT NOT NULL CHECK (status IN ('active', 'paused', 'revoked')),
26
- scopes_json TEXT NOT NULL DEFAULT '[]',
27
- key_hash TEXT,
28
- binding_reference TEXT,
29
- created_at TEXT NOT NULL,
30
- rotated_at TEXT,
31
- revoked_at TEXT,
32
- UNIQUE (installation_id, identity_key),
33
- CHECK (key_hash IS NULL OR binding_reference IS NULL)
34
- );
35
-
36
- CREATE TABLE role_bindings (
37
- id TEXT PRIMARY KEY,
38
- installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
39
- principal_type TEXT NOT NULL CHECK (principal_type IN ('operator', 'service_identity')),
40
- principal_id TEXT NOT NULL,
41
- role TEXT NOT NULL CHECK (role IN (
42
- 'owner', 'administrator', 'workflow_author', 'reviewer', 'appeal_reviewer',
43
- 'operations', 'auditor', 'quality_reviewer'
44
- )),
45
- queue_scope_json TEXT NOT NULL DEFAULT '[]',
46
- field_scope_json TEXT NOT NULL DEFAULT '{}',
47
- created_by TEXT NOT NULL,
48
- created_at TEXT NOT NULL,
49
- revoked_at TEXT
50
- );
51
-
52
- CREATE INDEX role_bindings_principal_idx
53
- ON role_bindings(installation_id, principal_type, principal_id, revoked_at, role);
54
-
55
- CREATE TABLE connection_definitions (
56
- id TEXT PRIMARY KEY,
57
- installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
58
- connection_key TEXT NOT NULL,
59
- name TEXT NOT NULL,
60
- description TEXT NOT NULL DEFAULT '',
61
- kind TEXT NOT NULL CHECK (kind IN (
62
- 'webhook', 'external_api', 'customer_worker', 'ai_provider', 'notification', 'email'
63
- )),
64
- status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'paused', 'archived')),
65
- active_version_id TEXT,
66
- created_by TEXT NOT NULL,
67
- created_at TEXT NOT NULL,
68
- updated_at TEXT NOT NULL,
69
- UNIQUE (installation_id, connection_key)
70
- );
71
-
72
- CREATE INDEX connection_definitions_admin_idx
73
- ON connection_definitions(installation_id, status, kind, name, id);
74
-
75
- CREATE TABLE connection_versions (
76
- id TEXT PRIMARY KEY,
77
- connection_id TEXT NOT NULL REFERENCES connection_definitions(id) ON DELETE CASCADE,
78
- version INTEGER NOT NULL CHECK (version >= 1),
79
- base_url TEXT,
80
- service_binding_name TEXT,
81
- allowed_hosts_json TEXT NOT NULL DEFAULT '[]',
82
- allowed_methods_json TEXT NOT NULL DEFAULT '["POST"]',
83
- allowed_path_prefixes_json TEXT NOT NULL DEFAULT '["/"]',
84
- redirect_policy_json TEXT NOT NULL DEFAULT '{"follow":false,"maximum":0}',
85
- request_limit_bytes INTEGER NOT NULL DEFAULT 65536 CHECK (request_limit_bytes BETWEEN 1 AND 1048576),
86
- response_limit_bytes INTEGER NOT NULL DEFAULT 262144 CHECK (response_limit_bytes BETWEEN 1 AND 5242880),
87
- response_content_types_json TEXT NOT NULL DEFAULT '["application/json"]',
88
- timeout_ms INTEGER NOT NULL DEFAULT 5000 CHECK (timeout_ms BETWEEN 100 AND 120000),
89
- concurrency_limit INTEGER NOT NULL DEFAULT 8 CHECK (concurrency_limit BETWEEN 1 AND 1000),
90
- requests_per_window INTEGER NOT NULL DEFAULT 60 CHECK (requests_per_window BETWEEN 1 AND 1000000),
91
- window_seconds INTEGER NOT NULL DEFAULT 60 CHECK (window_seconds BETWEEN 1 AND 86400),
92
- credential_strategy TEXT NOT NULL DEFAULT 'none' CHECK (credential_strategy IN (
93
- 'none', 'static_header', 'bearer', 'hmac', 'oauth2_client_credentials',
94
- 'oauth2_authorization_code', 'mutual_tls', 'service_binding'
95
- )),
96
- secret_reference TEXT,
97
- signing_strategy_json TEXT NOT NULL DEFAULT '{}',
98
- oauth_configuration_json TEXT NOT NULL DEFAULT '{}',
99
- published_at TEXT NOT NULL,
100
- retired_at TEXT,
101
- created_by TEXT NOT NULL,
102
- created_at TEXT NOT NULL,
103
- UNIQUE (connection_id, version),
104
- CHECK ((base_url IS NOT NULL) != (service_binding_name IS NOT NULL))
105
- );
106
-
107
- CREATE INDEX connection_versions_lookup_idx
108
- ON connection_versions(connection_id, version DESC, id);
109
-
110
- CREATE TABLE connection_health (
111
- connection_version_id TEXT PRIMARY KEY REFERENCES connection_versions(id) ON DELETE CASCADE,
112
- state TEXT NOT NULL DEFAULT 'unknown' CHECK (state IN ('unknown', 'healthy', 'degraded', 'open_circuit', 'paused')),
113
- consecutive_failures INTEGER NOT NULL DEFAULT 0 CHECK (consecutive_failures >= 0),
114
- opened_at TEXT,
115
- next_probe_at TEXT,
116
- last_success_at TEXT,
117
- last_error_code TEXT,
118
- revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
119
- updated_at TEXT NOT NULL
120
- );
121
-
122
- CREATE INDEX connection_health_probe_idx
123
- ON connection_health(state, next_probe_at, connection_version_id);
124
-
125
- CREATE TABLE connection_credentials (
126
- id TEXT PRIMARY KEY,
127
- connection_version_id TEXT NOT NULL REFERENCES connection_versions(id) ON DELETE CASCADE,
128
- strategy TEXT NOT NULL CHECK (strategy IN (
129
- 'static_header', 'bearer', 'hmac', 'oauth2_client_credentials',
130
- 'oauth2_authorization_code', 'mutual_tls', 'service_binding'
131
- )),
132
- secret_reference TEXT,
133
- encrypted_access_token TEXT,
134
- encrypted_refresh_token TEXT,
135
- token_type TEXT,
136
- granted_scopes_json TEXT NOT NULL DEFAULT '[]',
137
- expires_at TEXT,
138
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'refresh_failed', 'revoked')),
139
- last_refresh_at TEXT,
140
- last_error_code TEXT,
141
- created_at TEXT NOT NULL,
142
- updated_at TEXT NOT NULL,
143
- UNIQUE (connection_version_id)
144
- );
145
-
146
- CREATE INDEX connection_credentials_refresh_idx
147
- ON connection_credentials(status, expires_at, id);
148
-
149
- CREATE TABLE connection_oauth_states (
150
- id TEXT PRIMARY KEY,
151
- connection_id TEXT NOT NULL REFERENCES connection_definitions(id) ON DELETE CASCADE,
152
- actor_id TEXT NOT NULL,
153
- operator_session_id TEXT,
154
- state_hash TEXT NOT NULL UNIQUE,
155
- pkce_verifier_ciphertext TEXT,
156
- redirect_uri TEXT NOT NULL,
157
- requested_scopes_json TEXT NOT NULL DEFAULT '[]',
158
- expires_at TEXT NOT NULL,
159
- consumed_at TEXT,
160
- created_at TEXT NOT NULL
161
- );
162
-
163
- CREATE INDEX connection_oauth_states_expiry_idx
164
- ON connection_oauth_states(expires_at, consumed_at, id);
165
-
166
- CREATE TABLE component_definitions (
167
- id TEXT PRIMARY KEY,
168
- installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
169
- component_key TEXT NOT NULL,
170
- name TEXT NOT NULL,
171
- description TEXT NOT NULL DEFAULT '',
172
- component_kind TEXT NOT NULL CHECK (component_kind IN (
173
- 'built_in', 'webhook_enrichment', 'api_enrichment', 'workers_ai',
174
- 'specialist_agent', 'action', 'message'
175
- )),
176
- effect_class TEXT NOT NULL CHECK (effect_class IN ('read_only', 'side_effecting')),
177
- owner_kind TEXT NOT NULL DEFAULT 'customer' CHECK (owner_kind IN ('safest', 'customer')),
178
- status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'paused', 'deprecated', 'archived')),
179
- active_version_id TEXT,
180
- created_by TEXT NOT NULL,
181
- created_at TEXT NOT NULL,
182
- updated_at TEXT NOT NULL,
183
- UNIQUE (installation_id, component_key)
184
- );
185
-
186
- CREATE INDEX component_definitions_admin_idx
187
- ON component_definitions(installation_id, status, component_kind, effect_class, name, id);
188
-
189
- CREATE TABLE component_versions (
190
- id TEXT PRIMARY KEY,
191
- definition_id TEXT NOT NULL REFERENCES component_definitions(id) ON DELETE CASCADE,
192
- version INTEGER NOT NULL CHECK (version >= 1),
193
- implementation_kind TEXT NOT NULL CHECK (implementation_kind IN (
194
- 'built_in', 'webhook', 'external_api', 'workers_ai', 'specialist_agent', 'message_template'
195
- )),
196
- implementation_reference TEXT NOT NULL,
197
- input_schema_json TEXT NOT NULL,
198
- output_schema_json TEXT NOT NULL,
199
- allowed_input_fields_json TEXT NOT NULL DEFAULT '[]',
200
- data_classifications_json TEXT NOT NULL DEFAULT '[]',
201
- connection_version_id TEXT REFERENCES connection_versions(id),
202
- risk_class TEXT NOT NULL DEFAULT 'low' CHECK (risk_class IN ('low', 'medium', 'high', 'critical')),
203
- timeout_policy_json TEXT NOT NULL,
204
- retry_policy_json TEXT NOT NULL,
205
- cache_policy_json TEXT NOT NULL DEFAULT '{"enabled":false}',
206
- concurrency_policy_json TEXT NOT NULL DEFAULT '{}',
207
- budget_policy_json TEXT NOT NULL DEFAULT '{}',
208
- approval_policy_json TEXT NOT NULL DEFAULT '{}',
209
- tool_manifest_json TEXT NOT NULL DEFAULT '[]',
210
- compatibility_json TEXT NOT NULL DEFAULT '{}',
211
- package_digest TEXT,
212
- signature_metadata_json TEXT,
213
- published_at TEXT NOT NULL,
214
- retired_at TEXT,
215
- created_by TEXT NOT NULL,
216
- created_at TEXT NOT NULL,
217
- UNIQUE (definition_id, version)
218
- );
219
-
220
- CREATE INDEX component_versions_lookup_idx
221
- ON component_versions(definition_id, version DESC, id);
222
- CREATE INDEX component_versions_connection_idx
223
- ON component_versions(connection_version_id, definition_id, id);
224
-
225
- CREATE TABLE component_fixtures (
226
- id TEXT PRIMARY KEY,
227
- component_version_id TEXT NOT NULL REFERENCES component_versions(id) ON DELETE CASCADE,
228
- name TEXT NOT NULL,
229
- input_json TEXT NOT NULL,
230
- expected_json TEXT NOT NULL,
231
- expected_status TEXT NOT NULL DEFAULT 'succeeded' CHECK (expected_status IN ('succeeded', 'failed', 'timed_out')),
232
- created_by TEXT NOT NULL,
233
- created_at TEXT NOT NULL,
234
- UNIQUE (component_version_id, name)
235
- );
236
-
237
- CREATE TABLE component_test_runs (
238
- id TEXT PRIMARY KEY,
239
- component_version_id TEXT NOT NULL REFERENCES component_versions(id) ON DELETE CASCADE,
240
- fixture_id TEXT REFERENCES component_fixtures(id) ON DELETE SET NULL,
241
- status TEXT NOT NULL CHECK (status IN ('running', 'passed', 'failed')),
242
- output_json TEXT,
243
- error_code TEXT,
244
- started_by TEXT NOT NULL,
245
- started_at TEXT NOT NULL,
246
- finished_at TEXT
247
- );
248
-
249
- CREATE INDEX component_test_runs_version_idx
250
- ON component_test_runs(component_version_id, started_at DESC, id DESC);
251
-
252
- INSERT INTO connection_definitions (
253
- id, installation_id, connection_key, name, description, kind, status,
254
- active_version_id, created_by, created_at, updated_at
255
- )
256
- SELECT
257
- 'legacy-enrichment-connection:' || d.id,
258
- d.installation_id,
259
- 'legacy-' || d.enrichment_key,
260
- d.name || ' connection',
261
- 'Migrated from webhook enrichment ' || d.enrichment_key,
262
- 'webhook',
263
- CASE d.status WHEN 'paused' THEN 'paused' WHEN 'archived' THEN 'archived' ELSE 'active' END,
264
- 'legacy-enrichment-connection-version:' || d.active_version_id,
265
- d.created_by,
266
- d.created_at,
267
- d.updated_at
268
- FROM enrichment_definitions d;
269
-
270
- INSERT INTO connection_versions (
271
- id, connection_id, version, base_url, service_binding_name,
272
- allowed_hosts_json, allowed_methods_json, allowed_path_prefixes_json,
273
- redirect_policy_json, request_limit_bytes, response_limit_bytes,
274
- response_content_types_json, timeout_ms, concurrency_limit,
275
- requests_per_window, window_seconds, credential_strategy, secret_reference,
276
- signing_strategy_json, oauth_configuration_json, published_at, retired_at,
277
- created_by, created_at
278
- )
279
- SELECT
280
- 'legacy-enrichment-connection-version:' || v.id,
281
- 'legacy-enrichment-connection:' || v.definition_id,
282
- v.version,
283
- v.webhook_url,
284
- NULL,
285
- json_array(json_extract(v.webhook_url, '$.host')),
286
- '["POST"]',
287
- '["/"]',
288
- '{"follow":false,"maximum":0}',
289
- 65536,
290
- 262144,
291
- '["application/json"]',
292
- v.timeout_ms,
293
- 8,
294
- 60,
295
- 60,
296
- 'hmac',
297
- v.secret_binding,
298
- '{"kind":"safest_hmac_v1"}',
299
- '{}',
300
- v.published_at,
301
- NULL,
302
- v.created_by,
303
- v.created_at
304
- FROM enrichment_definition_versions v;
305
-
306
- UPDATE connection_versions
307
- SET allowed_hosts_json = json_array(
308
- substr(
309
- base_url,
310
- instr(base_url, '://') + 3,
311
- CASE
312
- WHEN instr(substr(base_url, instr(base_url, '://') + 3), '/') = 0
313
- THEN length(base_url)
314
- ELSE instr(substr(base_url, instr(base_url, '://') + 3), '/') - 1
315
- END
316
- )
317
- )
318
- WHERE id LIKE 'legacy-enrichment-connection-version:%';
319
-
320
- INSERT INTO connection_health (
321
- connection_version_id, state, consecutive_failures, revision, updated_at
322
- )
323
- SELECT
324
- 'legacy-enrichment-connection-version:' || v.id,
325
- 'unknown',
326
- 0,
327
- 1,
328
- v.created_at
329
- FROM enrichment_definition_versions v;
330
-
331
- INSERT INTO component_definitions (
332
- id, installation_id, component_key, name, description, component_kind,
333
- effect_class, owner_kind, status, active_version_id, created_by, created_at, updated_at
334
- )
335
- SELECT
336
- d.id,
337
- d.installation_id,
338
- d.enrichment_key,
339
- d.name,
340
- d.description,
341
- 'webhook_enrichment',
342
- 'read_only',
343
- 'customer',
344
- CASE d.status WHEN 'paused' THEN 'paused' WHEN 'archived' THEN 'archived' ELSE 'active' END,
345
- d.active_version_id,
346
- d.created_by,
347
- d.created_at,
348
- d.updated_at
349
- FROM enrichment_definitions d;
350
-
351
- INSERT INTO component_versions (
352
- id, definition_id, version, implementation_kind, implementation_reference,
353
- input_schema_json, output_schema_json, allowed_input_fields_json,
354
- data_classifications_json, connection_version_id, risk_class,
355
- timeout_policy_json, retry_policy_json, cache_policy_json,
356
- concurrency_policy_json, budget_policy_json, approval_policy_json,
357
- tool_manifest_json, compatibility_json, published_at, created_by, created_at
358
- )
359
- SELECT
360
- v.id,
361
- v.definition_id,
362
- v.version,
363
- 'webhook',
364
- v.webhook_url,
365
- json_object('type', 'object', 'properties', json(v.input_fields_json)),
366
- v.output_schema_json,
367
- v.input_fields_json,
368
- '["report_operational"]',
369
- 'legacy-enrichment-connection-version:' || v.id,
370
- 'low',
371
- json_object('timeout_ms', v.timeout_ms),
372
- json_object('maximum_attempts', v.max_attempts, 'backoff', 'exponential'),
373
- json_object('enabled', CASE WHEN v.ttl_seconds > 0 THEN json('true') ELSE json('false') END, 'ttl_seconds', v.ttl_seconds),
374
- '{"maximum_concurrency":8}',
375
- '{}',
376
- '{}',
377
- '[]',
378
- '{"legacy_enrichment":true}',
379
- v.published_at,
380
- v.created_by,
381
- v.created_at
382
- FROM enrichment_definition_versions v;