Skip to content

API hosting

The API backs the admin panel, the Android app, and any integration you write. It is optional: with it down, watches still run.

Terminal window
pnpm serve

Or as a container — the repository includes a Dockerfile:

Terminal window
docker build -t watcher .
docker run -d --name watcher --env-file .env -p 3000:3000 watcher

GET /health is the health-check endpoint. Secrets come from the environment; never bake them into an image.

  1. Everything the executor needs — the model key and the database URI.

  2. AUTH_JWT_SECRET, at least 32 characters. The API refuses to start without it rather than serving unauthenticated.

  3. WEB_ORIGIN — the panel’s origin, so a browser may call the API cross-origin. Comma-separate several. With none set, no CORS headers are sent at all.

  4. SCHEDULER_ENABLED=false if a scheduled CI job already runs your watches.

Any platform that runs a container works. What matters:

Requirement Why
Outbound internet Model providers, the sites being watched, alert channels
Environment secrets Everything sensitive arrives that way
HTTPS The panel and app expect it
Persistent database elsewhere The container itself is stateless

Suspend-when-idle hosting is fine, with one caveat below.

The Android app already accounts for this with a warm-up probe and extended retries on the first call.

The container keeps nothing that matters:

  • All persistent state is in the database.
  • Key cooldowns are per process and self-heal after a restart.
  • Rate-limiter windows reset on restart.

So you can redeploy at any moment. The only cost of a restart mid-run is that the run is lost and repeated next cycle.

On SIGTERM or SIGINT the service stops the scheduler, drains in-flight HTTP requests, flushes buffered usage metering, closes the database connection and exits.

Watcher is a single-process design. Run one instance with the scheduler enabled. You may run additional instances with SCHEDULER_ENABLED=false to serve API traffic, since API requests are stateless — but two schedulers against one database is not a supported topology.

Terminal window
curl -s https://<your-host>/health # {"status":"ok"}
curl -s https://<your-host>/config/status # booleans for each integration

Then sign in from the panel and open a watch’s run history — that exercises auth, the database and the read path in one go.