# How Meetily Uses SQLite for Storing Meeting Data, Transcripts, and Summaries

> Discover how Meetily stores meeting data transcripts and summaries using SQLite in its Tauri backend. Learn about database management automated migrations and WAL.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: deep-dive
- Published: 2026-07-14

---

**Meetily persists all meeting-related information in a local SQLite database located in the user's application-data directory, managed by the `DatabaseManager` in the Tauri backend with automated migrations and WAL management.**

Meetily is an open-source meeting assistant that leverages **SQLite** as its primary data store for an offline-first, local-first architecture. The application stores all meeting metadata, transcripts, speaker information, and generated summaries in a single SQLite database file. This approach ensures user data remains on-device while providing reliable ACID compliance and efficient querying through the Tauri backend.

## Database Architecture and Location

### Local Storage Configuration

The database file, named `meeting_minutes.sqlite`, resides in the system's application data directory (e.g., `~/Library/Application Support/Meetily/` on macOS or equivalent paths on Windows/Linux). This location ensures the database persists across application updates and system restarts while respecting platform-specific data storage conventions.

### DatabaseManager Implementation

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) handles the complete database lifecycle. The constructor `DatabaseManager::new_from_app_handle` constructs the full platform-specific path, checks for legacy `.db` files that need migration, and initializes the SQLite file if absent. It creates a pooled connection using `SqlitePool::connect` that remains available for the application lifetime.

## Schema Design and Migration Strategy

### Core Tables Structure

The initial schema, defined in [`frontend/src-tauri/migrations/20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20250916100000_initial_schema.sql), establishes the foundational tables:

- **`meetings`**: Stores meeting metadata including `id`, `title`, `start_ts`, and `end_ts`
- **`transcripts`**: Contains individual text segments with `meeting_id`, `speaker_id`, `text`, and timestamp `ts`
- **`speakers`**: Maintains speaker identities with `id` and `name` fields

### Extended Data Tables

Subsequent migrations incrementally expand the schema to support additional features:

- **`summaries`**: Added via [`20251101000000_add_summary_backup.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251101000000_add_summary_backup.sql) to store AI-generated meeting summaries
- **`meeting_notes`**: Introduced in [`20251223000000_add_meeting_notes.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20251223000000_add_meeting_notes.sql) for free-form user annotations
- **`settings`**: Stores API keys (OpenRouter, Gemini) and Ollama endpoint configurations

### Automated Migration System

Meetily uses `sqlx::migrate!("./migrations")` to automatically apply pending migrations on startup. This guarantees the schema matches the current application version without manual intervention. The migration files follow timestamp-based naming conventions (e.g., [`20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20250916100000_initial_schema.sql)) to ensure deterministic execution order.

## Connection Handling and Transactions

The `DatabaseManager` provides a pooled **`SqlitePool`** (`self.pool`) for concurrent access. For atomic operations, the **`with_transaction`** helper method manages transaction boundaries, automatically committing successful operations or rolling back on failure. This ensures data consistency when inserting related records across multiple tables, such as linking a transcript entry to its meeting and speaker.

## WAL Management and Data Integrity

To prevent corruption and ensure durability, the database implements **Write-Ahead Logging (WAL)** management. On startup, `DatabaseManager` checks for corrupted WAL or SHM files, removing them if necessary before reopening the database. During shutdown, the **`cleanup`** method executes `PRAGMA wal_checkpoint(TRUNCATE)` to flush pending writes to the main database file and remove temporary WAL files, leaving the database in a clean, portable state.

## Data Flow and Implementation Examples

### Storing Meeting Metadata

When a user initiates a meeting, the application inserts a row into the `meetings` table:

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

```

### Persisting Transcript Segments

As the Whisper transcription engine produces text fragments, they are immediately stored in the `transcripts` table:

```rust
// Save a transcript fragment
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?;

```

### Querying Stored Data

The frontend retrieves data through Tauri commands that execute SQL against the SQLite pool:

```rust
// Fetch all transcripts for a meeting
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`) in the user's application data directory
- The **`DatabaseManager`** in [`frontend/src-tauri/src/database/manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/manager.rs) handles pooling, transactions, and WAL management
- Core tables include `meetings`, `transcripts`, and `speakers`, with additional tables for `summaries`, `meeting_notes`, and `settings`
- Automated migrations via `sqlx::migrate!` ensure schema consistency across application versions
- WAL checkpointing on shutdown guarantees data durability and prevents file corruption

## Frequently Asked Questions

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

Meetily stores the database at `meeting_minutes.sqlite` in the platform-specific application data directory, such as `~/Library/Application Support/Meetily/` on macOS or equivalent locations on Windows and Linux. The exact path is constructed by `DatabaseManager::new_from_app_handle` based on the Tauri application handle.

### How does Meetily handle database schema updates?

The application uses `sqlx::migrate!` to automatically apply SQL migration files located in `frontend/src-tauri/migrations` on startup. Each migration file is timestamped to ensure correct execution order, allowing the schema to evolve from the initial [`20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/20250916100000_initial_schema.sql) through subsequent updates that add tables for summaries, notes, and API keys.

### What happens if the SQLite database is corrupted or has WAL issues?

On startup, `DatabaseManager` detects and removes corrupted WAL or SHM files before opening the database. During shutdown, the `cleanup` method runs `PRAGMA wal_checkpoint(TRUNCATE)` to flush all pending writes and clean up temporary files, ensuring the database remains in a consistent state.

### Can multiple processes access the Meetily database simultaneously?

While SQLite supports multiple readers, Meetily uses a connection pool (`SqlitePool`) within the single Tauri backend process to manage access. The `with_transaction` method ensures atomic operations by wrapping related inserts in transactions, preventing data inconsistency during concurrent read/write operations within the application.