Self-hosting
Run lurq against your own Postgres index.
Everything here runs against your own lurq Postgres index and needs a
DATABASE_URL. It's for operators running the hosted service and for
self-hosters, not for end users, who only run lurq setup
(Quick start).
The two planes
The commands that build an index are not in the published npm package.
lurqrun ships as a read-only oracle: it can query an index, extract API
surfaces, scan a codebase, and serve MCP. Everything that ingests, crawls,
mines, runs a sandbox, issues keys, or migrates the schema lives on a separate
operator binary that is deliberately excluded from the published files, so
ingestion code and its heavy dependencies never land on a user's machine.
To run the operator plane, clone the repo:
git clone https://github.com/jadenryu/lurq && cd lurq
npm install
npm run operator -- <command> # or: npx tsx src/bin/operator.ts <command>A built checkout can also run node dist-operator/bin/operator.js <command>,
which is what the production cron jobs use.
Configuration
Only DATABASE_URL is required; everything else degrades gracefully.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL | yes | Postgres connection (pgvector-enabled) |
GITHUB_TOKEN | recommended | GitHub signals (stars, cadence, issues, archived) |
EMBEDDING_PROVIDER / EMBEDDING_API_KEY / EMBEDDING_BASE_URL | no | Any OpenAI-compatible /v1/embeddings; falls back to a local embedder |
SUMMARY_PROVIDER / SUMMARY_API_KEY / SUMMARY_BASE_URL | no | Any OpenAI-compatible chat endpoint for usage guides; falls back to the npm description |
LURQ_SYNC_CONCURRENCY | no | Parallel ingest workers (default 5) |
LURQ_SYNC_REFRESH_CAP | no | Stalest non-seed packages refreshed per sync (default 400) |
PORT | no | Port for serve-http (defaults to 8080) |
LURQ_RATE_LIMIT_MAX / LURQ_IP_RATE_LIMIT_MAX / LURQ_RATE_LIMIT_WINDOW_MS | no | Per-key / per-IP rate limits for serve-http |
REDIS_URL | recommended for real traffic | Response cache and the shared rate-limit store |
LURQ_METRICS_TOKEN | no | Bearer token guarding /metrics; unset disables the endpoint |
LURQ_ISSUER_SECRET | no | Shared secret for the dashboard-authenticated routes |
E2B_API_KEY / E2B_TEMPLATE | no | VM-isolated sandbox for verifying untrusted packages |
Without REDIS_URL the response cache is a transparent pass-through and every request recomputes
its search on the database. Fine for one box; set it before serving real traffic, since it also
backs the rate limiter across instances.
Set up the index
npm run operator -- db migrate # pgvector extension + schema + curated seed list
npm run operator -- sync # compute scores from public APIs (~2 min)sync is idempotent and tolerant of single-source outages. Run it daily to keep
the index fresh.
Optional, to grow coverage beyond the seed list without curation:
npm run operator -- discover --cap 25 # crawl, merit-gate, ingest survivors
npm run operator -- worker # the autonomous loop (Ctrl-C stops cleanly)
npm run operator -- rescore # re-derive scores after a weight changeServe
These two are in the published package:
lurq serve-http # HTTP MCP server + API-key auth (helmet, rate limits)
lurq serve # stdio MCP server against your own DBserve-http fronts the central DB, so DATABASE_URL lives only on the service
host. Self-hosters can instead wire an agent to a local stdio server with
lurq install-skill --local.
Point your own clients at it with lurq setup --url https://lurq.internal/mcp;
the endpoint is stored alongside the key, so a later bare lurq setup stays on
your server.
API keys
For operators issuing keys to end users (operator plane, needs DATABASE_URL):
npm run operator -- keys create --label "acme" # issue a key (shown once)
npm run operator -- keys list # prefix, tier, last-used, status
npm run operator -- keys rotate lurq_live_ab12cd # replace, then revoke the old one
npm run operator -- keys revoke lurq_live_ab12cd # revoke by prefix or idKeys are shown once at creation and stored only as hashes. Interactively, the key is wiped from your terminal (screen and scroll-back) after you confirm you've copied it. Capture it when it's printed — it can't be recovered, only rotated or revoked and reissued.
rotate creates the replacement before revoking the original, so a failure
part-way through leaves you with a working key rather than none.