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. VFTE 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.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 + recorder_user_id / owner_only (#32) |
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.
Source: FEATURE-SPEC.md §2.6 & §2.12, conclave-shape-rotator/alembic/, infra/workspaces.py, api/transcripts_routes.py.