Skip to main content
Version: v5.0.0

Local Development

Prerequisites​

Node.js + pnpm (required)
brew install nvm pnpm
mkdir -p ~/.nvm
echo 'export NVM_DIR="$HOME/.nvm"' >> ~/.zshrc
echo '[ -s "$(brew --prefix nvm)/nvm.sh" ] && . "$(brew --prefix nvm)/nvm.sh"' >> ~/.zshrc
source ~/.zshrc
nvm install --lts
nvm use --lts
node -v
pnpm -v
SeaweedFS weed binary (required unless using external S3)
brew install seaweedfs
weed version
SeaweedFS Compatibility Note (April 16, 2026)

If you see intermittent S3 InternalError upload failures with embedded storage, use SeaweedFS 4.18. OpenReader currently pins 4.18 in CI and Docker builds while 4.19 compatibility is investigated.

NATS Server nats-server (required for embedded compute mode)

If COMPUTE_WORKER_URL is unset, startup launches embedded compute worker + NATS, so nats-server must be available on host PATH.

If you always use an external worker (COMPUTE_WORKER_URL set), this is not required.

brew install nats-server
nats-server -v
LibreOffice (optional, for DOCX conversion)
brew install libreoffice
Word-by-word highlighting (optional)

No extra native Whisper CLI build step is required.

Word-by-word highlighting and PDF layout parsing are worker-backed in current releases.

If you need mirrors or pinned artifact locations, set WHISPER_MODEL_BASE_URL in .env (current defaults expect q4 Whisper files at that base URL).

Docker Compose

To run OpenReader and Kokoro-FastAPI with Docker Compose, including slim, full, and local-build options, see Docker Compose.

Steps​

Required flow​

  1. Clone the repository.
git clone https://github.com/richardr1126/openreader.git
cd openreader
  1. Install dependencies.
pnpm i
  1. Configure the environment.
cp .env.example .env

Then edit .env.

Default embedded worker flow (no external worker URL):

# Leave COMPUTE_WORKER_URL unset.
# Entry point auto-starts embedded worker+NATS when available.
TTS_PLAYBACK_TOKEN_SECRET=local-tts-playback-token-secret

External worker flow:

COMPUTE_WORKER_URL=http://localhost:8081
# Only needed when browsers cannot reach COMPUTE_WORKER_URL directly.
# COMPUTE_WORKER_PUBLIC_URL=http://localhost:8081
COMPUTE_WORKER_TOKEN=<same-token-used-by-worker>
COMPUTE_CREDENTIAL_BROKER_TOKEN=<same-broker-token-used-by-worker>
TTS_PLAYBACK_TOKEN_SECRET=<same-secret-used-by-worker>

Use the same ownership split:

  • root .env: app routing/auth (AUTH_SECRET, COMPUTE_WORKER_URL, COMPUTE_WORKER_PUBLIC_URL, COMPUTE_WORKER_TOKEN, COMPUTE_CREDENTIAL_BROKER_TOKEN, TTS_PLAYBACK_TOKEN_SECRET) plus embedded-worker tuning
  • compute-worker/.env* (or worker platform env): worker runtime variables (NATS_*, S3_*, model base URLs, worker tuning), COMPUTE_CREDENTIAL_BROKER_URL, and the matching compute/broker/playback tokens
  • AUTH_SECRET, POSTGRES_URL, and SQLITE_DB_PATH remain app-only; the worker resolves enabled providers through the credential broker

Use one of these .env mode templates:

API_BASE=http://127.0.0.1:8880/v1
API_MODEL_NAME=kokoro
BASE_URL=http://localhost:3003
AUTH_SECRET=<generate-with-openssl-rand-base64-32>
TTS_PLAYBACK_TOKEN_SECRET=local-tts-playback-token-secret
# Optional when you need multiple local origins:
# AUTH_TRUSTED_ORIGINS=http://localhost:3003,http://127.0.0.1:3003
Env vars vs. admin panel

On first boot, API_KEY / API_BASE / API_MODEL_NAME can bootstrap default-openai, and RUNTIME_SEED_JSON / RUNTIME_SEED_JSON_PATH can seed runtime config, providers, and (when supplied) account email delivery. After that, the admin UI is authoritative and editing bootstrap env vars no longer changes app behavior. See Admin Panel.

TTS credentials

Configure TTS credentials in Settings → Admin → Shared providers. User browsers never supply provider credentials.

info

For all environment variables, see Environment Variables.

See Auth for app/auth behavior. See Admin Panel for the shared-provider and feature-flag management UI. Storage configuration details are in Object / Blob Storage. Refer to Database for database modes. Learn about migration behavior and commands in Migrations.

Scheduled maintenance tasks

Local and self-hosted Node.js deployments start the scheduled-task loop in-process and check for due work once per minute. No CRON_SECRET is required unless you intentionally invoke the cron HTTP route yourself. Manage task intervals and inspect failures from Settings → Admin → Scheduled tasks.

  1. Start the app.
pnpm dev

If you use embedded worker startup (no COMPUTE_WORKER_URL) and the host is missing nats-server, install nats-server locally or switch to external worker mode.

Provider reachability

Background speech generation runs in the compute worker, while optional custom voice discovery runs in the app server. A self-hosted provider base URL must therefore be reachable from both runtimes. For native pnpm dev/pnpm start with the embedded worker, http://127.0.0.1:<port>/v1 is correct. If the worker is remote, configure a URL reachable from that host as well.

Visit http://localhost:3003.

Optional workflows​

Run manual DB migrations only for troubleshooting or explicit migration workflows:

  • Migrations run automatically on startup through the shared entrypoint for both pnpm dev and pnpm start.
pnpm migrate
info

If POSTGRES_URL is set, migrations target Postgres; otherwise local SQLite is used. To disable automatic startup migrations, set RUN_DRIZZLE_MIGRATIONS=false and/or RUN_V4_DECOMMISSION=false. You can run the idempotent v4 legacy storage decommission manually with pnpm migrate-decommission. See Migrations before a production v4.4→v5 upgrade.