Documentation

Installation

Lector is distributed as a Docker image. The recommended way to run it is with Docker Compose.

Docker Compose

Create a docker-compose.yml file:

services:
  lector:
    image: ghcr.io/3stacks/lector:latest
    container_name: lector
    restart: unless-stopped
    ports:
      - "3400:3000"
    volumes:
      - ./data:/app/data
    environment:
      - NODE_ENV=production

Then start it:

docker compose up -d

Lector will be available at http://your-server:3400. All data is stored in a SQLite database inside the data directory.

Updating

# Pull the latest image
docker compose pull

# Restart with the new version
docker compose up -d

Your data is stored in a bind mount and will persist across updates.

Self-hosting

Lector is designed to run on your home network. Your reading history, vocabulary, and learning progress stay on hardware you control. There are no cloud accounts, no telemetry, and no external dependencies beyond optional AI translation.

Network access

Lector is designed to be accessed via a WireGuard VPN. We use Tailscale, which takes about five minutes to set up and gives every device on your tailnet a stable IP. This means your data never leaves your network.

With Tailscale, you can access Lector from your phone, from work, or from anywhere, without exposing any ports to the public internet.

Reverse proxy (HTTPS)

If you want HTTPS within your network (recommended), put Lector behind a reverse proxy. Caddy handles automatic TLS with minimal configuration:

lector.home.yourdomain.com {
    reverse_proxy lector:3000
}

Caddy will automatically provision and renew certificates. Pair this with a local DNS entry (e.g. via Pi-hole or your router) pointing lector.home.yourdomain.com to your server's IP.

Backups

Lector stores everything in a SQLite database and your imported books inside the data directory. To back it up:

# Copy the data directory
cp -r ./data ./backup-$(date +%Y%m%d)

Or include the bind mount path in your regular backup routine (rsync, borg, restic, etc.).

Reading

Lector supports two content formats:

  • EPUB files: upload directly through the library interface
  • Web articles: paste a URL and Lector extracts the content via Readability

You can also paste raw text directly for quick reading practice.

Click-to-translate

Click any word while reading to see its translation. If you have an ANTHROPIC_API_KEY configured, Lector uses Claude for context-aware translation of uncommon words and phrases. Without an API key, it falls back to a built-in dictionary of the top 2000 words.

Translation quality depends on which AI provider you have configured. See the LLM Providers section below for setup instructions.

Word states

Every word you encounter is tracked with a state:

  • New: you haven't interacted with this word yet
  • Learning: you've looked it up or saved it
  • Known: you've marked it as known

Words are highlighted in your text based on their state, so you can see at a glance how much of a passage you already know.

Cloze Practice

The cloze practice system ships with ~2900 Afrikaans-English sentence pairs from Tatoeba, ordered by word frequency so you learn common words first.

Practice modes

  • Multiple choice: pick the correct word from four options
  • Typing: type the missing word (with optional hard mode that disables hints)

SRS scheduling

Sentences are scheduled using spaced repetition with five mastery levels:

Mastery Interval
0%Immediate
25%1 day
50%3 days
75%7 days
100%14 days

LLM Providers

Lector uses a large language model for context-aware translations when you click words and phrases. You can choose between three providers in Settings → AI Provider.

A note on smaller models: 8B-parameter models (like Llama 3.1 8B via Ollama) work reasonably well for major languages but struggle with more obscure languages like Afrikaans. If translation quality is important to you, use Anthropic or Apfel.

Anthropic (cloud)

The highest-quality option. Uses Claude for context-aware translation of words, phrases, and grammar explanations.

Lector supports two authentication methods:

  • API key: Get one from console.anthropic.com. Set ANTHROPIC_API_KEY in your environment, or enter it in Settings. Pay-as-you-go billing.
  • OAuth token (Pro/Team plan): Uses your existing Claude Pro or Team subscription credits. Set CLAUDE_CODE_OAUTH_TOKEN in your environment. Note: OAuth connections have slower initial startup times compared to API keys.
  • Best translation quality, especially for less common languages
  • Requires internet access and an Anthropic account

Apfel (Apple Intelligence)

Runs Apple's on-device Foundation Model through an OpenAI-compatible API. No API keys, no cloud, no cost. Requires a Mac with Apple Silicon.

Requirements

  • macOS 26 (Tahoe) or newer
  • Apple Silicon (M1 or later)
  • Apple Intelligence enabled: System Settings → Apple Intelligence & Siri → enable

Setup

# Install
brew install Arthur-Ficial/tap/apfel

