Skip to content

Worker Liveness Probe (5-min) #1008

Worker Liveness Probe (5-min)

Worker Liveness Probe (5-min) #1008

name: Worker Liveness Probe (5-min)
# GAP-443 — external GHA scheduler for the Fly worker liveness probe.
#
# Background: Vercel Hobby tier crons run at most daily, and the one daily
# slot is already consumed by `dispatch.ts` (~14 billing/entitlement jobs).
# The liveness probe needs a sub-daily cadence, so (same rationale as
# GAP-142's reconciliation-crons.yml) this workflow drives it externally
# every 5 minutes - free on a public repo, no Vercel Pro required.
#
# The probe route (apps/server/src/routes/cron/worker-liveness.ts) reads the
# actual worker URL from REVEALUI_WORKER_HEALTH_URL, an env var on the
# api project. This workflow never sees or prints that URL. It calls the cron
# origin (REVEALUI_CRON_ORIGIN, default https://revealui-api.vercel.app) and
# reports the probe's healthy/status result, keeping the worker hostname out
# of the public repo and out of Actions logs.
on:
# Schedule re-enabled 2026-07-28 after GAP-455 green proof:
# run 30400804458 returned {"healthy":true,"status":200,"probe":4} with the
# #2267 classifier on production. Error buckets + guard-unavailable path
# ship on main; schedule was the remaining ops gate for the green case.
# Red-path proof (genuinely down worker) remains on GAP-455 before close.
schedule:
# Every 5 minutes (GitHub's minimum granularity). UTC. Scheduled runs can
# be delayed 5-15 min during high GHA load, so actual cadence is
# "approximately every 5 minutes," not exact - acceptable for a
# liveness backstop.
- cron: '*/5 * * * *'
workflow_dispatch:
inputs:
reason:
description: 'Reason for manual run'
required: false
default: 'manual ops trigger'
# This job only GETs a public API endpoint with a bearer secret header; it
# never uses GITHUB_TOKEN. Grant it nothing.
permissions: {}
jobs:
worker-liveness:
name: worker-liveness
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: GET /api/cron/worker-liveness
env:
API_URL: ${{ vars.REVEALUI_CRON_ORIGIN || 'https://revealui-api.vercel.app' }}
CRON_SECRET: ${{ secrets.REVEALUI_CRON_SECRET }}
run: |
if [[ -z "$CRON_SECRET" ]]; then
echo "::error::REVEALUI_CRON_SECRET secret not set"
exit 1
fi
response=$(curl -sS \
-H "X-Cron-Secret: $CRON_SECRET" \
-w "\nHTTP_STATUS:%{http_code}" \
"$API_URL/api/cron/worker-liveness" || true)
status=$(echo "$response" | grep "HTTP_STATUS:" | cut -d: -f2)
body=$(echo "$response" | sed '/HTTP_STATUS:/d')
echo "Status: $status"
echo "Body: $body"
if [[ "$status" != "200" ]]; then
echo "::error::worker-liveness returned $status (non-200) — worker unhealthy, unreachable, or misconfigured"
exit 1
fi