Architecture
System overview, components, and technology stack
This page describes Alex's runtime architecture for contributors and operators.
System overview
Alex has five major runtime pieces:
- Next.js application - Serves the UI and API routes.
- Rust runtime (
watcher-rs) - Handles ingestion, metadata extraction, cover generation, database bridge commands, and tunnel client. - SQLite database - Stores users, books, progress, collections, and settings.
- Storage backend(s) - Local folder or S3-compatible bucket, selected by configuration.
- Relay service (optional) - Reverse tunnel (
alex-relay) for public access without port forwarding.
Architecture diagram
Rendering diagram...
Core components
Next.js application
The Next.js app is the control plane for UI and HTTP APIs.
Key routes include:
/library- Main grid with search, filters, sort/read/[bookId]- Reader route (PDF or EPUB)/collectionsand/collections/[id]- Collection management/shared/[token]- Public collection view/admin/*- User and library administration/setupand/onboarding- First-run setup flows
Authentication (dual system)
Alex uses two authentication mechanisms that can coexist on the same server:
- Web auth (NextAuth.js v5) - Credential-based email/password login with JWT sessions (30-day expiry). Two NextAuth instances share the same secret and pinned cookie configuration:
- Full config - Used by API route handlers; includes credential provider and DB queries
- Middleware config - Lightweight Edge-runtime instance; JWT decode only, no providers
- Desktop auth - When
ALEX_DESKTOP=true, Electron generates a per-session random token (ALEX_DESKTOP_AUTH_TOKEN) and injects it via thex-alex-desktop-authheader on all requests. Middleware validates this header and grants a synthetic admin session.
Cookie names are pinned (with __Secure- prefix when NEXTAUTH_URL is HTTPS) to prevent mismatches behind reverse proxies. Middleware respects X-Forwarded-Host and X-Forwarded-Proto headers for correct redirects behind the relay.
Source-aware file serving
All book-byte endpoints use the same source pipeline:
/api/books/[id]/file/api/books/[id]/book.epub/api/shared/[token]/books/[bookId]/file/api/shared/[token]/books/[bookId]/book.epub
The server reads books.source and dispatches to the correct driver:
local- stream from disk with range supports3- stream fromwatcher-rs s3-stream
Rust runtime (watcher-rs)
watcher-rs runs independently from the Next.js server and has four responsibilities:
- Ingestion engine
- Local mode: recursive file events via
notify - S3 mode: poll + diff object keys
- Handlers for add/change/delete update the DB and covers
- Metadata and cover generation
- PDF metadata via
lopdf - EPUB metadata via
zip+quick-xml - Covers via
pdfium-render, EPUB extraction, or fallback generation
- Bridge commands for Next.js
watcher-rs dbfor SQL execution (query-all,query-one,execute)watcher-rs s3-streamfor object streaming
- Tunnel client
- Connects to the relay service via binary WebSocket (
tokio-tungstenite) - Forwards HTTP requests from the relay to the local Next.js server
- Uses
bincodefor efficient binary serialization of request/response frames
Alex Relay (alex-relay)
The relay is an optional standalone Rust service that enables public access to a desktop Alex instance without port forwarding or dynamic DNS.
- Built on Axum with WebSocket upgrade for tunnel connections
- Uses binary WebSocket frames carrying serialized HTTP request/response pairs (
bincode+serde) - Incoming requests to
relay.alexreader.appare proxied through the tunnel to the desktop app's local server - Preserves
Set-Cookieand other response headers; injectsX-Forwarded-HostandX-Forwarded-Proto - Key modules:
relay.rs(connection management),proxy.rs(request proxying),protocol.rs(frame types)
SQLite database
The DB is a single file (typically library.db) with tables for:
usersbooksreading_progresscollectionscollection_bookssettings(library_versiondrives SSE updates)
Data flows
Book ingestion
- Runtime selects local or S3 mode.
watcher-rsdetects adds/changes/deletes.- It computes hashes, extracts metadata, and generates covers.
- It writes
booksrows (including source metadata for S3 records). - It increments
settings.library_version. /api/library/eventspushes update notifications.
Reading
- User opens a book.
- API returns metadata + saved progress.
- Reader requests file bytes.
- Source driver streams from local disk or S3.
- Progress updates are persisted as the user reads.
Public sharing
- Owner enables sharing for a collection.
- A tokenized URL is generated.
- Public endpoints validate collection membership per request.
- Public readers store progress in browser local storage (not server-side).
Relay tunnel (desktop only)
- Electron spawns the tunnel daemon alongside the watcher and Next.js server.
- Tunnel client connects to
wss://relay.alexreader.app/_tunnel/ws. - Incoming HTTP requests are serialized as
bincodeframes and forwarded through the WebSocket. - Tunnel client deserializes requests and proxies them to
localhost:3210. - Responses (including
Set-Cookieheaders) are forwarded back through the tunnel. - Relay injects forwarded headers so Next.js generates correct URLs.
Deployment architectures
Development
- Next.js dev server (
pnpm dev) watcher-rsin separate terminal (pnpm watcher)- Rust toolchain required to build watcher binary
Docker
- Single container with Next.js + bundled
watcher-rs - Supports local or S3 mode via environment config
- Persistent
/app/datavolume for DB + covers - Runs as non-root
nodeuser
Electron desktop
- Embedded Next.js server on
localhost:3210 - Bundled
watcher-rsspawned by Electron - Storage mode selectable in onboarding/admin (
Local FolderorS3 / R2 Bucket) - Per-session desktop auth token injected via
x-alex-desktop-authheader - Optional relay tunnel for public access without port forwarding
- Persistent config via
electron-store(library path, S3 settings, relay preferences)
Technology stack
| Layer | Technology |
|---|---|
| Frontend | React 19, Next.js 16 |
| Styling | Tailwind CSS v4, shadcn/ui |
| Authentication | NextAuth.js v5 (dual-instance: full config + Edge middleware); desktop header-based auth |
| Database | SQLite via Rust (rusqlite) bridge |
| Ingestion | Rust notify (local) or S3 poller (S3 mode) |
| File serving | Source-driver pipeline |
| PDF rendering | PDF.js |
| EPUB rendering | epub.js + react-reader |
| Metadata extraction | lopdf, quick-xml, zip (Rust) |
| Cover generation | pdfium-render + fallback renderer (Rust) |
| Real-time | Server-Sent Events (SSE) |
| Relay | alex-relay (Axum + WebSocket tunnel); watcher-rs tunnel client |
| Desktop | Electron 34, electron-store, system tray |
| Testing | Playwright (E2E, web + Electron), Jest (unit), Storybook + Chromatic (visual) |
| CI/CD | GitHub Actions (CI, Docker release, Electron release, E2E, Chromatic, code review) |
| Deployment | Docker, Electron, Node.js 22 |