@marinoscar/kvox-eval 0.0.0-bootstrap → 0.1.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/ask/index.d.ts +6 -0
- package/dist/ask/index.js +28 -0
- package/dist/ask/question-schema.d.ts +121 -0
- package/dist/ask/question-schema.js +167 -0
- package/dist/ask/report.d.ts +88 -0
- package/dist/ask/report.js +170 -0
- package/dist/ask/scorer.d.ts +104 -0
- package/dist/ask/scorer.js +164 -0
- package/dist/ask-run/ask-command.d.ts +62 -0
- package/dist/ask-run/ask-command.js +254 -0
- package/dist/ask-run/host/app.d.ts +65 -0
- package/dist/ask-run/host/app.js +146 -0
- package/dist/ask-run/host/db.d.ts +38 -0
- package/dist/ask-run/host/db.js +126 -0
- package/dist/ask-run/host/ports.d.ts +35 -0
- package/dist/ask-run/host/ports.js +113 -0
- package/dist/ask-run/index.d.ts +13 -0
- package/dist/ask-run/index.js +35 -0
- package/dist/ask-run/replay.d.ts +128 -0
- package/dist/ask-run/replay.js +212 -0
- package/dist/ask-run/run-ask-eval.d.ts +125 -0
- package/dist/ask-run/run-ask-eval.js +193 -0
- package/dist/ask-run/seed-golden-graph.d.ts +64 -0
- package/dist/ask-run/seed-golden-graph.js +495 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/kg-command.d.ts +66 -0
- package/dist/cli/kg-command.js +238 -0
- package/dist/cli/main.d.ts +18 -0
- package/dist/cli/main.js +69 -0
- package/dist/fixtures/domains.d.ts +16 -0
- package/dist/fixtures/domains.js +33 -0
- package/dist/fixtures/fixture-schema.d.ts +323 -0
- package/dist/fixtures/fixture-schema.js +199 -0
- package/dist/fixtures/index.d.ts +16 -0
- package/dist/fixtures/index.js +40 -0
- package/dist/fixtures/load.d.ts +73 -0
- package/dist/fixtures/load.js +137 -0
- package/dist/fixtures/questions.d.ts +15 -0
- package/dist/fixtures/questions.js +26 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +36 -0
- package/dist/kg/gold-runner.d.ts +13 -0
- package/dist/kg/gold-runner.js +57 -0
- package/dist/kg/index.d.ts +17 -0
- package/dist/kg/index.js +65 -0
- package/dist/kg/internals.d.ts +7 -0
- package/dist/kg/internals.js +17 -0
- package/dist/kg/prediction-schema.d.ts +245 -0
- package/dist/kg/prediction-schema.js +102 -0
- package/dist/kg/predictions.d.ts +11 -0
- package/dist/kg/predictions.js +49 -0
- package/dist/kg/report.d.ts +84 -0
- package/dist/kg/report.js +125 -0
- package/dist/kg/run-kg-eval.d.ts +77 -0
- package/dist/kg/run-kg-eval.js +137 -0
- package/dist/kg/runner.d.ts +72 -0
- package/dist/kg/runner.js +51 -0
- package/dist/kg/runners/extract-resolve-runner.d.ts +40 -0
- package/dist/kg/runners/extract-resolve-runner.js +199 -0
- package/dist/kg/runners/extract-runner.d.ts +65 -0
- package/dist/kg/runners/extract-runner.js +243 -0
- package/dist/kg/scorer.d.ts +226 -0
- package/dist/kg/scorer.js +463 -0
- package/dist/kg/targets.d.ts +17 -0
- package/dist/kg/targets.js +16 -0
- package/dist/kg/text.d.ts +45 -0
- package/dist/kg/text.js +100 -0
- package/dist/live/adapters.d.ts +16 -0
- package/dist/live/adapters.js +58 -0
- package/dist/live/env.d.ts +39 -0
- package/dist/live/env.js +56 -0
- package/dist/live/index.d.ts +6 -0
- package/dist/live/index.js +16 -0
- package/dist/live/live-ai.d.ts +104 -0
- package/dist/live/live-ai.js +157 -0
- package/dist/parity/constants.d.ts +79 -0
- package/dist/parity/constants.js +104 -0
- package/dist/parity/index.d.ts +20 -0
- package/dist/parity/index.js +38 -0
- package/dist/parity/internals.d.ts +8 -0
- package/dist/parity/internals.js +16 -0
- package/dist/parity/scenario.d.ts +45 -0
- package/dist/parity/scenario.js +245 -0
- package/dist/parity/script.d.ts +44 -0
- package/dist/parity/script.js +136 -0
- package/dist/parity/snapshot.d.ts +43 -0
- package/dist/parity/snapshot.js +71 -0
- package/dist/real-data/index.d.ts +2 -0
- package/dist/real-data/index.js +12 -0
- package/dist/real-data/real-data.d.ts +76 -0
- package/dist/real-data/real-data.js +156 -0
- package/fixtures/kg-golden/README.md +136 -0
- package/fixtures/kg-golden/ask-questions.json +458 -0
- package/fixtures/kg-golden/meetings/m01-northwind-pilot-kickoff.json +792 -0
- package/fixtures/kg-golden/meetings/m02-halden-migration-planning.json +814 -0
- package/fixtures/kg-golden/meetings/m03-larkspur-weekly-sync.json +601 -0
- package/fixtures/kg-golden/meetings/m04-corvid-intake-discovery.json +690 -0
- package/fixtures/kg-golden/meetings/m05-hosting-vendor-selection.json +631 -0
- package/fixtures/kg-golden/meetings/m06-northwind-floor-data-review.json +649 -0
- package/fixtures/kg-golden/meetings/m07-gullwing-intro-notes.json +428 -0
- package/fixtures/kg-golden/meetings/m08-halden-migration-checkpoint.json +672 -0
- package/fixtures/kg-golden/meetings/m09-northwind-platform-org-update.json +711 -0
- package/fixtures/kg-golden/meetings/m10-corvid-prototype-review.json +623 -0
- package/fixtures/kg-golden/meetings/m11-solvane-grid-dashboard-scoping.json +689 -0
- package/fixtures/kg-golden/meetings/m12-northwind-pilot-replan.json +681 -0
- package/fixtures/kg-golden/meetings/m13-halden-cutover-go-no-go.json +721 -0
- package/fixtures/kg-golden/meetings/m14-gullwing-discovery-readout.json +719 -0
- package/fixtures/kg-golden/meetings/m15-halden-handover-ravi-leaving.json +716 -0
- package/fixtures/kg-golden/meetings/m16-varden-claims-portal-intro.json +704 -0
- package/fixtures/kg-golden/meetings/m17-corvid-usability-debrief.json +572 -0
- package/fixtures/kg-golden/meetings/m18-hosting-decision-revisited.json +655 -0
- package/fixtures/kg-golden/meetings/m19-solvane-integration-kickoff.json +726 -0
- package/fixtures/kg-golden/meetings/m20-northwind-go-live-retro.json +673 -0
- package/fixtures/kg-golden/meetings/m21-quarterly-account-review.json +449 -0
- package/fixtures/kg-golden/meetings/m22-halden-intro-call-2020.json +558 -0
- package/fixtures/kg-golden/meetings/m23-gullwing-model-review.json +702 -0
- package/fixtures/kg-golden/meetings/m24-varden-discovery-midpoint.json +643 -0
- package/fixtures/kg-golden/meetings/m25-corvid-rollout-planning.json +642 -0
- package/fixtures/kg-golden/meetings/m26-larkspur-engineering-prep.json +695 -0
- package/fixtures/kg-golden/meetings/m27-solvane-phase-one-demo.json +649 -0
- package/fixtures/kg-golden/meetings/m28-northwind-building-5-scoping.json +715 -0
- package/fixtures/kg-golden/meetings/m29-halden-southern-cutover-review.json +752 -0
- package/fixtures/kg-golden/meetings/m30-varden-portal-build-kickoff.json +697 -0
- package/fixtures/kg-golden/meetings/m31-gullwing-rollout-renewal.json +681 -0
- package/fixtures/kg-golden/meetings/m32-brambleway-weekly-sync.json +472 -0
- package/fixtures/kg-golden/meetings/m33-nina-oliver-one-on-one.json +342 -0
- package/fixtures/kg-golden/meetings/m34-coffee-with-dana.json +249 -0
- package/fixtures/kg-golden/meetings/m35-brambleway-june-sync.json +553 -0
- package/fixtures/kg-golden/meetings/m36-copenhagen-conference-planning.json +296 -0
- package/host/generated/client.d.ts +1 -0
- package/host/generated/client.js +5 -0
- package/host/generated/default.d.ts +1 -0
- package/host/generated/default.js +5 -0
- package/host/generated/edge.d.ts +1 -0
- package/host/generated/edge.js +1658 -0
- package/host/generated/index-browser.js +1684 -0
- package/host/generated/index.d.ts +192335 -0
- package/host/generated/index.js +1658 -0
- package/host/generated/package.json +139 -0
- package/host/generated/query_compiler_fast_bg.js +2 -0
- package/host/generated/query_compiler_fast_bg.wasm +0 -0
- package/host/generated/query_compiler_fast_bg.wasm-base64.js +2 -0
- package/host/generated/runtime/client.d.ts +3433 -0
- package/host/generated/runtime/client.js +110 -0
- package/host/generated/runtime/index-browser.d.ts +90 -0
- package/host/generated/runtime/index-browser.js +6 -0
- package/host/generated/runtime/wasm-compiler-edge.js +100 -0
- package/host/generated/schema.prisma +6312 -0
- package/host/generated/wasm-edge-light-loader.mjs +5 -0
- package/host/generated/wasm-worker-loader.mjs +5 -0
- package/host/prisma/fragments/kvox.ask.prisma +126 -0
- package/host/prisma/fragments/kvox.graph.prisma +1073 -0
- package/host/prisma/fragments/kvox.notes.prisma +787 -0
- package/host/prisma/fragments/kvox.search.prisma +211 -0
- package/host/prisma/fragments/kvox.transcripts.prisma +790 -0
- package/host/prisma/kvox.lock +101 -0
- package/host/prisma/migrations/20240101000000_initial/migration.sql +297 -0
- package/host/prisma/migrations/20240101000001_add_personal_access_tokens/migration.sql +31 -0
- package/host/prisma/migrations/20240101000002_add_credentials/migration.sql +20 -0
- package/host/prisma/migrations/20240101000003_add_notification_deliveries/migration.sql +27 -0
- package/host/prisma/migrations/20240101000004_drop_stale_uuid_defaults/migration.sql +35 -0
- package/host/prisma/migrations/20240101000005_add_notifications/migration.sql +22 -0
- package/host/prisma/migrations/20240101000006_add_push_subscriptions/migration.sql +25 -0
- package/host/prisma/migrations/20240101000007_add_jobs/migration.sql +110 -0
- package/host/prisma/migrations/20240101000008_add_worker_nodes/migration.sql +90 -0
- package/host/prisma/migrations/20240101000009_add_database_backup_runs/migration.sql +104 -0
- package/host/prisma/migrations/20240101000010_add_notification_broadcasts/migration.sql +57 -0
- package/host/prisma/migrations/20240101000011_add_backup_run_job_link/migration.sql +87 -0
- package/host/prisma/migrations/20240101000012_add_job_node_secrets/migration.sql +61 -0
- package/host/prisma/migrations/20240101000013_add_backup_run_pg_dump_version/migration.sql +26 -0
- package/host/prisma/migrations/20240101000014_add_job_claim_token/migration.sql +92 -0
- package/host/prisma/migrations/20240101000015_add_ai_platform/migration.sql +108 -0
- package/host/prisma/migrations/20240101000016_revoke_viewer_ai_use/migration.sql +32 -0
- package/host/prisma/migrations/20240101000017_add_user_credentials/migration.sql +29 -0
- package/host/prisma/migrations/20240101000018_add_device_session_credential_link/migration.sql +52 -0
- package/host/prisma/migrations/20240101000019_add_worker_node_vitals/migration.sql +3 -0
- package/host/prisma/migrations/20240101000020_add_job_trace_context/migration.sql +6 -0
- package/host/prisma/migrations/20240101000021_add_retention_created_at_indexes/migration.sql +13 -0
- package/host/prisma/migrations/20240101000022_add_organizations/migration.sql +142 -0
- package/host/prisma/migrations/20240101000023_split_system_org_roles/migration.sql +131 -0
- package/host/prisma/migrations/20240101000024_org_scoped_rls/migration.sql +215 -0
- package/host/prisma/migrations/20240101000025_add_groups/migration.sql +165 -0
- package/host/prisma/migrations/20240101000026_add_grants/migration.sql +117 -0
- package/host/prisma/migrations/20240101000027_add_org_settings/migration.sql +49 -0
- package/host/prisma/migrations/20240101000028_add_org_credentials/migration.sql +54 -0
- package/host/prisma/migrations/20240101000029_add_jobs_org_id/migration.sql +18 -0
- package/host/prisma/migrations/20240101000030_add_jobs_org_id_status_index/migration.sql +6 -0
- package/host/prisma/migrations/20240101000031_add_broadcast_target_org/migration.sql +21 -0
- package/host/prisma/migrations/20240101000032_add_android_app/migration.sql +60 -0
- package/host/prisma/migrations/20240101000033_open_job_reason/migration.sql +16 -0
- package/host/prisma/migrations/20240101000034_ai_usage_feature/migration.sql +37 -0
- package/host/prisma/migrations/20240101000035_add_app_metadata/migration.sql +49 -0
- package/host/prisma/migrations/20240101000036_open_sharing_vocabularies/migration.sql +97 -0
- package/host/prisma/migrations/20260914130000_kvox_add_transcripts/migration.sql +322 -0
- package/host/prisma/migrations/20260914140000_kvox_add_notes/migration.sql +311 -0
- package/host/prisma/migrations/20260915120000_kvox_add_search_vectors/migration.sql +150 -0
- package/host/prisma/migrations/20260915130000_kvox_add_search_embeddings/migration.sql +362 -0
- package/host/prisma/migrations/20260915210000_kvox_add_note_title_source/migration.sql +30 -0
- package/host/prisma/migrations/20260923120000_kvox_add_note_generation_context_snapshot/migration.sql +24 -0
- package/host/prisma/migrations/20260923130000_kvox_add_user_hidden_note_templates/migration.sql +35 -0
- package/host/prisma/migrations/20260923140000_kvox_add_transcript_speaker_identities/migration.sql +6 -0
- package/host/prisma/migrations/20260923150000_kvox_transcript_name_checks/migration.sql +95 -0
- package/host/prisma/migrations/20260923160000_kvox_add_note_body_format/migration.sql +27 -0
- package/host/prisma/migrations/20260923170000_kvox_add_note_version_body_format/migration.sql +23 -0
- package/host/prisma/migrations/20260926012548_kvox_add_knowledge_graph/migration.sql +680 -0
- package/host/prisma/migrations/20260926030000_kvox_fix_kg_relations_valid_precision_chk/migration.sql +29 -0
- package/host/prisma/migrations/20260926030100_kvox_add_transcript_recorded_at/migration.sql +9 -0
- package/host/prisma/migrations/20260926030200_kvox_kg_evidence_invariant/migration.sql +60 -0
- package/host/prisma/migrations/20260926040000_kvox_add_kg_graph_layouts/migration.sql +30 -0
- package/host/prisma/migrations/20260926050000_kvox_add_ask_conversations/migration.sql +99 -0
- package/host/prisma/migrations/20260926231512_kvox_add_kg_exports/migration.sql +52 -0
- package/host/prisma/migrations/20261016120000_kvox_transcripts_org_id/migration.sql +49 -0
- package/host/prisma/platform.lock +319 -0
- package/host/prisma/schema/app.kvox.ask.prisma +110 -0
- package/host/prisma/schema/app.kvox.graph.prisma +1030 -0
- package/host/prisma/schema/app.kvox.notes.prisma +746 -0
- package/host/prisma/schema/app.kvox.search.prisma +208 -0
- package/host/prisma/schema/app.kvox.transcripts.prisma +726 -0
- package/host/prisma/schema/platform.ai.prisma +419 -0
- package/host/prisma/schema/platform.android-app.prisma +46 -0
- package/host/prisma/schema/platform.base.prisma +15 -0
- package/host/prisma/schema/platform.credentials.prisma +156 -0
- package/host/prisma/schema/platform.db-backup.prisma +359 -0
- package/host/prisma/schema/platform.identity.prisma +638 -0
- package/host/prisma/schema/platform.jobs.prisma +824 -0
- package/host/prisma/schema/platform.notifications.prisma +641 -0
- package/host/prisma/schema/platform.settings.prisma +53 -0
- package/host/prisma/schema/platform.sharing.prisma +208 -0
- package/host/prisma/schema/platform.storage.prisma +118 -0
- package/package.json +112 -1
- package/parity/requests.ask.json +2681 -0
- package/parity/requests.m01.json +1554 -0
- package/parity/requests.m02.json +1554 -0
- package/parity/requests.m03.json +1554 -0
- package/parity/requests.m04.json +1554 -0
- package/parity/requests.m05.json +1554 -0
- package/parity/requests.m06.json +1554 -0
- package/parity/requests.m07.json +1554 -0
- package/parity/requests.m08.json +1554 -0
- package/parity/requests.m09.json +1554 -0
- package/parity/requests.m10.json +1554 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
// GENERATED by platform db compose — do not edit
|
|
2
|
+
// source: package:identity.prisma
|
|
3
|
+
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// User & Identity
|
|
6
|
+
// =============================================================================
|
|
7
|
+
|
|
8
|
+
// @extensible — back-relations only, added with "extend model User { ... }" in a fragment
|
|
9
|
+
model User {
|
|
10
|
+
id String @id @default(uuid()) @db.Uuid
|
|
11
|
+
email String @unique
|
|
12
|
+
displayName String? @map("display_name")
|
|
13
|
+
providerDisplayName String? @map("provider_display_name")
|
|
14
|
+
profileImageUrl String? @map("profile_image_url")
|
|
15
|
+
providerProfileImageUrl String? @map("provider_profile_image_url")
|
|
16
|
+
isActive Boolean @default(true) @map("is_active")
|
|
17
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
18
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
19
|
+
// App-owned, namespaced fields (`registerUserMetadataSchema`, PP-14.27): one top-level
|
|
20
|
+
// key per registered namespace, each validated against its zod schema on write.
|
|
21
|
+
// NOT a secret store (a secret-named field is refused at registration; secrets go
|
|
22
|
+
// in `user_credentials`). At most 16 KiB per row, enforced by the writer and, as a
|
|
23
|
+
// 32 KiB backstop, by `users_metadata_size_check`. Never returned by the platform's
|
|
24
|
+
// routes: an app exposes a namespace only through a route it writes.
|
|
25
|
+
metadata Json @default("{}") @db.JsonB
|
|
26
|
+
|
|
27
|
+
// Relations
|
|
28
|
+
identities UserIdentity[]
|
|
29
|
+
userRoles UserRole[]
|
|
30
|
+
auditEvents AuditEvent[] @relation("ActorEvents")
|
|
31
|
+
refreshTokens RefreshToken[] @relation("UserRefreshTokens")
|
|
32
|
+
allowlistEntriesAdded AllowedEmail[] @relation("AllowlistAddedBy")
|
|
33
|
+
allowlistEntry AllowedEmail? @relation("AllowlistClaimedBy")
|
|
34
|
+
deviceCodes DeviceCode[] @relation("UserDeviceCodes")
|
|
35
|
+
personalAccessTokens PersonalAccessToken[] @relation("UserPersonalAccessTokens")
|
|
36
|
+
memberships Membership[]
|
|
37
|
+
organizationsCreated Organization[] @relation("OrganizationCreatedBy")
|
|
38
|
+
invitesSent Invite[] @relation("InviteInvitedBy")
|
|
39
|
+
invitesAccepted Invite[] @relation("InviteAcceptedBy")
|
|
40
|
+
|
|
41
|
+
// composed from package:ai.prisma
|
|
42
|
+
// AI platform (#423, epic #419). Four relations, four different
|
|
43
|
+
// `onDelete` answers — see each model's own block comment for why:
|
|
44
|
+
// - `aiModelsUpdated` (`AiModel.updatedByUser`) is provenance on a
|
|
45
|
+
// CATALOG row nobody owns, so `SetNull`, matching `Credential
|
|
46
|
+
// .updatedByUser` above.
|
|
47
|
+
// - `aiKeys` (`UserAiKey.user`) is `Cascade`: a BYOK key belongs to the
|
|
48
|
+
// user who typed it in, in exactly the sense `PushSubscription.user`
|
|
49
|
+
// does — there is no reader for it once the account is gone.
|
|
50
|
+
// - `aiRuns` (`AiRun.user`) is `SetNull`: a run is history, kept for
|
|
51
|
+
// usage/audit purposes independent of whether its requester's account
|
|
52
|
+
// still exists, matching `NotificationDelivery.user`.
|
|
53
|
+
// - `aiUsageEvents` (`AiUsageEvent.user`) is `SetNull` for the identical
|
|
54
|
+
// reason `aiRuns` is: usage accounting must outlive the account it
|
|
55
|
+
// billed.
|
|
56
|
+
aiModelsUpdated AiModel[] @relation("AiModelUpdatedBy")
|
|
57
|
+
aiKeys UserAiKey[]
|
|
58
|
+
aiRuns AiRun[]
|
|
59
|
+
aiUsageEvents AiUsageEvent[]
|
|
60
|
+
|
|
61
|
+
// composed from package:android-app.prisma
|
|
62
|
+
androidAppReleases AndroidAppRelease[] @relation("AndroidAppReleaseUploader")
|
|
63
|
+
|
|
64
|
+
// composed from package:credentials.prisma
|
|
65
|
+
credentialUpdates Credential[] @relation("CredentialUpdatedBy")
|
|
66
|
+
// Per-user encrypted credentials (issue #387). Cascade: a user-owned
|
|
67
|
+
// secret has no meaning once its owner is gone — see `UserCredential`.
|
|
68
|
+
userCredentials UserCredential[] @relation("UserCredentials")
|
|
69
|
+
|
|
70
|
+
// composed from package:credentials.prisma
|
|
71
|
+
// Provenance on an organization's credential (issue #735). SetNull: an
|
|
72
|
+
// org's key outlives the administrator who last typed it in.
|
|
73
|
+
orgCredentialUpdates OrgCredential[] @relation("OrgCredentialUpdatedBy")
|
|
74
|
+
|
|
75
|
+
// composed from package:db-backup.prisma
|
|
76
|
+
// Two relations to ONE table, deliberately (#281, epic #254): a
|
|
77
|
+
// `database_backup_runs` row records both "who asked for this backup" and,
|
|
78
|
+
// once Phase 7 restores from it, "who restored it". They are different acts
|
|
79
|
+
// by (usually) different people at different times, so they are different
|
|
80
|
+
// columns and therefore different relation names. See the block comment
|
|
81
|
+
// above `DatabaseBackupRun` for why the restore audit lives on the backup's
|
|
82
|
+
// own row rather than in a second table.
|
|
83
|
+
databaseBackupRuns DatabaseBackupRun[] @relation("UserDatabaseBackupRuns")
|
|
84
|
+
databaseRestores DatabaseBackupRun[] @relation("UserDatabaseRestores")
|
|
85
|
+
|
|
86
|
+
// composed from package:jobs.prisma
|
|
87
|
+
workerNodes WorkerNode[] @relation("UserWorkerNodes")
|
|
88
|
+
nodeCredentials NodeCredential[] @relation("UserNodeCredentials")
|
|
89
|
+
|
|
90
|
+
// composed from package:notifications.prisma
|
|
91
|
+
notificationDeliveries NotificationDelivery[] @relation("UserNotificationDeliveries")
|
|
92
|
+
notifications Notification[] @relation("UserNotifications")
|
|
93
|
+
pushSubscriptions PushSubscription[] @relation("UserPushSubscriptions")
|
|
94
|
+
broadcastsCreated NotificationBroadcast[] @relation("BroadcastAuthor")
|
|
95
|
+
|
|
96
|
+
// composed from package:settings.prisma
|
|
97
|
+
userSettings UserSettings?
|
|
98
|
+
settingsUpdates SystemSettings[] @relation("SettingsUpdater")
|
|
99
|
+
orgSettingsUpdates OrgSettings[] @relation("OrgSettingsUpdater")
|
|
100
|
+
|
|
101
|
+
// composed from package:sharing.prisma
|
|
102
|
+
groupsCreated Group[] @relation("GroupCreatedBy")
|
|
103
|
+
groupMemberships GroupMember[] @relation("GroupMemberUser")
|
|
104
|
+
groupMembersAdded GroupMember[] @relation("GroupMemberAddedBy")
|
|
105
|
+
groupInvitesSent GroupInvite[] @relation("GroupInviteInvitedBy")
|
|
106
|
+
groupInvitesClaimed GroupInvite[] @relation("GroupInviteAcceptedBy")
|
|
107
|
+
grantsReceived Grant[] @relation("GrantGranteeUser")
|
|
108
|
+
grantsRevoked Grant[] @relation("GrantRevokedBy")
|
|
109
|
+
grantsGiven Grant[] @relation("GrantGrantedBy")
|
|
110
|
+
|
|
111
|
+
// composed from package:storage.prisma
|
|
112
|
+
storageObjects StorageObject[] @relation("UserStorageObjects")
|
|
113
|
+
|
|
114
|
+
// composed from app:kvox.ask.prisma
|
|
115
|
+
// Back-relations onto the platform's User (an `extend model` block holds back-relation fields only).
|
|
116
|
+
// Ask conversations (issue #376, epic #348). CASCADES — a saved question
|
|
117
|
+
// and its answers are the user's own content, derived from their private
|
|
118
|
+
// recordings, with no read path for anyone else (no `ask:read_any`), so a
|
|
119
|
+
// deleted account leaves no Ask history. See the block comment above
|
|
120
|
+
// `AskConversation`.
|
|
121
|
+
askConversations AskConversation[]
|
|
122
|
+
|
|
123
|
+
// composed from app:kvox.graph.prisma
|
|
124
|
+
// Back-relations onto the platform's User (an `extend model` block holds back-relation fields only).
|
|
125
|
+
// Connected Knowledge (epic #344, issue #351). Every `kg_*` table carries
|
|
126
|
+
// its own `owner_id` and CASCADES — an entity, relation, item, piece of
|
|
127
|
+
// evidence, mention, proposal, merge, distinct-pair or attribute
|
|
128
|
+
// definition has no meaning and no permission path to read it once its
|
|
129
|
+
// owner is gone, the identical reasoning `notes.owner_id`/
|
|
130
|
+
// `transcripts.owner_id` already establish (§10, and there is no
|
|
131
|
+
// `graph:read_any` for the same reason there is no `notes:read_any`).
|
|
132
|
+
// `kgEntityViews` is the one exception: it is keyed on `user_id`, not
|
|
133
|
+
// `owner_id`, because "which entities has THIS user looked at" is a
|
|
134
|
+
// per-viewer fact, not an ownership fact — see the block comment above
|
|
135
|
+
// `KgEntityView`.
|
|
136
|
+
kgEntities KgEntity[]
|
|
137
|
+
kgEntityAliases KgEntityAlias[]
|
|
138
|
+
kgRelations KgRelation[]
|
|
139
|
+
kgItems KgItem[]
|
|
140
|
+
kgEvidence KgEvidence[]
|
|
141
|
+
kgMentions KgMention[]
|
|
142
|
+
kgProposals KgProposal[]
|
|
143
|
+
kgMerges KgMerge[]
|
|
144
|
+
kgDistinctPairs KgDistinctPair[]
|
|
145
|
+
kgAttributeDefs KgAttributeDef[]
|
|
146
|
+
kgEntityDigests KgEntityDigest[]
|
|
147
|
+
kgEntityViews KgEntityView[]
|
|
148
|
+
kgGraphLayouts KgGraphLayout[]
|
|
149
|
+
|
|
150
|
+
// #386 — RDF exports of the owner's graph. CASCADES: a disposable,
|
|
151
|
+
// reproducible artefact with its own 7-day expiry, like `note_exports`.
|
|
152
|
+
kgExports KgExport[]
|
|
153
|
+
|
|
154
|
+
// composed from app:kvox.notes.prisma
|
|
155
|
+
// Back-relations onto the platform's User (an `extend model` block holds back-relation fields only).
|
|
156
|
+
// Notes (issue #48, epic #45). Four relations to the note tables, split by
|
|
157
|
+
// role for the identical reason the six transcript relations above are:
|
|
158
|
+
// "owns this note", "owns this custom template", "wrote this version" and
|
|
159
|
+
// "asked for this export" are four different acts. See the block comment
|
|
160
|
+
// above the `Note` model for the full reasoning behind each `onDelete`.
|
|
161
|
+
notes Note[] @relation("NoteOwner")
|
|
162
|
+
noteTemplatesOwned NoteTemplate[] @relation("NoteTemplateOwner")
|
|
163
|
+
noteVersionsAuthored NoteVersion[] @relation("NoteVersionAuthor")
|
|
164
|
+
noteExportsRequested NoteExport[] @relation("NoteExportRequestedBy")
|
|
165
|
+
|
|
166
|
+
// Per-user "hide this template from my picker" preference (#310, epic
|
|
167
|
+
// #306). CASCADES — the preference is meaningless once its owner is gone.
|
|
168
|
+
// See the block comment above `UserHiddenNoteTemplate` for why this is a
|
|
169
|
+
// join table rather than a `user_settings` namespace.
|
|
170
|
+
hiddenNoteTemplates UserHiddenNoteTemplate[]
|
|
171
|
+
|
|
172
|
+
// composed from app:kvox.search.prisma
|
|
173
|
+
// Back-relations onto the platform's User (an `extend model` block holds back-relation fields only).
|
|
174
|
+
// Semantic search index state (issue #181, epic #165). ONE relation, and
|
|
175
|
+
// it CASCADES — an index-state row for a document owned by nobody means
|
|
176
|
+
// nothing, the identical reasoning `transcripts`/`notes` already apply to
|
|
177
|
+
// their own `ownerId`. See the block comment above `SearchIndexState`.
|
|
178
|
+
searchIndexStates SearchIndexState[] @relation("SearchIndexStateOwner")
|
|
179
|
+
|
|
180
|
+
// composed from app:kvox.transcripts.prisma
|
|
181
|
+
// Back-relations onto the platform's User (an `extend model` block holds back-relation fields only).
|
|
182
|
+
// Transcripts (issue #24, epic #19). Four relations to the transcript
|
|
183
|
+
// tables, split by role for the same reason `databaseBackupRuns` /
|
|
184
|
+
// `databaseRestores` are two relations to one table above: "owns this
|
|
185
|
+
// transcript", "wrote this version", "last edited this segment" and "asked
|
|
186
|
+
// for this export" are four different acts, not one (a share is a platform
|
|
187
|
+
// `grants` row since issue #485). See the block comment above the `Transcript`
|
|
188
|
+
// model for the full reasoning behind every one of these relations'
|
|
189
|
+
// `onDelete` behaviour.
|
|
190
|
+
transcripts Transcript[] @relation("TranscriptOwner")
|
|
191
|
+
transcriptVersionsAuthored TranscriptVersion[] @relation("TranscriptVersionAuthor")
|
|
192
|
+
transcriptSegmentEdits TranscriptSegment[] @relation("TranscriptSegmentEditor")
|
|
193
|
+
transcriptExportsRequested TranscriptExport[] @relation("TranscriptExportRequestedBy")
|
|
194
|
+
|
|
195
|
+
// AI name checks (issue #328, epic #326). Two relations, split by role for
|
|
196
|
+
// the same reason the six transcript relations above are: "asked for this
|
|
197
|
+
// run" and "accepted/rejected this suggestion" are different acts. See the
|
|
198
|
+
// block comment above the `TranscriptNameCheck` model for the full
|
|
199
|
+
// reasoning behind each `onDelete`.
|
|
200
|
+
transcriptNameChecksRequested TranscriptNameCheck[] @relation("TranscriptNameCheckRequestedBy")
|
|
201
|
+
transcriptNameSuggestionsDecided TranscriptNameSuggestion[] @relation("TranscriptNameSuggestionDecidedBy")
|
|
202
|
+
|
|
203
|
+
@@map("users")
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
model UserIdentity {
|
|
207
|
+
id String @id @default(uuid()) @db.Uuid
|
|
208
|
+
userId String @map("user_id") @db.Uuid
|
|
209
|
+
provider String
|
|
210
|
+
providerSubject String @map("provider_subject")
|
|
211
|
+
providerEmail String? @map("provider_email")
|
|
212
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
213
|
+
|
|
214
|
+
// Relations
|
|
215
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
216
|
+
|
|
217
|
+
@@unique([provider, providerSubject])
|
|
218
|
+
@@map("user_identities")
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// =============================================================================
|
|
222
|
+
// RBAC - Roles & Permissions
|
|
223
|
+
// =============================================================================
|
|
224
|
+
//
|
|
225
|
+
// Two kinds of role (PP-6.3): SYSTEM roles operate the deployment and are held
|
|
226
|
+
// in `user_roles`; ORG roles operate one organization and are held on a
|
|
227
|
+
// membership (`memberships.role_id`). A role is granted only permissions of its
|
|
228
|
+
// own scope (enforced by the app's permission registry, seeded by
|
|
229
|
+
// `seedPlatform`). Roles stay global rows; per-organization custom roles are a
|
|
230
|
+
// later seam.
|
|
231
|
+
|
|
232
|
+
enum RoleScope {
|
|
233
|
+
system
|
|
234
|
+
org
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// @extensible — back-relations only, added with "extend model Role { ... }" in a fragment
|
|
238
|
+
// (a table of the app's own that points at a role, such as per-role settings). The
|
|
239
|
+
// role's own columns and the `role_permissions` grants stay the platform's: `seedPlatform`
|
|
240
|
+
// refreshes them from the registry on every run.
|
|
241
|
+
model Role {
|
|
242
|
+
id String @id @default(uuid()) @db.Uuid
|
|
243
|
+
name String @unique
|
|
244
|
+
description String?
|
|
245
|
+
scope RoleScope @default(system)
|
|
246
|
+
|
|
247
|
+
// Relations
|
|
248
|
+
rolePermissions RolePermission[]
|
|
249
|
+
userRoles UserRole[]
|
|
250
|
+
memberships Membership[]
|
|
251
|
+
invites Invite[]
|
|
252
|
+
|
|
253
|
+
@@map("roles")
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Deliberately NOT `@extensible` (PP-14.27). A permission is a string owned by code:
|
|
257
|
+
// the registry (`registerPermissions`) is the source of truth, `seedPlatform` upserts
|
|
258
|
+
// the rows by `name` and never deletes one, and a removed permission is retired by an
|
|
259
|
+
// explicit expand/contract migration. A foreign key from an app table would make that
|
|
260
|
+
// retirement fail and would tie app data to a row id that no registry declares. An app
|
|
261
|
+
// refers to a permission by its name string in code, never by a relation.
|
|
262
|
+
model Permission {
|
|
263
|
+
id String @id @default(uuid()) @db.Uuid
|
|
264
|
+
name String @unique
|
|
265
|
+
description String?
|
|
266
|
+
scope RoleScope @default(system)
|
|
267
|
+
|
|
268
|
+
// Relations
|
|
269
|
+
rolePermissions RolePermission[]
|
|
270
|
+
|
|
271
|
+
@@map("permissions")
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
model RolePermission {
|
|
275
|
+
roleId String @map("role_id") @db.Uuid
|
|
276
|
+
permissionId String @map("permission_id") @db.Uuid
|
|
277
|
+
|
|
278
|
+
// Relations
|
|
279
|
+
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
280
|
+
permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
|
|
281
|
+
|
|
282
|
+
@@id([roleId, permissionId])
|
|
283
|
+
@@map("role_permissions")
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
model UserRole {
|
|
287
|
+
userId String @map("user_id") @db.Uuid
|
|
288
|
+
roleId String @map("role_id") @db.Uuid
|
|
289
|
+
|
|
290
|
+
// Relations
|
|
291
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
292
|
+
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
293
|
+
|
|
294
|
+
@@id([userId, roleId])
|
|
295
|
+
@@map("user_roles")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// =============================================================================
|
|
299
|
+
// Audit
|
|
300
|
+
// =============================================================================
|
|
301
|
+
|
|
302
|
+
model AuditEvent {
|
|
303
|
+
id String @id @default(uuid()) @db.Uuid
|
|
304
|
+
actorUserId String? @map("actor_user_id") @db.Uuid
|
|
305
|
+
action String
|
|
306
|
+
targetType String @map("target_type")
|
|
307
|
+
targetId String @map("target_id")
|
|
308
|
+
meta Json?
|
|
309
|
+
// The organization the action happened in (#725). NULL for a system event
|
|
310
|
+
// (settings, backups, restores, the allowlist): see the backfill in
|
|
311
|
+
// `0025_org_scoped_rls`. `SetNull`: history outlives the organization. No
|
|
312
|
+
// row-level security on this table in this release (`org-optional`).
|
|
313
|
+
orgId String? @map("org_id") @db.Uuid
|
|
314
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
315
|
+
|
|
316
|
+
// Relations
|
|
317
|
+
actorUser User? @relation("ActorEvents", fields: [actorUserId], references: [id], onDelete: SetNull)
|
|
318
|
+
org Organization? @relation(fields: [orgId], references: [id], onDelete: SetNull)
|
|
319
|
+
|
|
320
|
+
@@index([actorUserId])
|
|
321
|
+
@@index([targetType, targetId])
|
|
322
|
+
@@index([createdAt])
|
|
323
|
+
@@index([orgId, createdAt])
|
|
324
|
+
@@map("audit_events")
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// =============================================================================
|
|
328
|
+
// Refresh Tokens (for token rotation)
|
|
329
|
+
// =============================================================================
|
|
330
|
+
|
|
331
|
+
model RefreshToken {
|
|
332
|
+
id String @id @default(uuid()) @db.Uuid
|
|
333
|
+
userId String @map("user_id") @db.Uuid
|
|
334
|
+
tokenHash String @unique @map("token_hash")
|
|
335
|
+
expiresAt DateTime @map("expires_at") @db.Timestamptz
|
|
336
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
337
|
+
revokedAt DateTime? @map("revoked_at") @db.Timestamptz
|
|
338
|
+
// The device-authorization session that minted this refresh token, if any
|
|
339
|
+
// (issue #518). Lets revoking a `DeviceCode` session reach the refresh
|
|
340
|
+
// token it handed out. Nullable: most refresh tokens come from the
|
|
341
|
+
// interactive login flow, not the device flow. `SetNull` because a device
|
|
342
|
+
// code can be purged independently of the token lifecycle it kicked off.
|
|
343
|
+
deviceCodeId String? @map("device_code_id") @db.Uuid
|
|
344
|
+
// The organization this session is active in (PP-6.1). Nullable (expand
|
|
345
|
+
// step); backfilled to the default organization. Carried by the active-org
|
|
346
|
+
// token (PP-6.4). `Cascade`: a credential has no meaning once its org is gone.
|
|
347
|
+
orgId String? @map("org_id") @db.Uuid
|
|
348
|
+
|
|
349
|
+
// Relations
|
|
350
|
+
user User @relation("UserRefreshTokens", fields: [userId], references: [id], onDelete: Cascade)
|
|
351
|
+
deviceCode DeviceCode? @relation(fields: [deviceCodeId], references: [id], onDelete: SetNull)
|
|
352
|
+
org Organization? @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
353
|
+
|
|
354
|
+
@@index([userId])
|
|
355
|
+
@@index([expiresAt])
|
|
356
|
+
@@index([deviceCodeId])
|
|
357
|
+
@@index([orgId])
|
|
358
|
+
@@map("refresh_tokens")
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// =============================================================================
|
|
362
|
+
// Personal Access Tokens (PAT)
|
|
363
|
+
// =============================================================================
|
|
364
|
+
|
|
365
|
+
enum PatDurationUnit {
|
|
366
|
+
minutes
|
|
367
|
+
days
|
|
368
|
+
months
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
model PersonalAccessToken {
|
|
372
|
+
id String @id @default(uuid()) @db.Uuid
|
|
373
|
+
userId String @map("user_id") @db.Uuid
|
|
374
|
+
name String
|
|
375
|
+
tokenHash String @unique @map("token_hash")
|
|
376
|
+
tokenPrefix String @map("token_prefix")
|
|
377
|
+
durationValue Int @map("duration_value")
|
|
378
|
+
durationUnit PatDurationUnit @map("duration_unit")
|
|
379
|
+
expiresAt DateTime @map("expires_at") @db.Timestamptz
|
|
380
|
+
lastUsedAt DateTime? @map("last_used_at") @db.Timestamptz
|
|
381
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
382
|
+
revokedAt DateTime? @map("revoked_at") @db.Timestamptz
|
|
383
|
+
// The organization the token acts in (PP-6.1). Nullable (expand step);
|
|
384
|
+
// backfilled to the default organization. `Cascade`: see `RefreshToken.orgId`.
|
|
385
|
+
orgId String? @map("org_id") @db.Uuid
|
|
386
|
+
|
|
387
|
+
// Relations
|
|
388
|
+
user User @relation("UserPersonalAccessTokens", fields: [userId], references: [id], onDelete: Cascade)
|
|
389
|
+
org Organization? @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
390
|
+
// The device-authorization sessions that issued this PAT (issue #518).
|
|
391
|
+
// Almost always at most one, but not constrained `@unique` here — see
|
|
392
|
+
// `DeviceCode.patId`.
|
|
393
|
+
deviceCodes DeviceCode[]
|
|
394
|
+
|
|
395
|
+
@@index([userId])
|
|
396
|
+
@@index([expiresAt])
|
|
397
|
+
@@index([orgId])
|
|
398
|
+
@@map("personal_access_tokens")
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// =============================================================================
|
|
402
|
+
// Allowlist (Email Access Control)
|
|
403
|
+
// =============================================================================
|
|
404
|
+
|
|
405
|
+
model AllowedEmail {
|
|
406
|
+
id String @id @default(uuid()) @db.Uuid
|
|
407
|
+
email String @unique
|
|
408
|
+
addedById String? @map("added_by_id") @db.Uuid
|
|
409
|
+
addedAt DateTime @default(now()) @map("added_at") @db.Timestamptz
|
|
410
|
+
claimedById String? @unique @map("claimed_by_id") @db.Uuid
|
|
411
|
+
claimedAt DateTime? @map("claimed_at") @db.Timestamptz
|
|
412
|
+
notes String?
|
|
413
|
+
|
|
414
|
+
// Relations
|
|
415
|
+
addedBy User? @relation("AllowlistAddedBy", fields: [addedById], references: [id], onDelete: SetNull)
|
|
416
|
+
claimedBy User? @relation("AllowlistClaimedBy", fields: [claimedById], references: [id], onDelete: SetNull)
|
|
417
|
+
|
|
418
|
+
@@map("allowed_emails")
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// =============================================================================
|
|
422
|
+
// Device Authorization Flow (RFC 8628)
|
|
423
|
+
// =============================================================================
|
|
424
|
+
|
|
425
|
+
enum DeviceCodeStatus {
|
|
426
|
+
pending
|
|
427
|
+
approved
|
|
428
|
+
denied
|
|
429
|
+
expired
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
model DeviceCode {
|
|
433
|
+
id String @id @default(uuid()) @db.Uuid
|
|
434
|
+
deviceCode String @unique @map("device_code")
|
|
435
|
+
userCode String @unique @map("user_code")
|
|
436
|
+
userId String? @map("user_id") @db.Uuid
|
|
437
|
+
status DeviceCodeStatus @default(pending)
|
|
438
|
+
clientInfo Json? @map("client_info")
|
|
439
|
+
scopes String[]
|
|
440
|
+
// The code's own expiry (RFC 8628 `expires_in`) — when the user/device
|
|
441
|
+
// codes stop being redeemable if never approved. Distinct from
|
|
442
|
+
// `credentialExpiresAt`, which is the lifetime of what got issued once
|
|
443
|
+
// approval happened (issue #518).
|
|
444
|
+
expiresAt DateTime @map("expires_at") @db.Timestamptz
|
|
445
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
446
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
447
|
+
|
|
448
|
+
// The PAT minted for this device once the polling client collected it
|
|
449
|
+
// (issue #518). Nullable until collection; `SetNull` so deleting the PAT
|
|
450
|
+
// (or the PAT row aging out) never cascades into deleting the device-code
|
|
451
|
+
// history. This is what lets "revoke this device session" reach the
|
|
452
|
+
// credential the device is actually holding.
|
|
453
|
+
patId String? @map("pat_id") @db.Uuid
|
|
454
|
+
|
|
455
|
+
// When the polling client (`POST /api/auth/device/token`) collected the
|
|
456
|
+
// credential above. Null until collection. Distinguishes "approved,
|
|
457
|
+
// waiting to be picked up" from "picked up and in active use" for the
|
|
458
|
+
// sessions list.
|
|
459
|
+
collectedAt DateTime? @map("collected_at") @db.Timestamptz
|
|
460
|
+
|
|
461
|
+
// The collected credential's own expiry, copied from the PAT at
|
|
462
|
+
// collection time so the sessions list can filter "still valid" without a
|
|
463
|
+
// join. Null until collection.
|
|
464
|
+
credentialExpiresAt DateTime? @map("credential_expires_at") @db.Timestamptz
|
|
465
|
+
|
|
466
|
+
// Set when this device session (and the credential it issued, if any) is
|
|
467
|
+
// revoked — from the code itself if revoked before collection, from the
|
|
468
|
+
// session-revocation endpoint after. Null means still active.
|
|
469
|
+
revokedAt DateTime? @map("revoked_at") @db.Timestamptz
|
|
470
|
+
|
|
471
|
+
// The organization the approved device session acts in (PP-6.1). Nullable
|
|
472
|
+
// (expand step); backfilled to the default organization. `Cascade`: see
|
|
473
|
+
// `RefreshToken.orgId`.
|
|
474
|
+
orgId String? @map("org_id") @db.Uuid
|
|
475
|
+
|
|
476
|
+
// Relations
|
|
477
|
+
user User? @relation("UserDeviceCodes", fields: [userId], references: [id], onDelete: Cascade)
|
|
478
|
+
org Organization? @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
479
|
+
pat PersonalAccessToken? @relation(fields: [patId], references: [id], onDelete: SetNull)
|
|
480
|
+
|
|
481
|
+
// Back-relation: refresh tokens minted through this device-authorization
|
|
482
|
+
// session (issue #518). See `RefreshToken.deviceCodeId`.
|
|
483
|
+
refreshTokens RefreshToken[]
|
|
484
|
+
|
|
485
|
+
@@index([deviceCode])
|
|
486
|
+
@@index([userCode])
|
|
487
|
+
@@index([status, expiresAt])
|
|
488
|
+
@@index([userId])
|
|
489
|
+
// Serves the sessions list (`GET /api/auth/device/sessions`, issue #518):
|
|
490
|
+
// "this user's device sessions that are not revoked" —
|
|
491
|
+
// approved-but-uncollected and collected-unexpired both live under
|
|
492
|
+
// `revokedAt IS NULL`; the remaining collected/expiry filtering happens
|
|
493
|
+
// in the query predicate, not a second index.
|
|
494
|
+
@@index([userId, revokedAt])
|
|
495
|
+
@@index([orgId])
|
|
496
|
+
@@map("device_codes")
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// =============================================================================
|
|
500
|
+
// Organizations (tenancy foundation, PP-6.1)
|
|
501
|
+
// =============================================================================
|
|
502
|
+
//
|
|
503
|
+
// Identity tables, read across organizations at login and guarded by service
|
|
504
|
+
// code: no RLS here (ADR 0002). The default organization is unique by the
|
|
505
|
+
// raw-SQL partial index `organizations_default_uniq_idx` (migration SQL only;
|
|
506
|
+
// Prisma cannot express it). A membership carries its org role (PP-6.3); an
|
|
507
|
+
// invite may name the org role the invitee will get.
|
|
508
|
+
|
|
509
|
+
enum MembershipStatus {
|
|
510
|
+
active
|
|
511
|
+
suspended
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
enum InviteStatus {
|
|
515
|
+
pending
|
|
516
|
+
accepted
|
|
517
|
+
revoked
|
|
518
|
+
expired
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// @extensible — back-relations only, added with "extend model Organization { ... }" in a fragment
|
|
522
|
+
model Organization {
|
|
523
|
+
id String @id @default(uuid()) @db.Uuid
|
|
524
|
+
name String
|
|
525
|
+
// Lower-case [a-z0-9-], 2..63 characters (validated by the service).
|
|
526
|
+
slug String @unique
|
|
527
|
+
// Exactly one row is true: organizations_default_uniq_idx.
|
|
528
|
+
isDefault Boolean @default(false) @map("is_default")
|
|
529
|
+
createdById String? @map("created_by_id") @db.Uuid
|
|
530
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
531
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
532
|
+
// App-owned, namespaced fields (`registerOrganizationMetadataSchema`, PP-14.27); same
|
|
533
|
+
// contract as `User.metadata`: validated per namespace, no secrets, 16 KiB per row
|
|
534
|
+
// (32 KiB backstop `organizations_metadata_size_check`), never returned by the
|
|
535
|
+
// platform's routes.
|
|
536
|
+
metadata Json @default("{}") @db.JsonB
|
|
537
|
+
|
|
538
|
+
// Relations
|
|
539
|
+
createdBy User? @relation("OrganizationCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
|
|
540
|
+
memberships Membership[]
|
|
541
|
+
invites Invite[]
|
|
542
|
+
refreshTokens RefreshToken[]
|
|
543
|
+
personalAccessTokens PersonalAccessToken[]
|
|
544
|
+
deviceCodes DeviceCode[]
|
|
545
|
+
// Audit history outlives the organization (`SetNull`); system events have none (#725).
|
|
546
|
+
auditEvents AuditEvent[]
|
|
547
|
+
|
|
548
|
+
// composed from package:ai.prisma
|
|
549
|
+
// AI runs and usage events (#725): see `AiRun.orgId` and `AiUsageEvent.orgId`
|
|
550
|
+
// for why one is `Restrict` and the other `SetNull`.
|
|
551
|
+
aiRuns AiRun[]
|
|
552
|
+
aiUsageEvents AiUsageEvent[]
|
|
553
|
+
|
|
554
|
+
// composed from package:credentials.prisma
|
|
555
|
+
orgCredentials OrgCredential[]
|
|
556
|
+
|
|
557
|
+
// composed from package:jobs.prisma
|
|
558
|
+
// The organization's jobs (issue #734): see `Job.orgId` for why `SetNull`.
|
|
559
|
+
jobs Job[]
|
|
560
|
+
|
|
561
|
+
// composed from package:notifications.prisma
|
|
562
|
+
notificationBroadcasts NotificationBroadcast[] @relation("BroadcastTargetOrg")
|
|
563
|
+
|
|
564
|
+
// composed from package:settings.prisma
|
|
565
|
+
settings OrgSettings?
|
|
566
|
+
|
|
567
|
+
// composed from package:sharing.prisma
|
|
568
|
+
groups Group[]
|
|
569
|
+
groupMembers GroupMember[]
|
|
570
|
+
groupInvites GroupInvite[]
|
|
571
|
+
grants Grant[]
|
|
572
|
+
|
|
573
|
+
// composed from package:storage.prisma
|
|
574
|
+
storageObjects StorageObject[]
|
|
575
|
+
storageObjectChunks StorageObjectChunk[]
|
|
576
|
+
|
|
577
|
+
// composed from app:kvox.transcripts.prisma
|
|
578
|
+
// Back-relations onto the platform's Organization (an `extend model` block holds back-relation fields only).
|
|
579
|
+
// `transcripts.org_id` (issue #485): the organization the sharing slice decides a transcript's access in.
|
|
580
|
+
transcripts Transcript[]
|
|
581
|
+
|
|
582
|
+
@@map("organizations")
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
model Membership {
|
|
586
|
+
id String @id @default(uuid()) @db.Uuid
|
|
587
|
+
orgId String @map("org_id") @db.Uuid
|
|
588
|
+
userId String @map("user_id") @db.Uuid
|
|
589
|
+
status MembershipStatus @default(active)
|
|
590
|
+
// Picks the default active organization at login (PP-6.4).
|
|
591
|
+
lastActiveAt DateTime? @map("last_active_at") @db.Timestamptz
|
|
592
|
+
// The member's org-scoped role (PP-6.3). `Restrict`: a role in use cannot be
|
|
593
|
+
// deleted out from under its members.
|
|
594
|
+
roleId String @map("role_id") @db.Uuid
|
|
595
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
596
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
597
|
+
|
|
598
|
+
// Relations
|
|
599
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
600
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
601
|
+
role Role @relation(fields: [roleId], references: [id], onDelete: Restrict)
|
|
602
|
+
|
|
603
|
+
@@unique([orgId, userId])
|
|
604
|
+
@@index([userId])
|
|
605
|
+
@@index([roleId])
|
|
606
|
+
@@map("memberships")
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
model Invite {
|
|
610
|
+
id String @id @default(uuid()) @db.Uuid
|
|
611
|
+
orgId String @map("org_id") @db.Uuid
|
|
612
|
+
// Stored lower-cased.
|
|
613
|
+
email String
|
|
614
|
+
status InviteStatus @default(pending)
|
|
615
|
+
// sha256 of an emailed accept token, if one is used.
|
|
616
|
+
tokenHash String? @unique @map("token_hash")
|
|
617
|
+
expiresAt DateTime? @map("expires_at") @db.Timestamptz
|
|
618
|
+
invitedById String? @map("invited_by_id") @db.Uuid
|
|
619
|
+
acceptedById String? @map("accepted_by_id") @db.Uuid
|
|
620
|
+
acceptedAt DateTime? @map("accepted_at") @db.Timestamptz
|
|
621
|
+
notes String?
|
|
622
|
+
// The org role the invitee gets on acceptance (PP-6.3). NULL means the
|
|
623
|
+
// default org role (`viewer`). `Restrict`, as on `Membership.roleId`.
|
|
624
|
+
roleId String? @map("role_id") @db.Uuid
|
|
625
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
626
|
+
|
|
627
|
+
// Relations
|
|
628
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
629
|
+
role Role? @relation(fields: [roleId], references: [id], onDelete: Restrict)
|
|
630
|
+
invitedBy User? @relation("InviteInvitedBy", fields: [invitedById], references: [id], onDelete: SetNull)
|
|
631
|
+
acceptedBy User? @relation("InviteAcceptedBy", fields: [acceptedById], references: [id], onDelete: SetNull)
|
|
632
|
+
|
|
633
|
+
// Re-inviting updates the row.
|
|
634
|
+
@@unique([orgId, email])
|
|
635
|
+
@@index([email])
|
|
636
|
+
@@index([roleId])
|
|
637
|
+
@@map("org_invites")
|
|
638
|
+
}
|