Browse docs

Data Model

The current SQLite schema and the state stored outside it

Alex stores accounts, indexed book metadata, reading progress, collections, and its library change counter in SQLite. Checked-in SQL migrations are the schema source of truth; the web app accesses the database through short-lived watcher-rs db commands.

Entity relationship diagram

Rendering diagram...

Timestamps are Unix seconds.

Tables

users

ColumnMeaning
idPrimary key. Normal users use UUIDs; the seeded desktop admin uses "1".
emailUnique login identifier.
password_hashbcrypt password hash.
display_nameName displayed in the UI.
roleadmin or user.
created_at, updated_atAccount timestamps.

pnpm db:seed upserts admin@localhost / admin123 as an admin. Re-running it resets the seeded account fields, including the password. Electron inserts the same account only when it is missing.

books

ColumnMeaning
idUUID primary key.
title, author, descriptionExtracted metadata with filename fallback for the title.
file_typepdf or epub.
file_pathUnique absolute path for local books or object key for S3 books.
file_sizeBytes.
file_hashUnique SHA-256 content hash.
cover_pathNullable path to a local JPEG cover.
page_countPDF page count; normally null for EPUB.
added_at, updated_atIngestion timestamps.
sourcelocal or s3; defaults to local.
s3_bucket, s3_etagBucket identity and polling ETag for S3 rows.

Unique path/key and hash indexes prevent duplicate source entries and duplicate content.

reading_progress

ColumnMeaning
idUUID primary key.
user_id, book_idReader and book foreign keys.
current_page, total_pagesPDF position.
epub_locationEPUB CFI position.
percent_completePercentage from 0 through 100.
statusnot_started, reading, or completed.
last_read_atLast saved position time.

Application code maintains one logical row per user/book, although the current migration does not declare (user_id, book_id) unique. Deleting a book cascades to its progress rows.

collections

ColumnMeaning
idUUID primary key.
user_idOwner foreign key.
name, descriptionCollection metadata. Route validation limits names to 100 characters.
share_tokenNullable, unique UUIDv4 public token.
shared_at, created_atSharing and creation timestamps.

Revoking sharing clears the token and timestamp. Re-enabling sharing later generates a new token.

collection_books

The (collection_id, book_id) composite primary key represents membership and prevents duplicates. added_at records when the membership was created. Deleting a book cascades to membership rows; collection deletion removes its memberships explicitly before deleting the collection.

settings

The key/value table currently stores library_version. Ingestion updates it whenever indexed books change, and the Server-Sent Events endpoint polls it to notify connected clients.

Current indexes and foreign keys

The migrations declare unique indexes for user email, book path, book hash, and collection share token, plus table primary keys. They do not declare separate title, author, progress-status, or foreign-key indexes.

Book foreign keys in reading_progress and collection_books use delete cascade. User foreign keys and collection_books.collection_id use SQLite's default no-action behavior.

Schema management

The schema files are:

  • 0000_wide_expediter.sql — Initial six tables
  • 0001_s3_source_columns.sql — Source, bucket, and ETag columns on books

pnpm db:push creates the schema when needed, repairs unique book indexes on older databases, and applies the S3 columns. Electron performs equivalent startup checks. Alex does not use a runtime ORM or migration-history table.

State outside SQLite

  • Book files live in LIBRARY_PATH or the configured S3 bucket.
  • Cover JPEGs live in COVERS_PATH.
  • Authenticated EPUB scroll restoration is also cached in browser storage as epub-progress:${bookId}.
  • Public reader progress is browser-only at shared-progress:${token}:${bookId}.
  • EPUB font size is saved as epub-reader-settings.
  • Desktop storage, window, secret, and relay settings live in Electron's user-data config.json.