Skip to content

Health Check

PayWarden exposes three health endpoints with different audiences and guarantees.

EndpointAuthPurpose
GET /api/v1/health/livePublicLiveness probe — process is up
GET /api/v1/healthPublicReadiness probe — minimal ok / degraded
GET /api/v1/admin/healthAdminDetailed dependency + chain status

GET /api/v1/health/live

Liveness probe. Returns 200 as long as the HTTP server can answer. Does not check the database, Redis, TronGrid, wallet, or chain watcher — this is intentional so a first-deploy bootstrap (pending migrations, uninitialized wallet) does not flap the container into a restart loop.

http
GET /api/v1/health/live

Response 200:

json
{ "status": "alive" }

This is the endpoint targeted by docker-compose.yml's HEALTHCHECK.


GET /api/v1/health

Public readiness probe. Exercises DB, Redis, TronGrid, wallet, and chain watcher on every call, but the response body is intentionally minimal — no infrastructure details (DB host, Redis host, block height, sweep config) are exposed on the public surface. Use this behind a load balancer to fail over a degraded instance.

http
GET /api/v1/health

Response 200 — healthy:

json
{ "status": "ok" }

Response 503 — degraded:

json
{ "status": "degraded" }

For the breakdown of which dependency is failing, an operator must hit the admin endpoint below.


GET /api/v1/admin/health

Detailed health report. Requires admin authentication (see Authentication). Used by the dashboard and on-call operators to diagnose a degraded instance after the public probe flips to 503.

http
GET /api/v1/admin/health
Cookie: admin_token=<jwt>

Response 200 — healthy:

json
{
  "status": "ok",
  "version": "0.1.0",
  "uptime": 3600,
  "checks": {
    "database": "connected",
    "redis": "connected",
    "trongrid": "reachable",
    "watcher": "running",
    "wallet": "initialized"
  },
  "system": {
    "tronNetwork": "mainnet",
    "currentBlock": 65432100,
    "scanMode": "address",
    "minConfirmations": 19,
    "sweepEnabled": true
  }
}

Response 503 — degraded: same shape with status: "degraded" and one or more entries under checks showing disconnected / stopped / uninitialized / degraded.

Fields

FieldTypeDescription
status"ok" | "degraded"Overall health
versionstringPayWarden version from package.json
uptimenumberProcess uptime in seconds
checks.database"connected" | "disconnected"PostgreSQL connectivity
checks.redis"connected" | "disconnected" | "degraded"Redis connectivity
checks.trongrid"reachable" | "disconnected" | "degraded"TronGrid reachability
checks.watcher"running" | "stopped"Chain watcher BullMQ job state
checks.wallet"initialized" | "uninitialized"Wallet service init state
system.tronNetwork"mainnet" | "nile" | "shasta"Configured TRON network
system.currentBlocknumber | nullLatest block number reported by TronGrid
system.scanMode"address" | "block"SCAN_MODE env value
system.minConfirmationsnumberMIN_CONFIRMATIONS env value
system.sweepEnabledbooleanTrue when both HOT_WALLET_KEY and COLD_WALLET_ADDRESS are set

Usage

Docker Compose health check

PayWarden's docker-compose.yml points its HEALTHCHECK at the liveness endpoint so bootstrap conditions do not cause restart loops:

yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/health/live"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 10s

Uptime monitoring

Use the public readiness endpoint with an external uptime monitor — UptimeRobot, Better Uptime, Grafana + blackbox exporter, etc.

bash
curl -sf http://localhost:3000/api/v1/health | jq '.status'

Kubernetes liveness / readiness

Map each probe to the matching endpoint — do not point the liveness probe at /api/v1/health, or a transient TronGrid blip will kill the pod.

yaml
livenessProbe:
  httpGet:
    path: /api/v1/health/live
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 30

readinessProbe:
  httpGet:
    path: /api/v1/health
    port: 3000
  initialDelaySeconds: 10
  periodSeconds: 10

Released under the BSL 1.1 License.