Data model
Schema highlights and the load-bearing migrations.
Conclave persists everything in one SQLite file inside the TEE (relational + FTS5 + sqlite-vec
vectors), managed by SQLAlchemy 2 + Alembic. Alembic auto-applies on boot. VFTEE keeps its own sealed
SQLite voiceprint store.
Key tables (conceptual)
transcript_sessions— the meeting:owner_user_id,recorder_user_id,owner_only,created_at,metadata(source/platform/origin),enrichment_state.raw_diarization— write-once span-level truth{start, end, local_speaker}. Never mutated; edits and identity live in overlays.transcript_v2+vocab— the editable, span-annotated doc (draft → approved) + personal vocabulary.live_segments— append-only live stream for the SSE view.resolved_speakers— the identity overlay{label: {voiceprint_id, name, confidence}}.chunks+ FTS5 + embeddings; entities / facts / obligations — bi-temporal (valid_to/superseded_by).workspaces/workspace_members/workspace_invites/meeting_workspace_shares— the membership + sharing graph.transcript_versions+version_events— the immutable version chain (#3): each approved edit appends a sealed version (via the#21seal signer);version_eventsrecords who/what/when.personal_access_tokens(hashedconclave_pat_…) +oauth_clients— the external agent surface (workspace PATs + OAuth "Connect with Conclave").magic_links, audio-store metadata, feedback, T&C acceptance.
Load-bearing migrations
| Migration | What it introduced |
|---|---|
0002 | workspaces / members / shares / magic_links |
0007 | entities / facts / obligations |
0012 → 0024 | share scope enum → per-artifact {transcript, insights, audio} flags |
0013 | retention |
0016 | live_segments |
0018 | transcript_v2 + vocab |
0022 | audio-store |
0023 | in-person agenda |
0025 | user T&C |
0027 | workspace_invites + meeting_workspace_shares + inperson_recorder + recorder_user_id / owner_only (#32) |
0028 | workspaces.type column — personal vs team split |
0029 | data backfill: re-point in-person session ownership from workspace-creator → actual recorder |
0032 | workspace PATs (hashed conclave_pat_…) — external read API |
0034 | OAuth 2.1 client registry ("Connect with Conclave") |
0035 | re-key speaker_tags onto the roster slot (slot_ref, #23/#124) — not additive: snapshot before deploying |
0036 | transcript_versions + version_events (#3) |
0037 | one-time backfill: an imported v1 for every already-published session (INSERT-only, #3) |
The raw / derived split is load-bearing:
raw_diarizationis immutable and never serialized unless authorized; everything else (overlays, v2, insights, KB) is derived and re-derivable.
Worked example: the sharing scopes (how to add one)
Sharing is per-artifact booleans, not an enum (that was the 0012 → 0024 change). Concretely:
- Columns:
share_transcript,share_insights,share_audioon the share record. - Model:
ShareConfigininfra/workspaces.pyis the single place a share's booleans are built. - Enforcement gates:
can_see_transcript/can_see_insights/can_see_audioinapi/transcripts_routes.pydecide what a viewer's response includes (insights are independently withholdable).
To add a new scope (e.g. "share attachments"): add the boolean column via a new Alembic migration
(mirror 0024), extend ShareConfig, add a can_see_attachments gate alongside the others, and
surface it in the share UI. This is the #31 pattern end to end.