Contributing to Reader

May 13, 2026 · View on GitHub

Thanks for your interest in contributing. This is the open source branch of the codebase that runs at https://r.jina.ai and https://s.jina.ai. The MongoDB-backed SaaS storage layer is not part of this branch — local development uses the stateless / bucket-cached modes only.

If you're not sure where to start, take a look at architecture.md first.

Local development

Requirements

  • Node.js 22+ — earlier versions will not build.
  • Docker (optional) — only needed if you want to run the bucket-cached storage mode against a local MinIO. Pure stateless mode needs nothing extra.
  • LibreOffice (optional) — only needed if you want to test MS Office document handling locally.

First-time setup

git clone git@github.com:jina-ai/reader.git
cd reader
npm install
# Optional: only if you want the local bucket cache
docker compose up -d

docker compose up -d starts:

ServicePort(s)Purpose
minio9000, 9001S3-compatible object storage for cached pages. Console on :9001.

Running the server

In VSCode, press F5 to launch the debugger.

Or, after exporting the environment variables (see below):

docker compose up -d
npm run dev

Useful scripts

  • npm run build — TypeScript compile (also runs an integrity check).
  • npm run build:watch — incremental build.
  • npm run start — run the compiled crawl entrypoint.
  • npm run dry-run — run search.js with NODE_ENV=dry-run to resolve the DI graph and exit. Used to warm NODE_COMPILE_CACHE in the Dockerfile.
  • npm run lint — ESLint over .js / .ts.

Environment variables

Reader picks up configuration from environment variables. The most relevant ones for local development are:

Storage & data

VariableNotes
GCP_STORAGE_ENDPOINTObject storage endpoint (use the local MinIO endpoint for dev). Enables Stage 1 bucket-cached mode.
GCP_STORAGE_BUCKETBucket name for cached objects.
GCP_STORAGE_ACCESS_KEYMinIO root user locally.
GCP_STORAGE_SECRET_KEYMinIO root password locally.
GCP_STORAGE_REGIONOptional; for parity with GCS.
GCLOUD_PROJECTAlternative trigger for the bucket layer when combined with GCP_STORAGE_ENDPOINT.
CACHE_LOCAL_STORAGE_ROOTFilesystem root for local cache (alternative to object storage in stateless modes).

Vendors & integrations

VariablePurpose
JINA_SERP_API_KEY / JINA_SERP_API_ORIGINJina SERP backend.
JINA_SERP_API_POLICYSERP routing policy.
SERPER_SEARCH_API_KEYserper.dev search backend.
THORDATA_PROXY_URL / THORDATA_PROXY_URL_ALTThordata residential proxy.
THORDATA_SERP_API_KEYThordata SERP API.
BRIGHTDATA_PROXY_URL / BRIGHTDATA_ISP_PROXY_URL / BRIGHTDATA_SERP_API_KEYBrightData proxy + SERP.
CLOUD_FLARE_API_KEYRequired for the cf-browser-rendering engine.
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_KEYBilling integration.
OPENAI_API_KEY / ANTHROPIC_API_KEY / OPENROUTER_API_KEY / GOOGLE_AI_STUDIO_API_KEY / REPLICATE_API_KEYLLM/VLM access.

Overrides & toggles

VariablePurpose
PORTHTTP port.
NODE_ENVdry-run is recognized for offline search runs.
DEBUG_BROWSERRun headless Chrome with non-headless / debug behavior.
OVERRIDE_CHROME_EXECUTABLE_PATHUse a specific Chrome binary instead of the bundled Puppeteer one.
OVERRIDE_JINA_VLM_URLPoint at a different VLM endpoint.
OVERRIDE_READERLM_VSwitch between ReaderLM versions.
OVERRIDE_GOOGLE_DOMAIN / OVERRIDE_BING_DOMAINUse a regional search domain.
OVERRIDE_MANAGE_SERVER_URLRedirect calls to the management server.
JINA_BOGO_SITES_RESORT_ORIGINOrigin for the bogo-sites resort list.
JINA_CRAWLER_OFFLOAD_ORIGINOffload crawler traffic to a peer cluster.
PREFERRED_PROXY_COUNTRYHint for proxy country selection.
SLACK_REPORT_WEBHOOK_URLSlack channel for runtime reports.

SECRETS_COMBINED

You can pass a base64-encoded JSON object via SECRETS_COMBINED to bundle multiple variables into one. See src/services/envconfig.ts.

Tests

The repo uses the Node.js built-in test runner (no Jest, no Vitest).

npm run test:unit       # unit tests
npm run test:e2e        # end-to-end tests (slower, hits docker services)
npm test                # both

npm run test:unit:coverage
npm run test:e2e:coverage
npm run test:coverage   # combined coverage report (c8)

Tests are written in TypeScript under tests/ and compiled into tests-build/ before running. The test entrypoints are tests-build/run-unit.js and tests-build/run.js.

Submitting changes

  1. Open an issue first if the change is non-trivial — it saves churn for both sides.
  2. Keep PRs focused. A bug fix and a refactor in the same PR are harder to review and revert.
  3. Run npm run lint and npm test before pushing.
  4. Reference the issue in the PR description if one exists.

Reporting issues

Bug reports are most useful when they include:

  • The exact URL (for r.jina.ai) or query (for s.jina.ai).
  • The request headers in use, especially any x-* overrides.
  • The expected vs actual output.

Open an issue on GitHub and we'll take a look.