@authrim/setup 0.1.134 → 0.1.136

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.
@@ -0,0 +1,1966 @@
1
+ -- =============================================================================
2
+ -- Fresh Schema for Authrim Core DB
3
+ -- Generated from conformance environment
4
+ -- =============================================================================
5
+
6
+ CREATE TABLE access_review_items (
7
+ id TEXT PRIMARY KEY,
8
+ review_id TEXT NOT NULL REFERENCES access_reviews(id) ON DELETE CASCADE,
9
+ tenant_id TEXT NOT NULL DEFAULT 'default',
10
+ user_id TEXT NOT NULL, -- User being reviewed
11
+ permission_type TEXT NOT NULL, -- role, permission, group_membership
12
+ permission_value TEXT NOT NULL, -- The specific permission/role/group
13
+ decision TEXT, -- approved, revoked, pending
14
+ decided_by TEXT, -- Reviewer who made decision
15
+ decided_at TEXT, -- When decision was made
16
+ justification TEXT, -- Reason for decision
17
+ created_at TEXT NOT NULL
18
+ );
19
+
20
+ CREATE TABLE access_reviews (
21
+ id TEXT PRIMARY KEY,
22
+ tenant_id TEXT NOT NULL DEFAULT 'default',
23
+ name TEXT NOT NULL, -- Review campaign name
24
+ description TEXT, -- Campaign description
25
+ scope TEXT NOT NULL, -- all_users, role, organization, application
26
+ scope_value TEXT, -- Value for scope (role_id, org_id, client_id)
27
+ status TEXT NOT NULL DEFAULT 'pending', -- pending, in_progress, completed, cancelled
28
+ reviewer_id TEXT, -- User assigned to review
29
+ total_items INTEGER NOT NULL DEFAULT 0, -- Total items to review
30
+ reviewed_items INTEGER NOT NULL DEFAULT 0, -- Items reviewed
31
+ approved_items INTEGER NOT NULL DEFAULT 0, -- Items approved (access retained)
32
+ revoked_items INTEGER NOT NULL DEFAULT 0, -- Items revoked (access removed)
33
+ created_at TEXT NOT NULL,
34
+ started_at TEXT, -- When review started
35
+ completed_at TEXT, -- When review completed
36
+ due_date TEXT -- Review deadline
37
+ );
38
+
39
+ CREATE TABLE admin_jobs (
40
+ -- Primary key
41
+ id TEXT PRIMARY KEY,
42
+
43
+ -- Multi-tenant support
44
+ tenant_id TEXT NOT NULL,
45
+
46
+ -- Job type (e.g., 'users/import', 'users/bulk-update', 'reports/generate')
47
+ job_type TEXT NOT NULL,
48
+
49
+ -- Job status (pending, processing, completed, failed, partial_failure)
50
+ status TEXT NOT NULL DEFAULT 'pending',
51
+
52
+ -- Progress tracking (JSON)
53
+ -- { "total": 100, "processed": 45, "succeeded": 43, "failed": 2 }
54
+ progress TEXT,
55
+
56
+ -- Job configuration (JSON)
57
+ -- Input parameters for the job
58
+ config TEXT,
59
+
60
+ -- R2 key for input file (for import jobs)
61
+ input_r2_key TEXT,
62
+
63
+ -- R2 key for result file (for completed jobs with large results)
64
+ result_r2_key TEXT,
65
+
66
+ -- Result summary (JSON, for completed jobs)
67
+ -- { "summary": {...}, "failures": [...] }
68
+ result TEXT,
69
+
70
+ -- Error information (for failed jobs)
71
+ error_code TEXT,
72
+ error_message TEXT,
73
+
74
+ -- Actor who created the job
75
+ created_by TEXT NOT NULL,
76
+
77
+ -- Timestamps (Unix timestamp in seconds)
78
+ created_at INTEGER NOT NULL,
79
+ updated_at INTEGER NOT NULL,
80
+ started_at INTEGER,
81
+ completed_at INTEGER,
82
+
83
+ -- Estimated completion time
84
+ estimated_completion INTEGER
85
+ );
86
+
87
+ CREATE TABLE ai_grants (
88
+ id TEXT PRIMARY KEY,
89
+ tenant_id TEXT NOT NULL DEFAULT 'default',
90
+ client_id TEXT NOT NULL,
91
+ ai_principal TEXT NOT NULL,
92
+ scopes TEXT NOT NULL,
93
+ scope_targets TEXT,
94
+ is_active INTEGER NOT NULL DEFAULT 1,
95
+ expires_at INTEGER,
96
+ created_by TEXT,
97
+ created_at INTEGER NOT NULL,
98
+ updated_at INTEGER NOT NULL,
99
+ revoked_at INTEGER,
100
+ revoked_by TEXT,
101
+ UNIQUE(tenant_id, client_id, ai_principal)
102
+ );
103
+
104
+ CREATE TABLE attribute_verifications (
105
+ id TEXT PRIMARY KEY,
106
+ tenant_id TEXT NOT NULL,
107
+ user_id TEXT NOT NULL,
108
+ vp_request_id TEXT REFERENCES vp_requests(id),
109
+ -- Issuer DID
110
+ issuer_did TEXT NOT NULL,
111
+ -- Verifiable Credential Type
112
+ credential_type TEXT NOT NULL,
113
+ -- Format: 'dc+sd-jwt' | 'mso_mdoc'
114
+ format TEXT NOT NULL,
115
+ -- Verification result: 'verified' | 'failed' | 'expired'
116
+ verification_result TEXT NOT NULL,
117
+ -- Individual verification flags
118
+ holder_binding_verified INTEGER DEFAULT 0,
119
+ issuer_trusted INTEGER DEFAULT 0,
120
+ status_valid INTEGER DEFAULT 0,
121
+ -- JSON array of user_verified_attributes IDs
122
+ mapped_attribute_ids TEXT,
123
+ verified_at TEXT DEFAULT (datetime('now')),
124
+ expires_at TEXT
125
+ );
126
+
127
+ CREATE TABLE audit_log (
128
+ id TEXT PRIMARY KEY,
129
+ user_id TEXT,
130
+ action TEXT NOT NULL,
131
+ resource_type TEXT,
132
+ resource_id TEXT,
133
+ ip_address TEXT,
134
+ user_agent TEXT,
135
+ metadata_json TEXT,
136
+ created_at INTEGER NOT NULL
137
+ , tenant_id TEXT NOT NULL DEFAULT 'default', severity TEXT DEFAULT 'info');
138
+
139
+ CREATE TABLE branding_settings (
140
+ id TEXT PRIMARY KEY DEFAULT 'default',
141
+ custom_css TEXT,
142
+ custom_html_header TEXT,
143
+ custom_html_footer TEXT,
144
+ logo_url TEXT,
145
+ background_image_url TEXT,
146
+ primary_color TEXT DEFAULT '#3B82F6',
147
+ secondary_color TEXT DEFAULT '#10B981',
148
+ font_family TEXT DEFAULT 'Inter',
149
+ -- Authentication method settings
150
+ enabled_auth_methods TEXT DEFAULT '["passkey","magic_link"]', -- JSON array
151
+ password_policy_json TEXT, -- Password policy config (if password auth enabled)
152
+ updated_at INTEGER NOT NULL
153
+ , tenant_id TEXT NOT NULL DEFAULT 'default');
154
+
155
+ CREATE TABLE check_api_keys (
156
+ id TEXT PRIMARY KEY,
157
+ tenant_id TEXT NOT NULL DEFAULT 'default',
158
+ client_id TEXT NOT NULL,
159
+ name TEXT NOT NULL,
160
+ key_hash TEXT NOT NULL, -- SHA-256 hash of the API key
161
+ key_prefix TEXT NOT NULL, -- First 8 chars (chk_xxxx) for identification
162
+ allowed_operations TEXT DEFAULT '["check"]', -- JSON array: check, batch, subscribe
163
+ rate_limit_tier TEXT DEFAULT 'moderate', -- strict, moderate, lenient
164
+ is_active INTEGER DEFAULT 1,
165
+ expires_at INTEGER, -- Unix timestamp, NULL = no expiry
166
+ created_by TEXT, -- User ID who created this key
167
+ created_at INTEGER NOT NULL,
168
+ updated_at INTEGER NOT NULL
169
+ );
170
+
171
+ CREATE TABLE "ciba_requests" (
172
+ auth_req_id TEXT PRIMARY KEY,
173
+ client_id TEXT NOT NULL,
174
+ scope TEXT NOT NULL,
175
+ login_hint TEXT,
176
+ login_hint_token TEXT,
177
+ id_token_hint TEXT,
178
+ binding_message TEXT,
179
+ user_code TEXT,
180
+ acr_values TEXT,
181
+ requested_expiry INTEGER,
182
+ status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'denied', 'expired')),
183
+ delivery_mode TEXT NOT NULL CHECK (delivery_mode IN ('poll', 'ping', 'push')),
184
+ client_notification_token TEXT,
185
+ client_notification_endpoint TEXT,
186
+ created_at INTEGER NOT NULL,
187
+ expires_at INTEGER NOT NULL,
188
+ last_poll_at INTEGER,
189
+ poll_count INTEGER DEFAULT 0,
190
+ interval INTEGER NOT NULL DEFAULT 5,
191
+ user_id TEXT,
192
+ sub TEXT,
193
+ nonce TEXT,
194
+ token_issued INTEGER DEFAULT 0,
195
+ token_issued_at INTEGER,
196
+ tenant_id TEXT NOT NULL DEFAULT 'default',
197
+ FOREIGN KEY (client_id) REFERENCES oauth_clients(client_id) ON DELETE CASCADE,
198
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
199
+ );
200
+
201
+ CREATE TABLE compliance_reports (
202
+ id TEXT PRIMARY KEY,
203
+ tenant_id TEXT NOT NULL DEFAULT 'default',
204
+ type TEXT NOT NULL, -- audit_log, access_report, user_activity, etc.
205
+ name TEXT NOT NULL, -- Report name/title
206
+ status TEXT NOT NULL DEFAULT 'pending', -- pending, generating, completed, failed
207
+ requested_by TEXT, -- User who requested the report
208
+ parameters TEXT, -- JSON: Report generation parameters
209
+ result_url TEXT, -- URL to download completed report
210
+ error_message TEXT, -- Error message if failed
211
+ created_at TEXT NOT NULL,
212
+ completed_at TEXT, -- When report generation completed
213
+ expires_at TEXT -- When report download expires
214
+ );
215
+
216
+ CREATE TABLE consent_history (
217
+ id TEXT PRIMARY KEY,
218
+ tenant_id TEXT NOT NULL DEFAULT 'default',
219
+ user_id TEXT NOT NULL,
220
+ client_id TEXT NOT NULL,
221
+ action TEXT NOT NULL, -- 'granted' | 'updated' | 'revoked' | 'version_upgraded' | 'expired' | 'scopes_updated'
222
+ scopes_before TEXT, -- JSON array of previous scopes (null for initial grant)
223
+ scopes_after TEXT, -- JSON array of new scopes (null for revocation)
224
+ privacy_policy_version TEXT,
225
+ tos_version TEXT,
226
+ ip_address_hash TEXT, -- Hashed IP for privacy
227
+ user_agent TEXT,
228
+ created_at INTEGER NOT NULL,
229
+ metadata_json TEXT, -- Additional context as JSON
230
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
231
+ );
232
+
233
+ CREATE TABLE consent_policy_versions (
234
+ id TEXT PRIMARY KEY,
235
+ tenant_id TEXT NOT NULL DEFAULT 'default',
236
+ version TEXT NOT NULL,
237
+ policy_type TEXT NOT NULL, -- 'privacy_policy' | 'terms_of_service' | 'cookie_policy'
238
+ policy_uri TEXT,
239
+ policy_hash TEXT, -- SHA-256 hash of policy content for integrity verification
240
+ effective_at INTEGER NOT NULL, -- Unix timestamp when this version becomes effective
241
+ created_at INTEGER NOT NULL,
242
+ UNIQUE (tenant_id, policy_type, version)
243
+ );
244
+
245
+ CREATE TABLE credential_configurations (
246
+ id TEXT PRIMARY KEY,
247
+ tenant_id TEXT NOT NULL,
248
+ -- Configuration ID (used in metadata)
249
+ configuration_id TEXT NOT NULL,
250
+ -- Format: 'dc+sd-jwt' | 'mso_mdoc'
251
+ format TEXT NOT NULL,
252
+ -- Verifiable Credential Type
253
+ vct TEXT NOT NULL,
254
+ -- JSON of display information
255
+ display TEXT,
256
+ -- JSON of claims configuration
257
+ claims TEXT,
258
+ -- JSON of proof types supported
259
+ proof_types_supported TEXT,
260
+ -- Signing algorithm
261
+ signing_alg TEXT DEFAULT 'ES256',
262
+ -- Active status
263
+ is_active INTEGER DEFAULT 1,
264
+ created_at TEXT DEFAULT (datetime('now')),
265
+ updated_at TEXT DEFAULT (datetime('now')),
266
+ UNIQUE(tenant_id, configuration_id)
267
+ );
268
+
269
+ CREATE TABLE credential_offers (
270
+ id TEXT PRIMARY KEY,
271
+ tenant_id TEXT NOT NULL,
272
+ user_id TEXT NOT NULL,
273
+ -- Credential configuration ID
274
+ credential_configuration_id TEXT NOT NULL,
275
+ -- Pre-authorized code
276
+ pre_authorized_code TEXT,
277
+ -- Transaction code (PIN)
278
+ tx_code TEXT,
279
+ -- JSON of grants configuration
280
+ grants TEXT NOT NULL,
281
+ -- Status: 'pending' | 'accepted' | 'issued' | 'failed' | 'expired'
282
+ status TEXT DEFAULT 'pending',
283
+ created_at TEXT DEFAULT (datetime('now')),
284
+ expires_at TEXT NOT NULL,
285
+ issued_at TEXT,
286
+ issued_credential_id TEXT REFERENCES issued_credentials(id)
287
+ );
288
+
289
+ CREATE TABLE d1_migrations(
290
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
291
+ name TEXT UNIQUE,
292
+ applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
293
+ );
294
+
295
+ CREATE TABLE data_export_requests (
296
+ id TEXT PRIMARY KEY,
297
+ tenant_id TEXT NOT NULL DEFAULT 'default',
298
+ user_id TEXT NOT NULL,
299
+ status TEXT NOT NULL DEFAULT 'pending', -- 'pending' | 'processing' | 'completed' | 'failed' | 'expired'
300
+ format TEXT NOT NULL DEFAULT 'json', -- 'json' | 'csv'
301
+ include_sections TEXT NOT NULL, -- JSON array: ["profile", "consents", "sessions", "audit_log", "passkeys"]
302
+ requested_at INTEGER NOT NULL,
303
+ started_at INTEGER,
304
+ completed_at INTEGER,
305
+ expires_at INTEGER, -- Download link expiration
306
+ file_path TEXT, -- R2 object path (for async exports)
307
+ file_size INTEGER,
308
+ error_message TEXT,
309
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
310
+ );
311
+
312
+ CREATE TABLE device_codes (
313
+ device_code TEXT PRIMARY KEY,
314
+ user_code TEXT UNIQUE NOT NULL,
315
+ client_id TEXT NOT NULL,
316
+ scope TEXT NOT NULL,
317
+ status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'denied', 'expired')),
318
+ user_id TEXT,
319
+ sub TEXT,
320
+ created_at INTEGER NOT NULL,
321
+ expires_at INTEGER NOT NULL,
322
+ last_poll_at INTEGER,
323
+ poll_count INTEGER DEFAULT 0, tenant_id TEXT NOT NULL DEFAULT 'default',
324
+ FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE
325
+ );
326
+
327
+ CREATE TABLE did_document_cache (
328
+ did TEXT PRIMARY KEY,
329
+ -- JSON of DID Document
330
+ document TEXT NOT NULL,
331
+ resolved_at TEXT DEFAULT (datetime('now')),
332
+ expires_at TEXT NOT NULL
333
+ );
334
+
335
+ CREATE TABLE external_idp_auth_states (
336
+ id TEXT PRIMARY KEY,
337
+ tenant_id TEXT NOT NULL DEFAULT 'default',
338
+ provider_id TEXT NOT NULL, -- References upstream_providers(id)
339
+ state TEXT UNIQUE NOT NULL, -- OAuth state parameter
340
+ nonce TEXT, -- OIDC nonce for ID token validation
341
+ code_verifier TEXT, -- PKCE code verifier
342
+ redirect_uri TEXT NOT NULL, -- Where to redirect after auth
343
+
344
+ -- For linking flow
345
+ user_id TEXT, -- Set if linking to existing account
346
+ session_id TEXT, -- Authrim session (for linking flow)
347
+
348
+ -- For OIDC proxy flow (future)
349
+ original_auth_request TEXT, -- JSON of original OIDC auth request
350
+
351
+ -- OIDC Core 1.0 parameters (for validation in callback)
352
+ max_age INTEGER, -- max_age parameter for auth_time validation
353
+ acr_values TEXT, -- acr_values parameter for acr validation
354
+
355
+ -- Timestamps
356
+ expires_at INTEGER NOT NULL,
357
+ created_at INTEGER NOT NULL,
358
+ consumed_at INTEGER, -- When state was consumed (for single-use)
359
+
360
+ FOREIGN KEY (provider_id) REFERENCES upstream_providers(id) ON DELETE CASCADE
361
+ );
362
+
363
+ CREATE TABLE "flows" (
364
+ id TEXT PRIMARY KEY NOT NULL,
365
+ tenant_id TEXT NOT NULL DEFAULT 'default',
366
+ client_id TEXT,
367
+ profile_id TEXT NOT NULL,
368
+ name TEXT NOT NULL,
369
+ description TEXT,
370
+ graph_definition TEXT NOT NULL,
371
+ compiled_plan TEXT,
372
+ version TEXT NOT NULL DEFAULT '1.0.0',
373
+ is_active INTEGER NOT NULL DEFAULT 1,
374
+ is_builtin INTEGER NOT NULL DEFAULT 0,
375
+ created_by TEXT,
376
+ created_at INTEGER NOT NULL,
377
+ updated_by TEXT,
378
+ updated_at INTEGER NOT NULL
379
+ );
380
+
381
+ CREATE TABLE idempotency_keys (
382
+ id TEXT PRIMARY KEY, -- Composite: tenant_id:actor_id:method:path:resource_id:key
383
+ tenant_id TEXT NOT NULL,
384
+ actor_id TEXT NOT NULL, -- admin_id who made the request
385
+ method TEXT NOT NULL, -- HTTP method (POST, PUT, DELETE)
386
+ path TEXT NOT NULL, -- API path pattern
387
+ resource_id TEXT, -- Target resource ID (if applicable)
388
+ idempotency_key TEXT NOT NULL,-- The Idempotency-Key header value
389
+ body_hash TEXT NOT NULL, -- SHA-256 hash of request body
390
+ response_status INTEGER NOT NULL,
391
+ response_body TEXT NOT NULL, -- Sanitized response (PII removed)
392
+ created_at INTEGER NOT NULL,
393
+ expires_at INTEGER NOT NULL,
394
+
395
+ FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
396
+ );
397
+
398
+ CREATE TABLE identity_providers (
399
+ id TEXT PRIMARY KEY,
400
+ name TEXT NOT NULL,
401
+ provider_type TEXT NOT NULL,
402
+ config_json TEXT NOT NULL,
403
+ enabled INTEGER DEFAULT 1,
404
+ created_at INTEGER NOT NULL,
405
+ updated_at INTEGER NOT NULL
406
+ , tenant_id TEXT NOT NULL DEFAULT 'default');
407
+
408
+ CREATE TABLE issued_credentials (
409
+ id TEXT PRIMARY KEY,
410
+ tenant_id TEXT NOT NULL,
411
+ user_id TEXT NOT NULL,
412
+ -- Verifiable Credential Type
413
+ credential_type TEXT NOT NULL,
414
+ -- Format: 'dc+sd-jwt' | 'mso_mdoc'
415
+ format TEXT NOT NULL,
416
+ -- JSON of claims included in credential
417
+ claims TEXT NOT NULL,
418
+ -- Status: 'active' | 'suspended' | 'revoked'
419
+ status TEXT DEFAULT 'active',
420
+ -- Status list index for revocation
421
+ status_list_index INTEGER,
422
+ created_at TEXT DEFAULT (datetime('now')),
423
+ expires_at TEXT,
424
+ revoked_at TEXT,
425
+ revoked_reason TEXT
426
+ );
427
+
428
+ CREATE TABLE "linked_identities" (
429
+ id TEXT PRIMARY KEY,
430
+ tenant_id TEXT NOT NULL DEFAULT 'default',
431
+ user_id TEXT NOT NULL,
432
+ provider_id TEXT NOT NULL,
433
+ provider_user_id TEXT NOT NULL,
434
+ provider_email TEXT,
435
+ email_verified INTEGER DEFAULT 0,
436
+ access_token_encrypted TEXT,
437
+ refresh_token_encrypted TEXT,
438
+ token_expires_at INTEGER,
439
+ raw_claims TEXT,
440
+ profile_data TEXT,
441
+ linked_at INTEGER NOT NULL,
442
+ last_login_at INTEGER,
443
+ updated_at INTEGER NOT NULL,
444
+ UNIQUE(provider_id, provider_user_id),
445
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE,
446
+ FOREIGN KEY (provider_id) REFERENCES upstream_providers(id) ON DELETE CASCADE
447
+ );
448
+
449
+ CREATE TABLE migration_metadata (
450
+ id TEXT PRIMARY KEY DEFAULT 'global',
451
+
452
+ -- Current schema version (highest applied migration version)
453
+ current_version INTEGER NOT NULL DEFAULT 0,
454
+
455
+ -- Last migration applied timestamp
456
+ last_migration_at INTEGER,
457
+
458
+ -- Environment (development, staging, production)
459
+ environment TEXT DEFAULT 'development',
460
+
461
+ -- Additional metadata as JSON
462
+ metadata_json TEXT
463
+ , tenant_id TEXT NOT NULL DEFAULT 'default');
464
+
465
+ CREATE TABLE "oauth_client_consents" (
466
+ id TEXT PRIMARY KEY,
467
+ user_id TEXT NOT NULL,
468
+ client_id TEXT NOT NULL,
469
+ scope TEXT NOT NULL,
470
+ granted_at INTEGER NOT NULL,
471
+ expires_at INTEGER,
472
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
473
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
474
+ tenant_id TEXT NOT NULL DEFAULT 'default', selected_scopes TEXT, privacy_policy_version TEXT, tos_version TEXT, consent_version INTEGER DEFAULT 1,
475
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE,
476
+ FOREIGN KEY (client_id) REFERENCES oauth_clients(client_id) ON DELETE CASCADE,
477
+ UNIQUE (user_id, client_id)
478
+ );
479
+
480
+ CREATE TABLE oauth_clients (
481
+ client_id TEXT PRIMARY KEY,
482
+ client_name TEXT NOT NULL,
483
+ redirect_uris TEXT NOT NULL,
484
+ grant_types TEXT NOT NULL,
485
+ response_types TEXT NOT NULL,
486
+ scope TEXT,
487
+ logo_uri TEXT,
488
+ client_uri TEXT,
489
+ policy_uri TEXT,
490
+ tos_uri TEXT,
491
+ contacts TEXT,
492
+ subject_type TEXT DEFAULT 'public',
493
+ sector_identifier_uri TEXT,
494
+ token_endpoint_auth_method TEXT DEFAULT 'client_secret_basic',
495
+ -- RFC 8693: Token Exchange settings
496
+ token_exchange_allowed INTEGER DEFAULT 0,
497
+ allowed_subject_token_clients TEXT, -- JSON array of client IDs
498
+ allowed_token_exchange_resources TEXT, -- JSON array of resource URIs
499
+ delegation_mode TEXT DEFAULT 'delegation', -- 'none' | 'delegation' | 'impersonation'
500
+ -- RFC 6749 Section 4.4: Client Credentials settings
501
+ client_credentials_allowed INTEGER DEFAULT 0,
502
+ allowed_scopes TEXT, -- JSON array of allowed scopes
503
+ default_scope TEXT, -- Default scope for Client Credentials
504
+ default_audience TEXT, -- Default audience for Client Credentials
505
+ created_at INTEGER NOT NULL,
506
+ updated_at INTEGER NOT NULL
507
+ , is_trusted INTEGER DEFAULT 0, skip_consent INTEGER DEFAULT 0, allow_claims_without_scope INTEGER DEFAULT 0, backchannel_token_delivery_mode TEXT, backchannel_client_notification_endpoint TEXT, backchannel_authentication_request_signing_alg TEXT, backchannel_user_code_parameter INTEGER DEFAULT 0, tenant_id TEXT NOT NULL DEFAULT 'default', jwks TEXT, jwks_uri TEXT, userinfo_signed_response_alg TEXT, post_logout_redirect_uris TEXT, allowed_redirect_origins TEXT, backchannel_logout_uri TEXT, backchannel_logout_session_required INTEGER DEFAULT 0, frontchannel_logout_uri TEXT, frontchannel_logout_session_required INTEGER DEFAULT 0, logout_webhook_uri TEXT, logout_webhook_secret_encrypted TEXT, registration_access_token_hash TEXT, initiate_login_uri TEXT, id_token_signed_response_alg TEXT, request_object_signing_alg TEXT, client_secret_hash TEXT, software_id TEXT, software_version TEXT, requestable_scopes TEXT);
508
+
509
+ CREATE TABLE operational_logs (
510
+ id TEXT PRIMARY KEY,
511
+ tenant_id TEXT NOT NULL,
512
+ subject_type TEXT NOT NULL, -- Code expects: 'user', 'client', 'session'
513
+ subject_id TEXT NOT NULL, -- Code expects this name, not 'resource_id'
514
+ actor_id TEXT NOT NULL, -- Who performed the operation
515
+ action TEXT NOT NULL, -- 'user.suspend', 'user.lock', etc.
516
+ reason_detail_encrypted TEXT,-- AES-GCM encrypted reason_detail
517
+ encryption_key_version INTEGER NOT NULL DEFAULT 1, -- Code expects this column
518
+ request_id TEXT, -- X-Request-ID header value
519
+ created_at INTEGER NOT NULL,
520
+ expires_at INTEGER NOT NULL, -- When this log should be deleted
521
+
522
+ FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
523
+ );
524
+
525
+ CREATE TABLE org_domain_mappings (
526
+ -- Primary key
527
+ id TEXT PRIMARY KEY,
528
+
529
+ -- Multi-tenant support
530
+ tenant_id TEXT NOT NULL DEFAULT 'default',
531
+
532
+ -- Domain identification (hashed for privacy)
533
+ -- Algorithm: HMAC-SHA256(lowercase(domain), secret_key)
534
+ domain_hash TEXT NOT NULL,
535
+
536
+ -- Key rotation support
537
+ domain_hash_version INTEGER DEFAULT 1,
538
+
539
+ -- Target organization
540
+ org_id TEXT NOT NULL, -- Reference to organizations.id
541
+
542
+ -- Auto-join settings
543
+ auto_join_enabled INTEGER DEFAULT 1, -- 0 = mapping exists but auto-join disabled
544
+ membership_type TEXT NOT NULL DEFAULT 'member', -- member, admin, owner
545
+ auto_assign_role_id TEXT, -- Optional: auto-assign this role on join
546
+
547
+ -- Verification status
548
+ verified INTEGER DEFAULT 0, -- 1 = domain ownership verified (DNS TXT, etc.)
549
+
550
+ -- Priority for multiple mappings
551
+ priority INTEGER DEFAULT 0, -- Higher = preferred when multiple match
552
+
553
+ -- Status
554
+ is_active INTEGER DEFAULT 1,
555
+
556
+ -- Timestamps
557
+ created_at INTEGER NOT NULL,
558
+ updated_at INTEGER NOT NULL, verification_token TEXT, verification_status TEXT DEFAULT 'unverified', verification_expires_at INTEGER, verification_method TEXT,
559
+
560
+ -- Constraints
561
+ -- Allow same domain to map to multiple orgs with different versions
562
+ UNIQUE(tenant_id, domain_hash, domain_hash_version, org_id)
563
+ );
564
+
565
+ CREATE TABLE organizations (
566
+ id TEXT PRIMARY KEY,
567
+ tenant_id TEXT NOT NULL DEFAULT 'default',
568
+ name TEXT NOT NULL,
569
+ display_name TEXT,
570
+ description TEXT,
571
+ org_type TEXT NOT NULL DEFAULT 'enterprise', -- distributor, enterprise, department
572
+ parent_org_id TEXT REFERENCES organizations(id),
573
+ plan TEXT DEFAULT 'free', -- free, starter, professional, enterprise
574
+ is_active INTEGER DEFAULT 1,
575
+ metadata_json TEXT,
576
+ created_at INTEGER NOT NULL,
577
+ updated_at INTEGER NOT NULL
578
+ );
579
+
580
+ CREATE TABLE "passkeys" (
581
+ id TEXT PRIMARY KEY,
582
+ user_id TEXT NOT NULL,
583
+ credential_id TEXT UNIQUE NOT NULL,
584
+ public_key TEXT NOT NULL,
585
+ counter INTEGER DEFAULT 0,
586
+ transports TEXT,
587
+ device_name TEXT,
588
+ created_at INTEGER NOT NULL,
589
+ last_used_at INTEGER,
590
+ tenant_id TEXT NOT NULL DEFAULT 'default',
591
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
592
+ );
593
+
594
+ CREATE TABLE "password_reset_tokens" (
595
+ id TEXT PRIMARY KEY,
596
+ user_id TEXT NOT NULL,
597
+ token_hash TEXT UNIQUE NOT NULL,
598
+ expires_at INTEGER NOT NULL,
599
+ used INTEGER DEFAULT 0,
600
+ created_at INTEGER NOT NULL,
601
+ tenant_id TEXT NOT NULL DEFAULT 'default',
602
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
603
+ );
604
+
605
+ CREATE TABLE permission_change_audit (
606
+ id TEXT PRIMARY KEY,
607
+ tenant_id TEXT NOT NULL DEFAULT 'default',
608
+ event_type TEXT NOT NULL, -- 'grant', 'revoke', 'modify'
609
+ subject_id TEXT NOT NULL,
610
+ resource TEXT, -- Resource affected (optional)
611
+ relation TEXT, -- Relation affected (optional)
612
+ permission TEXT, -- Permission affected (optional)
613
+ timestamp INTEGER NOT NULL, -- Event timestamp (Unix milliseconds)
614
+ created_at INTEGER NOT NULL -- Record creation time (Unix seconds)
615
+ );
616
+
617
+ CREATE TABLE permission_check_audit (
618
+ id TEXT PRIMARY KEY,
619
+ tenant_id TEXT NOT NULL DEFAULT 'default',
620
+ subject_id TEXT NOT NULL,
621
+ permission TEXT NOT NULL, -- Original permission string
622
+ permission_json TEXT, -- Structured permission (if provided)
623
+ allowed INTEGER NOT NULL, -- 1 = allowed, 0 = denied
624
+ resolved_via_json TEXT NOT NULL, -- JSON array: ["role", "rebac"]
625
+ final_decision TEXT NOT NULL, -- 'allow' | 'deny'
626
+ reason TEXT, -- Denial reason (when denied)
627
+ api_key_id TEXT, -- Which API key was used (if any)
628
+ client_id TEXT, -- Client ID (from API key or token)
629
+ checked_at INTEGER NOT NULL -- Unix timestamp
630
+ );
631
+
632
+ CREATE TABLE policy_rules (
633
+ id TEXT PRIMARY KEY NOT NULL,
634
+ tenant_id TEXT NOT NULL,
635
+
636
+ -- Rule identification
637
+ name TEXT NOT NULL,
638
+ description TEXT,
639
+
640
+ -- Rule configuration
641
+ priority INTEGER NOT NULL DEFAULT 100,
642
+ effect TEXT NOT NULL CHECK (effect IN ('allow', 'deny')),
643
+
644
+ -- Target matching (JSON arrays)
645
+ resource_types TEXT, -- JSON array of resource types to match
646
+ actions TEXT, -- JSON array of actions to match
647
+
648
+ -- Conditions (JSON array of PolicyCondition objects)
649
+ conditions TEXT NOT NULL DEFAULT '[]',
650
+
651
+ -- Status
652
+ enabled INTEGER NOT NULL DEFAULT 1,
653
+
654
+ -- Audit
655
+ created_by TEXT,
656
+ created_at INTEGER NOT NULL,
657
+ updated_by TEXT,
658
+ updated_at INTEGER NOT NULL,
659
+
660
+ -- Indexes
661
+ UNIQUE(tenant_id, name)
662
+ );
663
+
664
+ CREATE TABLE policy_simulations (
665
+ id TEXT PRIMARY KEY NOT NULL,
666
+ tenant_id TEXT NOT NULL,
667
+
668
+ -- Simulation input (JSON)
669
+ context TEXT NOT NULL,
670
+
671
+ -- Simulation result
672
+ allowed INTEGER NOT NULL,
673
+ reason TEXT NOT NULL,
674
+ decided_by TEXT,
675
+
676
+ -- Details (JSON)
677
+ details TEXT,
678
+ matched_rules TEXT, -- JSON array of rule IDs that were evaluated
679
+
680
+ -- Audit
681
+ simulated_by TEXT,
682
+ simulated_at INTEGER NOT NULL
683
+ );
684
+
685
+ CREATE TABLE presentation_definitions (
686
+ id TEXT PRIMARY KEY,
687
+ tenant_id TEXT NOT NULL,
688
+ name TEXT NOT NULL,
689
+ purpose TEXT,
690
+ -- JSON: {"dc+sd-jwt": {...}, "mso_mdoc": {...}}
691
+ format TEXT NOT NULL,
692
+ -- JSON array of input descriptors
693
+ input_descriptors TEXT NOT NULL,
694
+ -- JSON for complex submission requirements
695
+ submission_requirements TEXT,
696
+ -- DCQL query (preferred for HAIP)
697
+ dcql_query TEXT,
698
+ -- Active status
699
+ is_active INTEGER DEFAULT 1,
700
+ created_at TEXT DEFAULT (datetime('now')),
701
+ updated_at TEXT DEFAULT (datetime('now'))
702
+ );
703
+
704
+ CREATE TABLE refresh_token_shard_configs (
705
+ id TEXT PRIMARY KEY, -- UUID
706
+ tenant_id TEXT NOT NULL DEFAULT 'default',
707
+ client_id TEXT, -- NULL = global config
708
+ generation INTEGER NOT NULL,
709
+ shard_count INTEGER NOT NULL,
710
+ activated_at INTEGER NOT NULL, -- When this config was activated (ms)
711
+ deprecated_at INTEGER, -- When this config was deprecated (ms)
712
+ created_by TEXT, -- Admin user who created this config
713
+ notes TEXT, -- Human-readable notes
714
+
715
+ UNIQUE(tenant_id, client_id, generation)
716
+ );
717
+
718
+ CREATE TABLE relation_definitions (
719
+ id TEXT PRIMARY KEY,
720
+ tenant_id TEXT NOT NULL,
721
+ -- Object type this definition applies to
722
+ object_type TEXT NOT NULL, -- 'document', 'folder', 'org', etc.
723
+ -- Relation name being defined
724
+ relation_name TEXT NOT NULL, -- 'viewer', 'editor', 'owner', etc.
725
+ -- Relation composition rule (JSON)
726
+ definition_json TEXT NOT NULL,
727
+ -- Description for documentation
728
+ description TEXT,
729
+ -- Evaluation priority (higher = evaluated first)
730
+ priority INTEGER DEFAULT 0,
731
+ -- Whether this definition is active
732
+ is_active INTEGER DEFAULT 1,
733
+ -- Timestamps
734
+ created_at INTEGER NOT NULL,
735
+ updated_at INTEGER NOT NULL
736
+ );
737
+
738
+ CREATE TABLE relationship_closure (
739
+ id TEXT PRIMARY KEY,
740
+ tenant_id TEXT NOT NULL,
741
+ -- Ancestor (source) entity
742
+ ancestor_type TEXT NOT NULL, -- 'subject', 'org', 'group'
743
+ ancestor_id TEXT NOT NULL,
744
+ -- Descendant (target) entity
745
+ descendant_type TEXT NOT NULL, -- 'document', 'folder', 'org', 'resource'
746
+ descendant_id TEXT NOT NULL,
747
+ -- Computed relation (derived from relationship chain)
748
+ relation TEXT NOT NULL, -- 'viewer', 'editor', 'owner'
749
+ -- Path information
750
+ depth INTEGER NOT NULL, -- Number of hops (0 = direct)
751
+ path_json TEXT, -- JSON array of relationship IDs in the path
752
+ -- Computed metadata
753
+ effective_permission TEXT, -- Most restrictive permission in path
754
+ -- Timestamps
755
+ created_at INTEGER NOT NULL,
756
+ updated_at INTEGER NOT NULL
757
+ );
758
+
759
+ CREATE TABLE relationships (
760
+ id TEXT PRIMARY KEY,
761
+ tenant_id TEXT NOT NULL DEFAULT 'default',
762
+ relationship_type TEXT NOT NULL, -- parent_child, guardian, delegate, manager, reseller_of
763
+ from_type TEXT NOT NULL DEFAULT 'subject', -- subject, org (future)
764
+ from_id TEXT NOT NULL, -- subject_id or org_id
765
+ to_type TEXT NOT NULL DEFAULT 'subject', -- subject, org (future)
766
+ to_id TEXT NOT NULL, -- subject_id or org_id
767
+ permission_level TEXT NOT NULL DEFAULT 'full', -- full, limited, read_only
768
+ expires_at INTEGER, -- Optional expiration (UNIX seconds)
769
+ is_bidirectional INTEGER DEFAULT 0, -- Phase 1: always 0
770
+ metadata_json TEXT, -- Additional constraints, notes, etc.
771
+ created_at INTEGER NOT NULL,
772
+ updated_at INTEGER NOT NULL
773
+ , evidence_type TEXT DEFAULT 'manual', evidence_ref TEXT);
774
+
775
+ CREATE TABLE resource_permissions (
776
+ -- Primary key
777
+ id TEXT PRIMARY KEY,
778
+
779
+ -- Multi-tenant support
780
+ tenant_id TEXT NOT NULL DEFAULT 'default',
781
+
782
+ -- Subject (who has the permission)
783
+ subject_type TEXT NOT NULL DEFAULT 'user', -- 'user' | 'role' | 'org'
784
+ subject_id TEXT NOT NULL, -- user_id, role_id, or org_id
785
+
786
+ -- Resource (what is being accessed)
787
+ resource_type TEXT NOT NULL, -- e.g., 'documents', 'projects'
788
+ resource_id TEXT NOT NULL, -- e.g., 'doc_123', 'proj_456'
789
+
790
+ -- Actions allowed (JSON array)
791
+ -- Example: ["read", "write", "delete"]
792
+ actions_json TEXT NOT NULL,
793
+
794
+ -- Optional condition for permission (JSON)
795
+ -- Example: {"time_restricted": true, "hours": [9, 17]}
796
+ condition_json TEXT,
797
+
798
+ -- Expiration (UNIX seconds)
799
+ -- NULL = no expiration
800
+ -- Evaluated at token generation time only
801
+ expires_at INTEGER,
802
+
803
+ -- Status
804
+ is_active INTEGER DEFAULT 1,
805
+
806
+ -- Audit fields
807
+ granted_by TEXT, -- Admin or system that granted
808
+ created_at INTEGER NOT NULL,
809
+ updated_at INTEGER NOT NULL,
810
+
811
+ -- Constraints
812
+ -- Same subject can have only one permission entry per resource
813
+ UNIQUE(tenant_id, subject_type, subject_id, resource_type, resource_id)
814
+ );
815
+
816
+ CREATE TABLE role_assignment_rules (
817
+ -- Primary key
818
+ id TEXT PRIMARY KEY,
819
+
820
+ -- Multi-tenant support
821
+ tenant_id TEXT NOT NULL DEFAULT 'default',
822
+
823
+ -- Rule identification
824
+ name TEXT NOT NULL,
825
+ description TEXT,
826
+
827
+ -- Target role (reference only, no FK for flexibility)
828
+ role_id TEXT NOT NULL,
829
+
830
+ -- Scope for assigned role
831
+ scope_type TEXT NOT NULL DEFAULT 'global', -- global, org, resource
832
+ scope_target TEXT NOT NULL DEFAULT '', -- e.g., 'org:org_123' or '' for global
833
+
834
+ -- Conditions (JSON format)
835
+ -- Example: {"type": "and", "conditions": [
836
+ -- {"field": "email_domain_hash", "operator": "eq", "value": "abc123..."},
837
+ -- {"field": "idp_claim", "claim_path": "groups", "operator": "contains", "value": "admin"}
838
+ -- ]}
839
+ conditions_json TEXT NOT NULL,
840
+
841
+ -- Actions (JSON format)
842
+ -- Example: [
843
+ -- {"type": "assign_role", "role_id": "role_org_admin", "scope_type": "org", "scope_target": "auto"},
844
+ -- {"type": "join_org", "org_id": "auto"}
845
+ -- ]
846
+ actions_json TEXT NOT NULL,
847
+
848
+ -- Priority and control
849
+ priority INTEGER NOT NULL DEFAULT 0, -- Higher = evaluated first (DESC order)
850
+ stop_processing INTEGER DEFAULT 0, -- 1 = stop evaluating further rules after match
851
+ is_active INTEGER DEFAULT 1, -- 0 = disabled
852
+
853
+ -- Validity period (optional, UNIX seconds)
854
+ valid_from INTEGER, -- NULL = no start restriction
855
+ valid_until INTEGER, -- NULL = no end restriction
856
+
857
+ -- Audit fields
858
+ created_by TEXT, -- Admin user ID who created
859
+ created_at INTEGER NOT NULL,
860
+ updated_at INTEGER NOT NULL,
861
+
862
+ -- Constraints
863
+ UNIQUE(tenant_id, name)
864
+ );
865
+
866
+ CREATE TABLE "role_assignments" (
867
+ id TEXT PRIMARY KEY,
868
+ tenant_id TEXT NOT NULL DEFAULT 'default',
869
+ subject_id TEXT NOT NULL,
870
+ role_id TEXT NOT NULL,
871
+ scope_type TEXT NOT NULL DEFAULT 'global',
872
+ scope_target TEXT NOT NULL DEFAULT '',
873
+ expires_at INTEGER,
874
+ assigned_by TEXT,
875
+ metadata_json TEXT,
876
+ created_at INTEGER NOT NULL,
877
+ updated_at INTEGER NOT NULL,
878
+ FOREIGN KEY (subject_id) REFERENCES users_core(id) ON DELETE CASCADE,
879
+ FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
880
+ );
881
+
882
+ CREATE TABLE roles (
883
+ id TEXT PRIMARY KEY,
884
+ name TEXT UNIQUE NOT NULL,
885
+ description TEXT,
886
+ permissions_json TEXT NOT NULL,
887
+ created_at INTEGER NOT NULL
888
+ , tenant_id TEXT NOT NULL DEFAULT 'default', role_type TEXT NOT NULL DEFAULT 'custom', hierarchy_level INTEGER DEFAULT 0, is_assignable INTEGER DEFAULT 1, parent_role_id TEXT REFERENCES roles(id), display_name TEXT, is_system INTEGER NOT NULL DEFAULT 0, updated_at INTEGER);
889
+
890
+ CREATE TABLE schema_migrations (
891
+ -- Migration version (from filename: 001_initial_schema.sql -> version = 1)
892
+ version INTEGER PRIMARY KEY,
893
+
894
+ -- Human-readable migration name (from filename: 001_initial_schema.sql -> name = "initial_schema")
895
+ name TEXT NOT NULL,
896
+
897
+ -- When the migration was applied (Unix timestamp in seconds)
898
+ applied_at INTEGER NOT NULL,
899
+
900
+ -- SHA-256 checksum of the migration SQL file (detects file modifications)
901
+ checksum TEXT NOT NULL,
902
+
903
+ -- How long the migration took to execute (milliseconds)
904
+ execution_time_ms INTEGER,
905
+
906
+ -- Optional: SQL for rolling back this migration
907
+ rollback_sql TEXT
908
+ );
909
+
910
+ CREATE TABLE scope_mappings (
911
+ scope TEXT NOT NULL,
912
+ claim_name TEXT NOT NULL,
913
+ source_table TEXT NOT NULL,
914
+ source_column TEXT NOT NULL,
915
+ transformation TEXT,
916
+ condition TEXT,
917
+ created_at INTEGER NOT NULL, tenant_id TEXT NOT NULL DEFAULT 'default',
918
+ PRIMARY KEY (scope, claim_name)
919
+ );
920
+
921
+ CREATE TABLE security_alerts (
922
+ id TEXT PRIMARY KEY,
923
+ tenant_id TEXT NOT NULL,
924
+ type TEXT NOT NULL CHECK (type IN (
925
+ 'brute_force',
926
+ 'credential_stuffing',
927
+ 'suspicious_login',
928
+ 'impossible_travel',
929
+ 'account_takeover',
930
+ 'mfa_bypass_attempt',
931
+ 'token_abuse',
932
+ 'rate_limit_exceeded',
933
+ 'config_change',
934
+ 'privilege_escalation',
935
+ 'data_exfiltration',
936
+ 'other'
937
+ )),
938
+ severity TEXT NOT NULL CHECK (severity IN ('critical', 'high', 'medium', 'low', 'info')),
939
+ status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'acknowledged', 'resolved', 'dismissed')),
940
+ title TEXT NOT NULL,
941
+ description TEXT,
942
+ source_ip TEXT,
943
+ user_id TEXT,
944
+ client_id TEXT,
945
+ metadata TEXT, -- JSON string for additional context
946
+ created_at INTEGER NOT NULL,
947
+ updated_at INTEGER NOT NULL,
948
+ acknowledged_at INTEGER,
949
+ acknowledged_by TEXT,
950
+ resolved_at INTEGER,
951
+ resolved_by TEXT,
952
+
953
+ FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
954
+ );
955
+
956
+ CREATE TABLE security_threats (
957
+ id TEXT PRIMARY KEY,
958
+ tenant_id TEXT NOT NULL DEFAULT 'default',
959
+ type TEXT NOT NULL, -- credential_compromise, attack_pattern, vulnerability, etc.
960
+ severity TEXT NOT NULL, -- critical, high, medium, low, info
961
+ status TEXT NOT NULL DEFAULT 'active', -- active, investigating, mitigated, resolved
962
+ title TEXT NOT NULL, -- Short title
963
+ description TEXT, -- Detailed description
964
+ source TEXT, -- Detection source (system, external, manual)
965
+ affected_resources TEXT, -- JSON: List of affected resources
966
+ indicators TEXT, -- JSON: Indicators of compromise (IOCs)
967
+ metadata TEXT, -- JSON: Additional context
968
+ created_at TEXT NOT NULL,
969
+ updated_at TEXT NOT NULL,
970
+ detected_at TEXT NOT NULL, -- When threat was detected
971
+ mitigated_at TEXT -- When threat was mitigated
972
+ );
973
+
974
+ CREATE TABLE "session_clients" (
975
+ id TEXT PRIMARY KEY,
976
+ session_id TEXT NOT NULL,
977
+ client_id TEXT NOT NULL,
978
+ first_token_at INTEGER NOT NULL,
979
+ last_token_at INTEGER NOT NULL,
980
+ last_seen_at INTEGER,
981
+
982
+ -- Only keep the client_id foreign key
983
+ FOREIGN KEY (client_id) REFERENCES oauth_clients(client_id) ON DELETE CASCADE,
984
+
985
+ UNIQUE (session_id, client_id)
986
+ );
987
+
988
+ CREATE TABLE "sessions" (
989
+ id TEXT PRIMARY KEY,
990
+ user_id TEXT NOT NULL,
991
+ expires_at INTEGER NOT NULL,
992
+ created_at INTEGER NOT NULL,
993
+ external_provider_id TEXT,
994
+ external_provider_sub TEXT,
995
+ tenant_id TEXT NOT NULL DEFAULT 'default',
996
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
997
+ );
998
+
999
+ CREATE TABLE settings_history (
1000
+ -- Primary key
1001
+ id TEXT PRIMARY KEY,
1002
+
1003
+ -- Multi-tenant support
1004
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1005
+
1006
+ -- Category (oauth, rate_limit, logout, webhook, feature_flags, etc.)
1007
+ category TEXT NOT NULL,
1008
+
1009
+ -- Version number (auto-incremented per tenant+category)
1010
+ version INTEGER NOT NULL,
1011
+
1012
+ -- Full configuration snapshot (JSON)
1013
+ -- This allows complete restoration without dependencies
1014
+ snapshot TEXT NOT NULL,
1015
+
1016
+ -- Change summary (JSON)
1017
+ -- { "added": [...], "removed": [...], "modified": [...] }
1018
+ changes TEXT NOT NULL,
1019
+
1020
+ -- Actor who made the change
1021
+ actor_id TEXT, -- User ID or 'system'
1022
+ actor_type TEXT, -- 'user', 'admin', 'system', 'api'
1023
+
1024
+ -- Change metadata
1025
+ change_reason TEXT, -- Optional reason for the change
1026
+ change_source TEXT, -- 'admin_api', 'settings_ui', 'migration', 'rollback'
1027
+
1028
+ -- Timestamps
1029
+ created_at INTEGER NOT NULL,
1030
+
1031
+ -- Constraints
1032
+ UNIQUE(tenant_id, category, version)
1033
+ );
1034
+
1035
+ CREATE TABLE status_lists (
1036
+ id TEXT PRIMARY KEY,
1037
+ tenant_id TEXT NOT NULL,
1038
+ -- Purpose: 'revocation' | 'suspension'
1039
+ purpose TEXT NOT NULL DEFAULT 'revocation',
1040
+ -- Bitstring of status values (base64url encoded)
1041
+ encoded_list TEXT NOT NULL,
1042
+ -- Current index for new credentials
1043
+ current_index INTEGER DEFAULT 0,
1044
+ -- Total capacity
1045
+ capacity INTEGER DEFAULT 131072,
1046
+ created_at TEXT DEFAULT (datetime('now')),
1047
+ updated_at TEXT DEFAULT (datetime('now'))
1048
+ );
1049
+
1050
+ CREATE TABLE subject_identifiers (
1051
+ id TEXT PRIMARY KEY,
1052
+ tenant_id TEXT NOT NULL,
1053
+ -- User this identifier belongs to
1054
+ subject_id TEXT NOT NULL, -- References users(id)
1055
+ -- Identifier details
1056
+ identifier_type TEXT NOT NULL, -- 'email', 'did', 'phone', 'username'
1057
+ identifier_value TEXT NOT NULL, -- 'user@example.com', 'did:key:z6Mk...'
1058
+ -- Flags
1059
+ is_primary INTEGER DEFAULT 0, -- Whether this is the primary identifier
1060
+ -- Verification
1061
+ verified_at INTEGER, -- When the identifier was verified
1062
+ verification_method TEXT, -- 'email_verification', 'did_auth', 'phone_sms'
1063
+ -- Timestamps
1064
+ created_at INTEGER NOT NULL,
1065
+ updated_at INTEGER NOT NULL
1066
+ );
1067
+
1068
+ CREATE TABLE "subject_org_membership" (
1069
+ id TEXT PRIMARY KEY,
1070
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1071
+ subject_id TEXT NOT NULL,
1072
+ org_id TEXT NOT NULL,
1073
+ membership_type TEXT NOT NULL DEFAULT 'member',
1074
+ is_primary INTEGER DEFAULT 0,
1075
+ created_at INTEGER NOT NULL,
1076
+ updated_at INTEGER NOT NULL,
1077
+ FOREIGN KEY (subject_id) REFERENCES users_core(id) ON DELETE CASCADE,
1078
+ FOREIGN KEY (org_id) REFERENCES organizations(id) ON DELETE CASCADE
1079
+ );
1080
+
1081
+ CREATE TABLE suspicious_activities (
1082
+ id TEXT PRIMARY KEY,
1083
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1084
+ type TEXT NOT NULL, -- brute_force, credential_stuffing, anomalous_login, etc.
1085
+ severity TEXT NOT NULL, -- critical, high, medium, low, info
1086
+ user_id TEXT, -- Associated user (nullable for pre-auth events)
1087
+ client_id TEXT, -- Associated OAuth client
1088
+ source_ip TEXT, -- Source IP address
1089
+ user_agent TEXT, -- User agent string
1090
+ description TEXT, -- Human-readable description
1091
+ metadata TEXT, -- JSON: Additional context data
1092
+ created_at TEXT NOT NULL, -- When detected
1093
+ resolved_at TEXT -- When resolved/dismissed
1094
+ );
1095
+
1096
+ CREATE TABLE token_claim_rules (
1097
+ -- Primary key
1098
+ id TEXT PRIMARY KEY,
1099
+
1100
+ -- Multi-tenant support
1101
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1102
+
1103
+ -- Rule identification
1104
+ name TEXT NOT NULL,
1105
+ description TEXT,
1106
+
1107
+ -- Target token type
1108
+ token_type TEXT NOT NULL DEFAULT 'access', -- 'access' | 'id' | 'both'
1109
+
1110
+ -- Conditions (JSON format, same structure as role_assignment_rules)
1111
+ -- Example: {"type": "and", "conditions": [
1112
+ -- {"field": "has_role", "operator": "contains", "value": "premium_user"},
1113
+ -- {"field": "org_type", "operator": "eq", "value": "enterprise"}
1114
+ -- ]}
1115
+ conditions_json TEXT NOT NULL,
1116
+
1117
+ -- Actions (JSON format)
1118
+ -- Example: [
1119
+ -- {"type": "add_claim", "claim_name": "tier", "claim_value": "premium"},
1120
+ -- {"type": "add_claim_template", "claim_name": "greeting", "template": "Hello {{user_type}}"},
1121
+ -- {"type": "copy_from_context", "claim_name": "org", "context_field": "org_id"}
1122
+ -- ]
1123
+ actions_json TEXT NOT NULL,
1124
+
1125
+ -- Priority and control
1126
+ priority INTEGER NOT NULL DEFAULT 0, -- Higher = evaluated first (DESC order)
1127
+ stop_processing INTEGER DEFAULT 0, -- 1 = stop evaluating further rules after match
1128
+ is_active INTEGER DEFAULT 1, -- 0 = disabled
1129
+
1130
+ -- Validity period (optional, UNIX seconds)
1131
+ valid_from INTEGER, -- NULL = no start restriction
1132
+ valid_until INTEGER, -- NULL = no end restriction
1133
+
1134
+ -- Audit fields
1135
+ created_by TEXT, -- Admin user ID who created
1136
+ created_at INTEGER NOT NULL,
1137
+ updated_at INTEGER NOT NULL,
1138
+
1139
+ -- Constraints
1140
+ UNIQUE(tenant_id, name)
1141
+ );
1142
+
1143
+ CREATE TABLE trusted_issuers (
1144
+ id TEXT PRIMARY KEY,
1145
+ tenant_id TEXT NOT NULL,
1146
+ issuer_did TEXT NOT NULL,
1147
+ display_name TEXT,
1148
+ -- JSON array of accepted Verifiable Credential Types
1149
+ credential_types TEXT,
1150
+ -- Trust level: 'standard' | 'high' (HAIP-compliant)
1151
+ trust_level TEXT DEFAULT 'standard',
1152
+ -- JWKS URI for issuer public keys
1153
+ jwks_uri TEXT,
1154
+ -- Issuer status: 'active' | 'suspended' | 'revoked'
1155
+ status TEXT DEFAULT 'active',
1156
+ created_at TEXT DEFAULT (datetime('now')),
1157
+ updated_at TEXT DEFAULT (datetime('now')),
1158
+ UNIQUE(tenant_id, issuer_did)
1159
+ );
1160
+
1161
+ CREATE TABLE upstream_providers (
1162
+ id TEXT PRIMARY KEY,
1163
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1164
+ name TEXT NOT NULL, -- Display name: "Google", "GitHub"
1165
+ provider_type TEXT NOT NULL, -- 'oidc' | 'oauth2'
1166
+ enabled INTEGER DEFAULT 1,
1167
+ priority INTEGER DEFAULT 0, -- Display order (lower = higher priority)
1168
+
1169
+ -- OIDC/OAuth2 endpoints
1170
+ issuer TEXT, -- OIDC issuer URL (for discovery)
1171
+ client_id TEXT NOT NULL,
1172
+ client_secret_encrypted TEXT NOT NULL, -- Encrypted with RP_TOKEN_ENCRYPTION_KEY
1173
+ authorization_endpoint TEXT, -- Override for non-standard providers
1174
+ token_endpoint TEXT,
1175
+ userinfo_endpoint TEXT,
1176
+ jwks_uri TEXT,
1177
+ scopes TEXT NOT NULL DEFAULT 'openid email profile', -- Space-separated
1178
+
1179
+ -- Configuration
1180
+ attribute_mapping TEXT DEFAULT '{}', -- JSON: {"sub": "sub", "email": "email"}
1181
+ auto_link_email INTEGER DEFAULT 1, -- Enable email-based identity stitching
1182
+ jit_provisioning INTEGER DEFAULT 1, -- Create user on first login
1183
+ require_email_verified INTEGER DEFAULT 1, -- Only link if email is verified
1184
+
1185
+ -- Provider-specific settings
1186
+ provider_quirks TEXT DEFAULT '{}', -- JSON for provider-specific handling
1187
+
1188
+ -- UI customization
1189
+ icon_url TEXT, -- Provider icon for login button
1190
+ button_color TEXT, -- Brand color for login button (hex)
1191
+ button_text TEXT, -- Custom button text (optional)
1192
+
1193
+ -- Metadata
1194
+ created_at INTEGER NOT NULL,
1195
+ updated_at INTEGER NOT NULL
1196
+ , slug TEXT, token_endpoint_auth_method TEXT DEFAULT 'client_secret_post', always_fetch_userinfo INTEGER DEFAULT 0, use_request_object INTEGER DEFAULT 0, request_object_signing_alg TEXT, private_key_jwk_encrypted TEXT, public_key_jwk TEXT);
1197
+
1198
+ CREATE TABLE "user_custom_fields" (
1199
+ user_id TEXT NOT NULL,
1200
+ field_name TEXT NOT NULL,
1201
+ field_value TEXT,
1202
+ field_type TEXT,
1203
+ searchable INTEGER DEFAULT 1,
1204
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1205
+ PRIMARY KEY (user_id, field_name),
1206
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
1207
+ );
1208
+
1209
+ CREATE TABLE "user_roles" (
1210
+ user_id TEXT NOT NULL,
1211
+ role_id TEXT NOT NULL,
1212
+ created_at INTEGER NOT NULL,
1213
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1214
+ PRIMARY KEY (user_id, role_id),
1215
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE,
1216
+ FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
1217
+ );
1218
+
1219
+ CREATE TABLE "user_token_families" (
1220
+ jti TEXT PRIMARY KEY,
1221
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1222
+ user_id TEXT NOT NULL,
1223
+ client_id TEXT NOT NULL,
1224
+ generation INTEGER NOT NULL,
1225
+ expires_at INTEGER NOT NULL,
1226
+ is_revoked INTEGER DEFAULT 0,
1227
+ FOREIGN KEY (user_id) REFERENCES users_core(id) ON DELETE CASCADE
1228
+ );
1229
+
1230
+ CREATE TABLE user_verified_attributes (
1231
+ id TEXT PRIMARY KEY,
1232
+ tenant_id TEXT NOT NULL,
1233
+ user_id TEXT NOT NULL,
1234
+ -- Attribute name: 'age_over_18', 'country', 'organization', etc.
1235
+ attribute_name TEXT NOT NULL,
1236
+ -- Attribute value: 'true', 'JP', 'Acme Corp', etc.
1237
+ attribute_value TEXT NOT NULL,
1238
+ -- Source type: 'vc' | 'saml' | 'oidc' | 'manual'
1239
+ source_type TEXT NOT NULL DEFAULT 'vc',
1240
+ -- Issuer DID (for VC-sourced attributes)
1241
+ issuer_did TEXT,
1242
+ -- Reference to verification record
1243
+ verification_id TEXT REFERENCES attribute_verifications(id),
1244
+ verified_at TEXT DEFAULT (datetime('now')),
1245
+ expires_at TEXT,
1246
+ -- Each user can have only one value per attribute
1247
+ UNIQUE(tenant_id, user_id, attribute_name)
1248
+ );
1249
+
1250
+ CREATE TABLE users (
1251
+ id TEXT PRIMARY KEY,
1252
+ email TEXT UNIQUE NOT NULL,
1253
+ email_verified INTEGER DEFAULT 0,
1254
+ name TEXT,
1255
+ given_name TEXT,
1256
+ family_name TEXT,
1257
+ middle_name TEXT,
1258
+ nickname TEXT,
1259
+ preferred_username TEXT,
1260
+ profile TEXT,
1261
+ picture TEXT,
1262
+ website TEXT,
1263
+ gender TEXT,
1264
+ birthdate TEXT,
1265
+ zoneinfo TEXT,
1266
+ locale TEXT,
1267
+ phone_number TEXT,
1268
+ phone_number_verified INTEGER DEFAULT 0,
1269
+ address_json TEXT,
1270
+ custom_attributes_json TEXT,
1271
+ parent_user_id TEXT REFERENCES users(id),
1272
+ identity_provider_id TEXT REFERENCES identity_providers(id),
1273
+ -- Password authentication fields (optional, disabled by default)
1274
+ password_hash TEXT,
1275
+ password_changed_at INTEGER,
1276
+ failed_login_attempts INTEGER DEFAULT 0,
1277
+ locked_until INTEGER,
1278
+ -- Timestamps
1279
+ created_at INTEGER NOT NULL,
1280
+ updated_at INTEGER NOT NULL,
1281
+ last_login_at INTEGER
1282
+ , tenant_id TEXT NOT NULL DEFAULT 'default', user_type TEXT NOT NULL DEFAULT 'end_user', status TEXT DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'locked')), suspended_at INTEGER, suspended_until INTEGER, locked_at INTEGER);
1283
+
1284
+ CREATE TABLE users_core (
1285
+ -- Primary key (UUID, same as users_pii.id)
1286
+ id TEXT PRIMARY KEY,
1287
+
1288
+ -- Multi-tenant support
1289
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1290
+
1291
+ -- Verification status (not PII - just flags)
1292
+ email_verified INTEGER DEFAULT 0,
1293
+ phone_number_verified INTEGER DEFAULT 0,
1294
+
1295
+ -- Blind index for domain-based role assignment (Phase 8)
1296
+ -- Stored as hash, cannot be reversed to original domain
1297
+ email_domain_hash TEXT,
1298
+
1299
+ -- Authentication
1300
+ password_hash TEXT,
1301
+
1302
+ -- Soft delete (1 = active, 0 = deleted)
1303
+ is_active INTEGER DEFAULT 1,
1304
+
1305
+ -- User type: end_user | admin | m2m
1306
+ user_type TEXT NOT NULL DEFAULT 'end_user',
1307
+
1308
+ -- PII partition info
1309
+ -- Which database contains this user's PII (e.g., 'default', 'eu', 'tenant-acme')
1310
+ pii_partition TEXT NOT NULL DEFAULT 'default',
1311
+
1312
+ -- PII write status
1313
+ -- none: No PII (M2M clients)
1314
+ -- pending: Core created, PII write in progress
1315
+ -- active: Both Core and PII created successfully
1316
+ -- failed: PII write failed (requires retry via Admin UI)
1317
+ -- deleted: PII deleted (GDPR), tombstone created
1318
+ pii_status TEXT NOT NULL DEFAULT 'pending',
1319
+
1320
+ -- Timestamps
1321
+ created_at INTEGER NOT NULL,
1322
+ updated_at INTEGER NOT NULL,
1323
+ last_login_at INTEGER
1324
+ , email_domain_hash_version INTEGER DEFAULT 1, external_id TEXT DEFAULT NULL, status TEXT DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'locked')), suspended_at INTEGER, suspended_until INTEGER, locked_at INTEGER, locked_until INTEGER);
1325
+
1326
+ CREATE TABLE verified_attributes (
1327
+ id TEXT PRIMARY KEY,
1328
+ tenant_id TEXT NOT NULL,
1329
+ -- Subject this attribute belongs to
1330
+ subject_id TEXT NOT NULL, -- References users(id)
1331
+ -- Attribute details
1332
+ attribute_name TEXT NOT NULL, -- 'age_over_18', 'medical_license', 'subscription_tier'
1333
+ attribute_value TEXT, -- 'true', 'MD12345', 'premium'
1334
+ -- Source information (for auditing and trust evaluation)
1335
+ source TEXT NOT NULL DEFAULT 'manual', -- 'manual', 'vc', 'jwt_sd', 'kyc_provider'
1336
+ issuer TEXT, -- Issuer DID or URL (Phase 4+)
1337
+ credential_id TEXT, -- VC ID for traceability (Phase 4+)
1338
+ -- Validity
1339
+ verified_at INTEGER NOT NULL, -- When the attribute was verified/extracted
1340
+ expires_at INTEGER, -- When the attribute expires (from VC exp)
1341
+ revoked_at INTEGER, -- When the attribute was revoked
1342
+ -- Timestamps
1343
+ created_at INTEGER NOT NULL,
1344
+ updated_at INTEGER NOT NULL
1345
+ );
1346
+
1347
+ CREATE TABLE vp_requests (
1348
+ id TEXT PRIMARY KEY,
1349
+ tenant_id TEXT NOT NULL,
1350
+ client_id TEXT NOT NULL,
1351
+ -- Nonce for replay protection (single-use, enforced by DO)
1352
+ nonce TEXT NOT NULL,
1353
+ state TEXT,
1354
+ -- Reference to presentation definition (optional, can use inline)
1355
+ presentation_definition_id TEXT REFERENCES presentation_definitions(id),
1356
+ response_uri TEXT NOT NULL,
1357
+ -- Response mode: 'direct_post' | 'direct_post.jwt' | 'fragment' | 'query'
1358
+ response_mode TEXT DEFAULT 'direct_post',
1359
+ -- Request status: 'pending' | 'submitted' | 'verified' | 'failed' | 'expired'
1360
+ status TEXT DEFAULT 'pending',
1361
+ -- Error information if failed
1362
+ error_code TEXT,
1363
+ error_description TEXT,
1364
+ created_at TEXT DEFAULT (datetime('now')),
1365
+ expires_at TEXT NOT NULL,
1366
+ verified_at TEXT
1367
+ );
1368
+
1369
+ CREATE TABLE webhook_configs (
1370
+ id TEXT PRIMARY KEY,
1371
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1372
+ client_id TEXT,
1373
+ scope TEXT NOT NULL DEFAULT 'tenant',
1374
+ name TEXT NOT NULL,
1375
+ url TEXT NOT NULL,
1376
+ events TEXT NOT NULL,
1377
+ secret_encrypted TEXT,
1378
+ headers TEXT,
1379
+ retry_policy TEXT NOT NULL,
1380
+ timeout_ms INTEGER NOT NULL DEFAULT 10000,
1381
+ active INTEGER NOT NULL DEFAULT 1,
1382
+ created_at TEXT NOT NULL,
1383
+ updated_at TEXT NOT NULL,
1384
+ last_success_at TEXT,
1385
+ last_failure_at TEXT
1386
+ );
1387
+
1388
+ CREATE TABLE webhook_delivery_logs (
1389
+ id TEXT PRIMARY KEY,
1390
+ webhook_id TEXT NOT NULL,
1391
+ event_id TEXT NOT NULL,
1392
+ event_type TEXT NOT NULL,
1393
+ tenant_id TEXT NOT NULL,
1394
+ attempt INTEGER NOT NULL DEFAULT 1,
1395
+ status TEXT NOT NULL,
1396
+ status_code INTEGER,
1397
+ error_message TEXT,
1398
+ duration_ms INTEGER,
1399
+ created_at TEXT NOT NULL,
1400
+ FOREIGN KEY (webhook_id) REFERENCES webhook_configs(id) ON DELETE CASCADE
1401
+ );
1402
+
1403
+ CREATE TABLE websocket_subscriptions (
1404
+ id TEXT PRIMARY KEY,
1405
+ tenant_id TEXT NOT NULL DEFAULT 'default',
1406
+ connection_id TEXT NOT NULL,
1407
+ subject_id TEXT NOT NULL,
1408
+ watched_subjects TEXT DEFAULT '[]', -- JSON array of subject IDs to watch
1409
+ watched_resources TEXT DEFAULT '[]', -- JSON array of resource patterns
1410
+ watched_relations TEXT DEFAULT '[]', -- JSON array of relation types
1411
+ connected_at INTEGER NOT NULL,
1412
+ is_active INTEGER DEFAULT 1
1413
+ );
1414
+
1415
+ -- =============================================================================
1416
+ -- Indexes
1417
+ -- =============================================================================
1418
+
1419
+ CREATE INDEX idx_access_review_items_decision ON access_review_items(review_id, decision);
1420
+
1421
+ CREATE INDEX idx_access_review_items_review ON access_review_items(review_id);
1422
+
1423
+ CREATE INDEX idx_access_review_items_user ON access_review_items(tenant_id, user_id);
1424
+
1425
+ CREATE INDEX idx_access_reviews_created ON access_reviews(tenant_id, created_at);
1426
+
1427
+ CREATE INDEX idx_access_reviews_due ON access_reviews(tenant_id, due_date);
1428
+
1429
+ CREATE INDEX idx_access_reviews_reviewer ON access_reviews(tenant_id, reviewer_id);
1430
+
1431
+ CREATE INDEX idx_access_reviews_status ON access_reviews(tenant_id, status);
1432
+
1433
+ CREATE INDEX idx_access_reviews_tenant ON access_reviews(tenant_id);
1434
+
1435
+ CREATE INDEX idx_admin_jobs_cleanup ON admin_jobs(
1436
+ status,
1437
+ completed_at
1438
+ );
1439
+
1440
+ CREATE INDEX idx_admin_jobs_status ON admin_jobs(
1441
+ tenant_id,
1442
+ status,
1443
+ created_at DESC
1444
+ );
1445
+
1446
+ CREATE INDEX idx_admin_jobs_tenant ON admin_jobs(
1447
+ tenant_id,
1448
+ created_at DESC
1449
+ );
1450
+
1451
+ CREATE INDEX idx_admin_jobs_type ON admin_jobs(
1452
+ tenant_id,
1453
+ job_type,
1454
+ created_at DESC
1455
+ );
1456
+
1457
+ CREATE INDEX idx_ai_grants_active ON ai_grants(is_active) WHERE is_active = 1;
1458
+
1459
+ CREATE INDEX idx_ai_grants_client ON ai_grants(client_id);
1460
+
1461
+ CREATE INDEX idx_ai_grants_expires ON ai_grants(expires_at) WHERE expires_at IS NOT NULL;
1462
+
1463
+ CREATE INDEX idx_ai_grants_principal ON ai_grants(ai_principal);
1464
+
1465
+ CREATE INDEX idx_ai_grants_tenant ON ai_grants(tenant_id);
1466
+
1467
+ CREATE INDEX idx_attribute_verifications_result ON attribute_verifications(verification_result);
1468
+
1469
+ CREATE INDEX idx_attribute_verifications_user ON attribute_verifications(tenant_id, user_id);
1470
+
1471
+ CREATE INDEX idx_audit_log_action ON audit_log(action);
1472
+
1473
+ CREATE INDEX idx_audit_log_created_at ON audit_log(created_at);
1474
+
1475
+ CREATE INDEX idx_audit_log_resource ON audit_log(resource_type, resource_id);
1476
+
1477
+ CREATE INDEX idx_audit_log_severity ON audit_log(severity);
1478
+
1479
+ CREATE INDEX idx_audit_log_tenant_id ON audit_log(tenant_id);
1480
+
1481
+ CREATE INDEX idx_audit_log_user_id ON audit_log(user_id);
1482
+
1483
+ CREATE INDEX idx_check_api_keys_client
1484
+ ON check_api_keys(client_id);
1485
+
1486
+ CREATE UNIQUE INDEX idx_check_api_keys_hash
1487
+ ON check_api_keys(key_hash);
1488
+
1489
+ CREATE INDEX idx_check_api_keys_prefix
1490
+ ON check_api_keys(key_prefix);
1491
+
1492
+ CREATE INDEX idx_check_api_keys_tenant_active
1493
+ ON check_api_keys(tenant_id, is_active);
1494
+
1495
+ CREATE INDEX idx_ciba_client ON ciba_requests(client_id);
1496
+
1497
+ CREATE INDEX idx_ciba_status ON ciba_requests(status);
1498
+
1499
+ CREATE INDEX idx_ciba_user ON ciba_requests(user_id);
1500
+
1501
+ CREATE INDEX idx_clients_claims_setting ON oauth_clients(allow_claims_without_scope);
1502
+
1503
+ CREATE INDEX idx_clients_created_at ON oauth_clients(created_at);
1504
+
1505
+ CREATE INDEX idx_clients_software_id_tenant ON oauth_clients(software_id, tenant_id);
1506
+
1507
+ CREATE INDEX idx_clients_trusted ON oauth_clients(is_trusted);
1508
+
1509
+ CREATE INDEX idx_closure_ancestor_lookup
1510
+ ON relationship_closure(tenant_id, ancestor_type, ancestor_id, relation);
1511
+
1512
+ CREATE INDEX idx_closure_depth
1513
+ ON relationship_closure(tenant_id, depth);
1514
+
1515
+ CREATE INDEX idx_closure_descendant_lookup
1516
+ ON relationship_closure(tenant_id, descendant_type, descendant_id, relation);
1517
+
1518
+ CREATE UNIQUE INDEX idx_closure_unique
1519
+ ON relationship_closure(tenant_id, ancestor_type, ancestor_id, descendant_type, descendant_id, relation);
1520
+
1521
+ CREATE INDEX idx_compliance_reports_created ON compliance_reports(tenant_id, created_at);
1522
+
1523
+ CREATE INDEX idx_compliance_reports_requested ON compliance_reports(tenant_id, requested_by);
1524
+
1525
+ CREATE INDEX idx_compliance_reports_status ON compliance_reports(tenant_id, status);
1526
+
1527
+ CREATE INDEX idx_compliance_reports_tenant ON compliance_reports(tenant_id);
1528
+
1529
+ CREATE INDEX idx_compliance_reports_type ON compliance_reports(tenant_id, type);
1530
+
1531
+ CREATE INDEX idx_consent_history_action
1532
+ ON consent_history(action, created_at);
1533
+
1534
+ CREATE INDEX idx_consent_history_client
1535
+ ON consent_history(client_id, created_at);
1536
+
1537
+ CREATE INDEX idx_consent_history_tenant
1538
+ ON consent_history(tenant_id, created_at);
1539
+
1540
+ CREATE INDEX idx_consent_history_user
1541
+ ON consent_history(user_id, created_at);
1542
+
1543
+ CREATE INDEX idx_consent_policy_versions_effective
1544
+ ON consent_policy_versions(effective_at);
1545
+
1546
+ CREATE INDEX idx_consent_policy_versions_tenant
1547
+ ON consent_policy_versions(tenant_id, policy_type);
1548
+
1549
+ CREATE INDEX idx_consents_client ON oauth_client_consents(client_id);
1550
+
1551
+ CREATE INDEX idx_consents_expires_at_active
1552
+ ON oauth_client_consents(expires_at) WHERE expires_at IS NOT NULL;
1553
+
1554
+ CREATE INDEX idx_consents_user ON oauth_client_consents(user_id);
1555
+
1556
+ CREATE INDEX idx_credential_configurations_tenant ON credential_configurations(tenant_id);
1557
+
1558
+ CREATE INDEX idx_credential_offers_code ON credential_offers(pre_authorized_code);
1559
+
1560
+ CREATE INDEX idx_credential_offers_status ON credential_offers(tenant_id, status);
1561
+
1562
+ CREATE INDEX idx_data_export_expires
1563
+ ON data_export_requests(expires_at) WHERE expires_at IS NOT NULL;
1564
+
1565
+ CREATE INDEX idx_data_export_status
1566
+ ON data_export_requests(status, requested_at);
1567
+
1568
+ CREATE INDEX idx_data_export_user
1569
+ ON data_export_requests(user_id, status);
1570
+
1571
+ CREATE INDEX idx_device_codes_client_id ON device_codes(client_id);
1572
+
1573
+ CREATE INDEX idx_device_codes_expires_at ON device_codes(expires_at);
1574
+
1575
+ CREATE INDEX idx_device_codes_status ON device_codes(status);
1576
+
1577
+ CREATE INDEX idx_device_codes_user_code ON device_codes(user_code);
1578
+
1579
+ CREATE INDEX idx_did_document_cache_expires ON did_document_cache(expires_at);
1580
+
1581
+ CREATE INDEX idx_external_idp_auth_states_consumed_at
1582
+ ON external_idp_auth_states(consumed_at);
1583
+
1584
+ CREATE INDEX idx_external_idp_auth_states_expires_at
1585
+ ON external_idp_auth_states(expires_at);
1586
+
1587
+ CREATE INDEX idx_external_idp_auth_states_state
1588
+ ON external_idp_auth_states(state);
1589
+
1590
+ CREATE INDEX idx_flows_builtin ON flows(is_builtin);
1591
+
1592
+ CREATE INDEX idx_flows_client ON flows(tenant_id, client_id);
1593
+
1594
+ CREATE INDEX idx_flows_lookup ON flows(tenant_id, client_id, profile_id, is_active);
1595
+
1596
+ CREATE INDEX idx_flows_profile ON flows(tenant_id, profile_id);
1597
+
1598
+ CREATE INDEX idx_flows_tenant ON flows(tenant_id, is_active);
1599
+
1600
+ CREATE INDEX idx_idempotency_keys_expires
1601
+ ON idempotency_keys(expires_at);
1602
+
1603
+ CREATE INDEX idx_idempotency_keys_lookup
1604
+ ON idempotency_keys(tenant_id, actor_id, idempotency_key);
1605
+
1606
+ CREATE INDEX idx_identity_providers_type ON identity_providers(provider_type);
1607
+
1608
+ CREATE INDEX idx_issued_credentials_status ON issued_credentials(status);
1609
+
1610
+ CREATE INDEX idx_issued_credentials_type ON issued_credentials(credential_type);
1611
+
1612
+ CREATE INDEX idx_issued_credentials_user ON issued_credentials(tenant_id, user_id);
1613
+
1614
+ CREATE INDEX idx_linked_identities_provider ON linked_identities(provider_id);
1615
+
1616
+ CREATE INDEX idx_linked_identities_user ON linked_identities(user_id);
1617
+
1618
+ CREATE INDEX idx_membership_org ON subject_org_membership(org_id);
1619
+
1620
+ CREATE INDEX idx_membership_subject ON subject_org_membership(subject_id);
1621
+
1622
+ CREATE INDEX idx_oauth_clients_tenant_id ON oauth_clients(tenant_id);
1623
+
1624
+ CREATE INDEX idx_odm_lookup ON org_domain_mappings(
1625
+ tenant_id,
1626
+ domain_hash,
1627
+ is_active,
1628
+ verified DESC,
1629
+ priority DESC
1630
+ );
1631
+
1632
+ CREATE INDEX idx_odm_org ON org_domain_mappings(org_id);
1633
+
1634
+ CREATE INDEX idx_odm_verification_status ON org_domain_mappings(
1635
+ verification_status,
1636
+ verification_expires_at
1637
+ );
1638
+
1639
+ CREATE INDEX idx_odm_version ON org_domain_mappings(domain_hash_version);
1640
+
1641
+ CREATE INDEX idx_operational_logs_actor
1642
+ ON operational_logs(actor_id);
1643
+
1644
+ CREATE INDEX idx_operational_logs_expires
1645
+ ON operational_logs(expires_at);
1646
+
1647
+ CREATE INDEX idx_operational_logs_subject
1648
+ ON operational_logs(subject_type, subject_id);
1649
+
1650
+ CREATE INDEX idx_operational_logs_tenant_created
1651
+ ON operational_logs(tenant_id, created_at DESC);
1652
+
1653
+ CREATE INDEX idx_organizations_is_active ON organizations(is_active);
1654
+
1655
+ CREATE INDEX idx_organizations_org_type ON organizations(org_type);
1656
+
1657
+ CREATE INDEX idx_organizations_parent_org_id ON organizations(parent_org_id);
1658
+
1659
+ CREATE INDEX idx_organizations_tenant_id ON organizations(tenant_id);
1660
+
1661
+ CREATE UNIQUE INDEX idx_organizations_tenant_name ON organizations(tenant_id, name);
1662
+
1663
+ CREATE INDEX idx_passkeys_tenant ON passkeys(tenant_id);
1664
+
1665
+ CREATE INDEX idx_passkeys_user ON passkeys(user_id);
1666
+
1667
+ CREATE INDEX idx_password_reset_user ON password_reset_tokens(user_id);
1668
+
1669
+ CREATE INDEX idx_pca_api_key
1670
+ ON permission_check_audit(api_key_id)
1671
+ WHERE api_key_id IS NOT NULL;
1672
+
1673
+ CREATE INDEX idx_pca_checked_at
1674
+ ON permission_check_audit(checked_at);
1675
+
1676
+ CREATE INDEX idx_pca_denied
1677
+ ON permission_check_audit(tenant_id, final_decision)
1678
+ WHERE final_decision = 'deny';
1679
+
1680
+ CREATE INDEX idx_pca_tenant_subject
1681
+ ON permission_check_audit(tenant_id, subject_id);
1682
+
1683
+ CREATE INDEX idx_pcaudit_event_type
1684
+ ON permission_change_audit(tenant_id, event_type);
1685
+
1686
+ CREATE INDEX idx_pcaudit_tenant_subject
1687
+ ON permission_change_audit(tenant_id, subject_id);
1688
+
1689
+ CREATE INDEX idx_pcaudit_timestamp
1690
+ ON permission_change_audit(timestamp);
1691
+
1692
+ CREATE INDEX idx_policy_rules_priority ON policy_rules(tenant_id, priority DESC);
1693
+
1694
+ CREATE INDEX idx_policy_rules_tenant ON policy_rules(tenant_id, enabled);
1695
+
1696
+ CREATE INDEX idx_policy_simulations_tenant ON policy_simulations(tenant_id, simulated_at DESC);
1697
+
1698
+ CREATE INDEX idx_presentation_definitions_tenant ON presentation_definitions(tenant_id);
1699
+
1700
+ CREATE INDEX idx_rar_evaluation ON role_assignment_rules(
1701
+ tenant_id,
1702
+ is_active,
1703
+ priority DESC
1704
+ );
1705
+
1706
+ CREATE INDEX idx_rar_role ON role_assignment_rules(role_id);
1707
+
1708
+ CREATE INDEX idx_relation_defs_active
1709
+ ON relation_definitions(tenant_id, is_active);
1710
+
1711
+ CREATE INDEX idx_relation_defs_lookup
1712
+ ON relation_definitions(tenant_id, object_type, relation_name);
1713
+
1714
+ CREATE INDEX idx_relation_defs_tenant_object
1715
+ ON relation_definitions(tenant_id, object_type);
1716
+
1717
+ CREATE UNIQUE INDEX idx_relation_defs_unique
1718
+ ON relation_definitions(tenant_id, object_type, relation_name);
1719
+
1720
+ CREATE INDEX idx_relationships_evidence_type
1721
+ ON relationships(tenant_id, evidence_type);
1722
+
1723
+ CREATE INDEX idx_relationships_expires_at ON relationships(expires_at);
1724
+
1725
+ CREATE INDEX idx_relationships_from ON relationships(from_type, from_id);
1726
+
1727
+ CREATE INDEX idx_relationships_tenant_id ON relationships(tenant_id);
1728
+
1729
+ CREATE INDEX idx_relationships_to ON relationships(to_type, to_id);
1730
+
1731
+ CREATE INDEX idx_relationships_type ON relationships(relationship_type);
1732
+
1733
+ CREATE UNIQUE INDEX idx_relationships_unique
1734
+ ON relationships(tenant_id, relationship_type, from_type, from_id, to_type, to_id);
1735
+
1736
+ CREATE INDEX idx_role_assignments_role ON role_assignments(role_id);
1737
+
1738
+ CREATE INDEX idx_role_assignments_subject ON role_assignments(subject_id);
1739
+
1740
+ CREATE INDEX idx_roles_hierarchy_level ON roles(hierarchy_level);
1741
+
1742
+ CREATE INDEX idx_roles_name ON roles(name);
1743
+
1744
+ CREATE INDEX idx_roles_parent_role_id ON roles(parent_role_id);
1745
+
1746
+ CREATE INDEX idx_roles_role_type ON roles(role_type);
1747
+
1748
+ CREATE INDEX idx_roles_tenant_id ON roles(tenant_id);
1749
+
1750
+ CREATE INDEX idx_rp_expires ON resource_permissions(expires_at)
1751
+ WHERE expires_at IS NOT NULL;
1752
+
1753
+ CREATE INDEX idx_rp_lookup ON resource_permissions(
1754
+ tenant_id,
1755
+ subject_type,
1756
+ subject_id,
1757
+ resource_type,
1758
+ is_active
1759
+ );
1760
+
1761
+ CREATE INDEX idx_rp_resource ON resource_permissions(
1762
+ tenant_id,
1763
+ resource_type,
1764
+ resource_id,
1765
+ is_active
1766
+ );
1767
+
1768
+ CREATE INDEX idx_rtsc_activated_at
1769
+ ON refresh_token_shard_configs(activated_at);
1770
+
1771
+ CREATE INDEX idx_rtsc_generation
1772
+ ON refresh_token_shard_configs(generation);
1773
+
1774
+ CREATE INDEX idx_rtsc_tenant_client
1775
+ ON refresh_token_shard_configs(tenant_id, client_id);
1776
+
1777
+ CREATE INDEX idx_schema_migrations_applied_at ON schema_migrations(applied_at DESC);
1778
+
1779
+ CREATE INDEX idx_schema_migrations_checksum ON schema_migrations(checksum);
1780
+
1781
+ CREATE INDEX idx_scope_mappings_scope ON scope_mappings(scope);
1782
+
1783
+ CREATE INDEX idx_security_alerts_tenant_created
1784
+ ON security_alerts(tenant_id, created_at DESC);
1785
+
1786
+ CREATE INDEX idx_security_alerts_tenant_severity
1787
+ ON security_alerts(tenant_id, severity);
1788
+
1789
+ CREATE INDEX idx_security_alerts_tenant_status
1790
+ ON security_alerts(tenant_id, status);
1791
+
1792
+ CREATE INDEX idx_security_alerts_tenant_type
1793
+ ON security_alerts(tenant_id, type);
1794
+
1795
+ CREATE INDEX idx_security_alerts_user
1796
+ ON security_alerts(user_id);
1797
+
1798
+ CREATE INDEX idx_security_threats_detected ON security_threats(tenant_id, detected_at);
1799
+
1800
+ CREATE INDEX idx_security_threats_severity ON security_threats(tenant_id, severity);
1801
+
1802
+ CREATE INDEX idx_security_threats_status ON security_threats(tenant_id, status);
1803
+
1804
+ CREATE INDEX idx_security_threats_tenant ON security_threats(tenant_id);
1805
+
1806
+ CREATE INDEX idx_security_threats_type ON security_threats(tenant_id, type);
1807
+
1808
+ CREATE INDEX idx_session_clients_client_id ON session_clients(client_id);
1809
+
1810
+ CREATE INDEX idx_session_clients_last_seen_at ON session_clients(last_seen_at);
1811
+
1812
+ CREATE INDEX idx_session_clients_session_id ON session_clients(session_id);
1813
+
1814
+ CREATE INDEX idx_sessions_expires ON sessions(expires_at);
1815
+
1816
+ CREATE INDEX idx_sessions_tenant ON sessions(tenant_id);
1817
+
1818
+ CREATE INDEX idx_sessions_user ON sessions(user_id);
1819
+
1820
+ CREATE INDEX idx_settings_history_actor ON settings_history(
1821
+ actor_id,
1822
+ created_at DESC
1823
+ );
1824
+
1825
+ CREATE INDEX idx_settings_history_category ON settings_history(
1826
+ tenant_id,
1827
+ category,
1828
+ version DESC
1829
+ );
1830
+
1831
+ CREATE INDEX idx_settings_history_cleanup ON settings_history(
1832
+ tenant_id,
1833
+ category,
1834
+ created_at
1835
+ );
1836
+
1837
+ CREATE INDEX idx_status_lists_tenant ON status_lists(tenant_id);
1838
+
1839
+ CREATE INDEX idx_subject_identifiers_lookup
1840
+ ON subject_identifiers(tenant_id, identifier_type, identifier_value);
1841
+
1842
+ CREATE INDEX idx_subject_identifiers_primary
1843
+ ON subject_identifiers(tenant_id, subject_id, is_primary);
1844
+
1845
+ CREATE INDEX idx_subject_identifiers_tenant_subject
1846
+ ON subject_identifiers(tenant_id, subject_id);
1847
+
1848
+ CREATE UNIQUE INDEX idx_subject_identifiers_unique
1849
+ ON subject_identifiers(tenant_id, identifier_type, identifier_value);
1850
+
1851
+ CREATE INDEX idx_suspicious_activities_created ON suspicious_activities(tenant_id, created_at);
1852
+
1853
+ CREATE INDEX idx_suspicious_activities_severity ON suspicious_activities(tenant_id, severity);
1854
+
1855
+ CREATE INDEX idx_suspicious_activities_tenant ON suspicious_activities(tenant_id);
1856
+
1857
+ CREATE INDEX idx_suspicious_activities_type ON suspicious_activities(tenant_id, type);
1858
+
1859
+ CREATE INDEX idx_suspicious_activities_user ON suspicious_activities(tenant_id, user_id);
1860
+
1861
+ CREATE INDEX idx_tcr_evaluation ON token_claim_rules(
1862
+ tenant_id,
1863
+ token_type,
1864
+ is_active,
1865
+ priority DESC,
1866
+ created_at ASC
1867
+ );
1868
+
1869
+ CREATE INDEX idx_token_families_client ON user_token_families(client_id);
1870
+
1871
+ CREATE INDEX idx_token_families_user ON user_token_families(user_id);
1872
+
1873
+ CREATE INDEX idx_trusted_issuers_did ON trusted_issuers(issuer_did);
1874
+
1875
+ CREATE INDEX idx_trusted_issuers_tenant ON trusted_issuers(tenant_id);
1876
+
1877
+ CREATE INDEX idx_upstream_providers_enabled
1878
+ ON upstream_providers(tenant_id, enabled);
1879
+
1880
+ CREATE INDEX idx_upstream_providers_tenant_id
1881
+ ON upstream_providers(tenant_id);
1882
+
1883
+ CREATE UNIQUE INDEX idx_upstream_providers_tenant_name
1884
+ ON upstream_providers(tenant_id, name);
1885
+
1886
+ CREATE UNIQUE INDEX idx_upstream_providers_tenant_slug
1887
+ ON upstream_providers(tenant_id, slug)
1888
+ WHERE slug IS NOT NULL;
1889
+
1890
+ CREATE INDEX idx_user_verified_attributes_name ON user_verified_attributes(tenant_id, attribute_name);
1891
+
1892
+ CREATE INDEX idx_user_verified_attributes_user ON user_verified_attributes(tenant_id, user_id);
1893
+
1894
+ CREATE INDEX idx_users_core_active ON users_core(is_active);
1895
+
1896
+ CREATE INDEX idx_users_core_email_domain ON users_core(email_domain_hash);
1897
+
1898
+ CREATE INDEX idx_users_core_hash_version ON users_core(email_domain_hash_version);
1899
+
1900
+ CREATE INDEX idx_users_core_partition ON users_core(pii_partition);
1901
+
1902
+ CREATE INDEX idx_users_core_pii_status ON users_core(pii_status);
1903
+
1904
+ CREATE INDEX idx_users_core_status ON users_core(tenant_id, status);
1905
+
1906
+ CREATE INDEX idx_users_core_tenant ON users_core(tenant_id);
1907
+
1908
+ CREATE INDEX idx_users_core_tenant_external_id ON users_core(tenant_id, external_id);
1909
+
1910
+ CREATE INDEX idx_users_core_type ON users_core(tenant_id, user_type);
1911
+
1912
+ CREATE INDEX idx_users_created_at ON users(created_at);
1913
+
1914
+ CREATE UNIQUE INDEX idx_users_tenant_email ON users(tenant_id, email);
1915
+
1916
+ CREATE INDEX idx_users_tenant_id ON users(tenant_id);
1917
+
1918
+ CREATE INDEX idx_users_tenant_status ON users(tenant_id, status);
1919
+
1920
+ CREATE INDEX idx_users_user_type ON users(user_type);
1921
+
1922
+ CREATE INDEX idx_verified_attributes_expires
1923
+ ON verified_attributes(tenant_id, expires_at);
1924
+
1925
+ CREATE INDEX idx_verified_attributes_lookup
1926
+ ON verified_attributes(tenant_id, subject_id, attribute_name);
1927
+
1928
+ CREATE INDEX idx_verified_attributes_source
1929
+ ON verified_attributes(tenant_id, source);
1930
+
1931
+ CREATE INDEX idx_verified_attributes_tenant_subject
1932
+ ON verified_attributes(tenant_id, subject_id);
1933
+
1934
+ CREATE INDEX idx_verified_attributes_unique_check
1935
+ ON verified_attributes(tenant_id, subject_id, attribute_name, source);
1936
+
1937
+ CREATE INDEX idx_vp_requests_nonce ON vp_requests(nonce);
1938
+
1939
+ CREATE INDEX idx_vp_requests_tenant_status ON vp_requests(tenant_id, status);
1940
+
1941
+ CREATE INDEX idx_webhook_configs_active ON webhook_configs(tenant_id, active) WHERE active = 1;
1942
+
1943
+ CREATE INDEX idx_webhook_configs_client ON webhook_configs(tenant_id, client_id);
1944
+
1945
+ CREATE INDEX idx_webhook_configs_scope ON webhook_configs(tenant_id, scope);
1946
+
1947
+ CREATE INDEX idx_webhook_configs_tenant ON webhook_configs(tenant_id);
1948
+
1949
+ CREATE INDEX idx_webhook_delivery_logs_created ON webhook_delivery_logs(created_at);
1950
+
1951
+ CREATE INDEX idx_webhook_delivery_logs_event ON webhook_delivery_logs(event_id);
1952
+
1953
+ CREATE INDEX idx_webhook_delivery_logs_tenant ON webhook_delivery_logs(tenant_id);
1954
+
1955
+ CREATE INDEX idx_webhook_delivery_logs_webhook ON webhook_delivery_logs(webhook_id);
1956
+
1957
+ CREATE INDEX idx_ws_subs_active
1958
+ ON websocket_subscriptions(is_active)
1959
+ WHERE is_active = 1;
1960
+
1961
+ CREATE INDEX idx_ws_subs_connection
1962
+ ON websocket_subscriptions(connection_id);
1963
+
1964
+ CREATE INDEX idx_ws_subs_subject
1965
+ ON websocket_subscriptions(subject_id, is_active);
1966
+