# How Meetily Stores Meeting Data and Transcripts in SQLite: Complete Technical Guide

> Discover how Meetily stores meeting data and transcripts in SQLite. This technical guide explains the DatabaseManager Rust struct, connection pooling, and schema migrations in the Meetily app.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-28

---

**Meetily persists all meeting-related information in a local SQLite database located in the user's application data directory, utilizing a `DatabaseManager` Rust struct in the Tauri backend to handle connection pooling, automated schema migrations, and Write-Ahead Logging (WAL) maintenance.**

Meetily, an open-source meeting transcription application developed by Zackriya-Solutions, stores all user data locally using SQLite to ensure privacy and offline functionality. The Tauri-based backend manages the database lifecycle through a centralized `DatabaseManager` that abstracts connection pooling, migration handling, and transaction safety. This article examines the exact storage mechanisms, schema evolution strategy, and data flow from audio capture to persistent storage as implemented in the repository source code.

## Database Initialization and Migration Strategy

The database lifecycle begins in [`frontend/src-tauri/src/database/manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/manager.rs), where the `DatabaseManager` struct orchestrates file creation, legacy migration, and schema versioning. When the application starts, `DatabaseManager::new_from_app_handle` constructs the full platform-specific path to `meeting_minutes.sqlite` (e.g., `~/Library/Application Support/Meetily/meeting_minutes.sqlite` on macOS), copies any existing legacy `.db` files if detected, creates the database file when missing, and then applies all pending migrations.

Migration scripts reside in `frontend/src-tauri/migrations` and are executed automatically via `sqlx::migrate!("./migrations")`. This ensures the schema matches the current code version on every startup. The migration files follow a chronological naming convention that incrementally evolves the database structure:

- **[`20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20250916100000_initial_schema.sql)** – Creates core tables including `meetings`, `transcripts`, and `speakers` with essential columns such as `id`, `title`, `start_ts`, `end_ts`, `meeting_id`, `speaker_id`, `text`, and `ts`.
- **[`20250920155811_add_openrouter_api_key.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20250920155811_add_openrouter_api_key.sql)** – Introduces a `settings` table for storing API keys.
- **[`20251006000000_add_audio_sync_fields.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251006000000_add_audio_sync_fields.sql)** – Extends the `meetings` table with audio-synchronization metadata like `audio_offset`.
- **[`20251101000000_add_summary_backup.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251101000000_add_summary_backup.sql)** – Adds a `summaries` table to persist generated meeting summaries.
- **[`20251223000000_add_meeting_notes.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251223000000_add_meeting_notes.sql)** – Creates a `meeting_notes` table linked to specific meetings for free-form note storage.
- **[`20251229000000_add_gemini_api_key.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251229000000_add_gemini_api_key.sql)** – Adds Gemini API key storage to the `settings` table.

## Connection Pooling and WAL Management

The `DatabaseManager` maintains a pooled connection via `SqlitePool::connect`, storing the pool in `self.pool` for reuse across all queries. This pooling strategy prevents connection exhaustion during high-frequency transcription inserts.

For data integrity, the manager implements Write-Ahead Logging (WAL) management routines:

- **Startup Cleanup** – On initialization, the manager checks for corrupted WAL/SHM files (`.sqlite-wal` and `.sqlite-shm`), removes them if necessary, and re-opens the database to ensure a clean state.
- **Transaction Helper** – The `with_transaction` method wraps operations in transactions that automatically commit on success or roll back on failure.
- **Shutdown Checkpoint** – The `cleanup` method executes `PRAGMA wal_checkpoint(TRUNCATE)` during application shutdown, flushing pending writes to the main database and deleting WAL files to prevent data fragmentation.

## Database Schema Structure

The schema centers on relational tables that link meeting metadata to transcription segments and derived content:

- **`meetings`** – Stores meeting metadata with columns `id`, `title`, `start_ts`, and `end_ts`.
- **`transcripts`** – Contains individual transcription segments with `id`, `meeting_id` (foreign key), `speaker_id`, `text`, and `ts` (timestamp).
- **`speakers`** – Maps speaker identifiers to display names using `id` and `name` columns.
- **`summaries`** – Persists AI-generated meeting summaries linked to specific meeting records.
- **`meeting_notes`** – Stores user-edited notes associated with meetings.
- **`settings`** – Houses configuration data including API keys for OpenRouter, Gemini, and Ollama endpoints.

The `transcripts` table maintains referential integrity through `meeting_id` and `speaker_id` relationships, enabling efficient queries that reconstruct the full conversation timeline for any given meeting.

## Data Flow from Recording to Storage

The storage pipeline operates through four distinct stages:

1. **Audio Capture** – The application captures raw microphone and system audio streams during active recording sessions.
2. **Speech Processing** – Only speech segments are extracted and sent to the Whisper transcription engine, filtering out silence or noise.
3. **Transcription Insertion** – Whisper returns text fragments that are inserted into the `transcripts` table via Tauri commands, linking each segment to the active `meeting_id` and identified `speaker_id`.
4. **Metadata Persistence** – When users create meetings or generate summaries, the frontend invokes Tauri commands that execute SQL insert statements against the SQLite pool, populating the `meetings`, `summaries`, and `meeting_notes` tables.

Queries follow a standard pattern where the frontend fetches data by invoking commands that run prepared statements like `SELECT * FROM transcripts WHERE meeting_id = ? ORDER BY ts`.

## Practical Code Examples

The following Rust patterns demonstrate how Meetily interacts with the database layer using `sqlx`:

```rust
// Initialize the database manager at application startup
let db = DatabaseManager::new_from_app_handle(&app_handle).await?;

// Insert a new meeting record
sqlx::query!(
    "INSERT INTO meetings (title, start_ts) VALUES (?, ?)",
    "Team Stand-up",
    chrono::Utc::now().timestamp()
)
.execute(db.pool())
.await?;

```

```rust
// Persist a transcription segment with speaker attribution
sqlx::query!(
    "INSERT INTO transcripts (meeting_id, speaker_id, text, ts) VALUES (?, ?, ?, ?)",
    meeting_id,
    speaker_id,
    "We need to finalize the budget.",
    chrono::Utc::now().timestamp()
)
.execute(db.pool())
.await?;

```

```rust
// Retrieve all transcripts for a specific meeting ordered by timestamp
let rows = sqlx::query!(
    "SELECT text, ts FROM transcripts WHERE meeting_id = ? ORDER BY ts",
    meeting_id
)
.fetch_all(db.pool())
.await?;

```

## Summary

- Meetily stores all data in a local SQLite file (`meeting_minutes.sqlite`) managed by the `DatabaseManager` struct in [`frontend/src-tauri/src/database/manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/manager.rs).
- Schema evolution is handled automatically through versioned migration files in `frontend/src-tauri/migrations`, utilizing `sqlx::migrate!` for deployment.
- The application uses connection pooling (`SqlitePool`) and a custom `with_transaction` helper to ensure thread-safe database access.
- Write-Ahead Logging (WAL) files are managed through startup corruption checks and shutdown checkpointing via `PRAGMA wal_checkpoint(TRUNCATE)`.
- Core tables include `meetings`, `transcripts`, `speakers`, `summaries`, `meeting_notes`, and `settings`, supporting full offline storage of meeting metadata and transcripts.