# Verify the model is available
apfel --model-info

# Start as a background service
brew services start arthur-ficial/tap/apfel

Then in Lector, go to Settings → AI Provider, select Apfel (self-hosted), and set:

  • Server URL: http://localhost:11434
  • Model: apple-foundationmodel

Click Test Connection to verify. You should see a green "Connected" status.

Apfel is significantly stronger than local 8B Ollama models for translation quality, and runs entirely on-device with no API keys or internet required. If you don't have an Anthropic API key, Apfel is the best local option for Mac users.

Ollama (local)

Runs open-source models locally via Ollama. No API keys or cloud required, but translation quality depends heavily on model size.

  • Install Ollama and pull a model: ollama pull llama3.1:8b
  • Select a model in Lector's settings
  • Works on any platform (macOS, Linux, Windows)

Quality warning: 8B models produce noticeably weaker translations for less common languages. For Afrikaans and similar languages, prefer Anthropic or Apfel if available.

AnkiConnect

Lector connects directly to AnkiConnect running on your local machine. This lets you push vocabulary cards from Lector straight into Anki Desktop. No proxy server, no cloud sync.

Setup

  1. Install the AnkiConnect add-on in Anki Desktop
  2. In AnkiConnect's config, add your Lector origin to the CORS allowlist:
{
  "webCorsOriginList": ["http://localhost:3000"]
}

If you're accessing Lector via a reverse proxy, add that origin too (e.g. https://lector.home.yourdomain.com).

Note: AnkiConnect runs on localhost:8765 by default. The connection is browser-to-localhost, so Anki Desktop must be running on the same machine as your browser.

Configuration

Environment variables

Variable Default Description
NODE_ENV production Set to production for deployed instances
LLM_PROVIDER anthropic AI provider for translations: anthropic, apfel, or ollama
ANTHROPIC_API_KEY Claude API key (only needed when LLM_PROVIDER=anthropic)
CLAUDE_CODE_OAUTH_TOKEN OAuth token for Pro/Team plan credits (alternative to API key, slower startup)
APFEL_URL http://localhost:11434 Apfel server URL (only needed when LLM_PROVIDER=apfel)
OLLAMA_URL http://localhost:11434 Ollama server URL (only needed when LLM_PROVIDER=ollama)
OLLAMA_MODEL llama3.1:8b Ollama model name
GOOGLE_CLOUD_API_KEY Optional. Google Cloud API key for text-to-speech pronunciation.

Full Docker Compose example

Here's a complete example with all optional environment variables:

services:
  lector:
    image: ghcr.io/3stacks/lector:${LECTOR_VERSION:-latest}
    container_name: lector
    restart: unless-stopped
    ports:
      - "3400:3000"
    volumes:
      - ./data:/app/data
    environment:
      - NODE_ENV=production
      - LLM_PROVIDER=${LLM_PROVIDER:-anthropic}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
      - APFEL_URL=${APFEL_URL}
      - OLLAMA_URL=${OLLAMA_URL}
      - OLLAMA_MODEL=${OLLAMA_MODEL}
      - GOOGLE_CLOUD_API_KEY=${GOOGLE_CLOUD_API_KEY}

Supported languages

Lector's reader works with any language, but language packs provide sentence banks and dictionaries for cloze practice.

Language pair Status Sentences
Afrikaans → English Available ~2900
Spanish → English Coming soon
German → English Coming soon

All sentence data is sourced from Tatoeba. Adding a new language pair is straightforward. If you'd like to see your language supported, open a PR on GitHub. We'd love contributions.

Sentence bank

The cloze practice system ships with ~2900 Afrikaans-English sentence pairs sourced from Tatoeba. Each sentence is tagged with word frequency data so you learn the most common words first.

To regenerate from Tatoeba's latest data dumps:

npm run fetch-sentences

This downloads Tatoeba's per-language TSV exports, joins Afrikaans sentences with English translations, and tags each with word frequency data.

Data attribution: Sentence bank sourced from Tatoeba, licensed under CC-BY 2.0 FR. Word frequency dictionary compiled from publicly available frequency lists.

Tech stack

  • Framework: Next.js 16 with React 19
  • Database: SQLite (better-sqlite3) server-side, Dexie (IndexedDB) client-side
  • Styling: Tailwind CSS v4
  • AI: Anthropic Claude, Apple Intelligence (Apfel), or Ollama (all optional)
  • TTS: Google Cloud Text-to-Speech (optional)
  • Container: Docker (single image, bind mount for data)