Skip to main content
Version: Next

Database

This page covers database mode selection for OpenReader.

Scope of this page

  • Focus: SQL metadata, state, and relational tables.
  • Not covered here: object key layout and blob transport details (see Object / Blob Storage).

Database mode

  • SQLite (default): embedded DB at docstore/sqlite3.db; good for local/self-host single-instance setups.
  • Postgres: enabled when POSTGRES_URL is set; recommended for production/distributed deployments.

What the database stores

  • Document and audiobook metadata/state used by server routes.
  • Auth/session tables (user, session, account, verification) when auth is enabled — schema is auto-generated by Better Auth.
  • TTS character usage counters (user_tts_chars) for daily rate limiting (when enabled).
  • User settings preferences (user_preferences) when auth is enabled.
  • User reading progress (user_document_progress) when auth is enabled.
  • Document preview job/asset metadata (document_previews) for server-side PDF/EPUB thumbnails.
  • TTS segment metadata (tts_segments) for server-side playback caching:
    • Segment identity + settings hash
    • Audio object key and duration
    • Optional alignment payload for word highlighting
    • Status/error state
    • Text fingerprint/hash (plaintext segment text is not stored)

App-specific tables are manually maintained in Drizzle schema files, while auth tables are generated by the Better Auth CLI. Both are migrated together via Drizzle. See Migrations for details.

What the database does not store

  • Raw document file bytes
  • Audiobook audio bytes
  • TTS segment audio bytes
  • Generated preview image bytes

Those payloads live in object storage. SQL stores the metadata, references, and status.

  • POSTGRES_URL

For database variable behavior, see Environment Variables.

State sync summary

  • With auth enabled, settings and reading progress are stored in SQL and synced from the app.
  • With auth disabled, settings and reading progress remain local in the browser.
  • Sync is currently request-based (not realtime push invalidation).