## Frequently Asked Questions

### Where does Meetily store the SQLite database file on disk?

The database file is located in the platform-specific application data directory, specifically at `~/Library/Application Support/Meetily/meeting_minutes.sqlite` on macOS, with equivalent paths under `%APPDATA%` on Windows and `$HOME/.config` on Linux. This location is constructed dynamically by `DatabaseManager::new_from_app_handle` based on the Tauri application handle.

### How does Meetily handle database schema updates when the application upgrades?

Schema updates are applied automatically on startup through `sqlx::migrate!("./migrations")`, which executes SQL scripts in `frontend/src-tauri/migrations` in chronological order. Each migration file (such as [`20251101000000_add_summary_backup.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251101000000_add_summary_backup.sql)) contains the specific DDL statements needed to evolve the schema, ensuring the database structure always matches the current application version without manual intervention.

### What tables are created in the Meetily SQLite database?

The core tables include `meetings` (meeting metadata), `transcripts` (speech-to-text segments), `speakers` (speaker identification), `summaries` (AI-generated meeting summaries), `meeting_notes` (user notes), and `settings` (API keys and configuration). These tables are defined in [`20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20250916100000_initial_schema.sql) and expanded by subsequent migration files.

### How does Meetily prevent data loss if the application crashes during a meeting?

The `DatabaseManager` utilizes Write-Ahead Logging (WAL) mode with automatic checkpointing. On startup, it removes corrupted WAL/SHM files to prevent database lock-ups. During normal operation, the `with_transaction` helper ensures atomic writes, and the `cleanup` method performs a `PRAGMA wal_checkpoint(TRUNCATE)` on graceful shutdown, forcing all pending writes from the WAL to the main database file.