Mailpilot

Database Schema

Mailpilot uses SQLite for persistent storage. The database path is configurable via state.database_path (default: ./data/mailpilot.db).

Tables

processed_messages

Tracks which emails have been processed to prevent duplicate processing.

ColumnTypeDescription
idINTEGERPrimary key (auto-increment)
message_idTEXTEmail Message-ID header
account_nameTEXTAccount that processed this email
processed_atINTEGERUnix timestamp of processing

Indexes:

  • idx_processed_messages_account - For filtering by account
  • idx_processed_messages_processed_at - For TTL cleanup

Unique Constraint: (message_id, account_name)

Retention: Configurable via state.processed_ttl (default: 24h)


audit_log

Records all actions taken on emails for auditing and debugging.

ColumnTypeDescription
idINTEGERPrimary key (auto-increment)
message_idTEXTEmail Message-ID header
account_nameTEXTAccount that processed this email
actionsTEXTJSON array of actions taken
llm_providerTEXTLLM provider used (nullable)
llm_modelTEXTLLM model used (nullable)
subjectTEXTEmail subject (nullable, opt-in)
confidenceREALLLM confidence score 0.0-1.0 (nullable)
reasoningTEXTLLM reasoning for the classification (nullable)
created_atINTEGERUnix timestamp of processing

Indexes:

  • idx_audit_log_account - For filtering by account
  • idx_audit_log_created_at - For TTL cleanup and sorting

Retention: Configurable via state.audit_retention (default: 30d)

Privacy Note: The subject column is only populated if state.audit_subjects is set to true.


dashboard_users

Stores dashboard admin accounts.

ColumnTypeDescription
idINTEGERPrimary key (auto-increment)
usernameTEXTUnique username
password_hashTEXTbcrypt hashed password
created_atINTEGERUnix timestamp of account creation

Unique Constraint: username


dashboard_sessions

Manages authenticated dashboard sessions.

ColumnTypeDescription
idTEXTSession ID (primary key)
user_idINTEGERForeign key to dashboard_users.id
created_atINTEGERUnix timestamp of session creation
expires_atINTEGERUnix timestamp when session expires

Foreign Key: user_id references dashboard_users(id) with ON DELETE CASCADE

Indexes:

  • idx_dashboard_sessions_expires - For cleanup of expired sessions
  • idx_dashboard_sessions_user - For looking up user sessions

Session TTL: Configurable via dashboard.session_ttl (default: 24h)


dead_letter

Stores emails that failed processing for later retry.

ColumnTypeDescription
idINTEGERPrimary key (auto-increment)
message_idTEXTEmail Message-ID header
account_nameTEXTAccount the email belongs to
folderTEXTIMAP folder containing the email
uidINTEGERIMAP UID of the email
errorTEXTError message from failed processing
attemptsINTEGERNumber of processing attempts (default: 1)
created_atINTEGERUnix timestamp when first failed
resolved_atINTEGERUnix timestamp when resolved (nullable)
retry_statusTEXTStatus: pending, retrying, exhausted, success, skipped
next_retry_atINTEGERUnix timestamp for next retry attempt (nullable)
last_retry_atINTEGERUnix timestamp of last retry attempt (nullable)

Indexes:

  • idx_dead_letter_account - For filtering by account
  • idx_dead_letter_resolved - For finding unresolved entries
  • idx_dead_letter_retry - For finding entries due for retry

Retry Status Values:

  • pending - Waiting for next retry attempt
  • retrying - Currently being retried
  • exhausted - Max retry attempts reached
  • success - Successfully processed after retry
  • skipped - User manually skipped retries

Resolution: Entries are marked resolved (not deleted) when successfully retried, exhausted, or manually skipped.


Database Configuration

state:
  database_path: ./data/mailpilot.db  # SQLite database location
  processed_ttl: 24h                  # How long to remember processed message IDs
  audit_retention: 30d                # How long to keep audit log entries
  audit_subjects: false               # Store email subjects (privacy tradeoff)

Pragmas

The database is initialized with:

  • journal_mode = WAL - Write-Ahead Logging for better concurrent performance
  • foreign_keys = ON - Enforce foreign key constraints

Next Steps