Full API + scheduler + worker + Postgres + Redis + MinIO:
docker compose up --build
curl -sS http://localhost:8080/v1/healthCreate a client against the Compose stack (pepper already set in compose):
docker compose exec api node dist/cli/create-api-client.js \
--name "Ops Client" --tier free \
--scopes read:public,read:internal,write:ingestionLoad SQL fixtures / HTML fixture crawls:
docker compose --profile fixtures run --rm migrate-fixtures
docker compose run --rm --no-deps \
-v "$PWD/fixtures:/app/fixtures:ro" \
worker node dist/workers/ingestion-worker.js --fixture ppac_rspLogs and lifecycle:
docker compose logs -f api worker scheduler
docker compose down # keep volumes
docker compose down -v # wipe DB/redis/minio dataSee docker.md for ports, image layout, and troubleshooting.
npm install
cp .env.example .env
npm run devProduction-style local start:
npm run build
npm startThe service uses native node:http.
Host:
npm run db:migrate
npm run db:seed
# Local/test only:
npm run db:fixturesCompose (automatic on docker compose up; manual re-run):
docker compose run --rm migrate
docker compose run --rm migrate-seed
docker compose --profile fixtures run --rm migrate-fixturesdb:migrate applies schema migrations. db:seed applies schema and reference
seeds but excludes fixtures. db:fixtures explicitly loads local/test data and
is blocked when NODE_ENV=production.
Host (pepper must match the API process):
API_KEY_PEPPER=dev-pepper \
DATABASE_URL=postgres://oilapi:oilapi@localhost:55432/oilapi \
npx tsx src/cli/create-api-client.ts --name "Dev Client" --tier free --scopes read:publicCompose:
docker compose exec api node dist/cli/create-api-client.js \
--name "Dev Client" --tier free --scopes read:publicUI: start .smoke-frontend and open http://localhost:5173/keys.html
The plaintext api_key is printed once. Store only the shown value in the
client secret manager. The database stores only the HMAC hash.
Included in docker compose up as services scheduler and worker.
npx tsx src/workers/scheduler.ts
npx tsx src/workers/ingestion-worker.tsRun fixture ingestion directly for development:
npx tsx src/workers/ingestion-worker.ts --fixture ppac_rsp
npx tsx src/workers/ingestion-worker.ts --fixture igl_pricescurl -H "Authorization: Bearer $OPS_API_KEY" \
http://localhost:8080/v1/internal/sources/healthThe key needs read:internal.
curl -X POST \
-H "Authorization: Bearer $INGESTION_API_KEY" \
-H "content-type: application/json" \
-d '{"source_key":"ppac_rsp","mode":"manual","reason":"replay failed crawl"}' \
http://localhost:8080/v1/internal/ingestion/runsThe key needs write:ingestion.
Manual and scheduled live runs are accepted only when the source is active,
ingestion_enabled=true, and its adapter declares validated live support.
Unverified adapters are rejected before a crawl or job is created.
- Check
/v1/internal/sources/health. - Inspect
/v1/internal/crawl-runs?source_key=<source>. - Replay the crawl with the manual ingestion endpoint.
- If the source still fails, inspect fixture/live fetch errors and artifact hashes.
- If stale but data is under 24 hours old, keep serving with warning logs.
- If expired, prefer internal warehouse history and escalate source repair.
The stored key hash depends on the pepper. Safe rotation requires reissuing API keys or a dual-pepper transition.
- Create new clients with the new pepper.
- Distribute new plaintext keys to clients.
- Confirm traffic on new
client_idvalues. - Revoke old clients.
- Deploy with only the new
API_KEY_PEPPER.
Do not rotate the pepper alone without replacing client keys; existing hashes will stop validating.