This runbook defines the minimum production operating standard for apps/relay.
Platform-wide ownership, incident severity, backups, and credential rotation are in
OPERATIONS.md.
- The Fly production operator owns mitigation and rollback.
- The incident commander in
OPERATIONS.mdowns severity, coordination, and handoffs. - Any indication that an unauthorized socket was accepted is SEV-1 and must be escalated to the security owner immediately.
- Availability: 99.9% monthly (
/healthzreachable) - Unauthorized ticket rejection accuracy: 100% (no unauthorized connects accepted)
- Ticket consume failure rate: under 0.5% over 15 minutes
The availability source of truth must be an external monitor. The scheduled GitHub synthetic workflow is a secondary detector. Apply the ticket failure threshold only when there are at least 20 attempts in the window, and investigate individual failures for security impact regardless of volume.
fly.toml configures a Fly service-level GET check against /healthz every 15 seconds. The
check controls routing but does not restart a failed Machine. The production relay runs one
always-warm 256 MB Machine; there is no second Machine to receive traffic when that check
fails. The dev relay uses fly.dev.toml and may be stopped until the next request.
Inspect platform state:
fly checks list --app <app-name>
fly status --app <app-name>
fly machine list --app <app-name>
fly logs --app <app-name>Configure these alert routes outside the repository:
- Production
/healthz: page after two consecutive 5-minute probe failures; recover after two consecutive successes. - Fly Machine unhealthy or unavailable for 2 minutes: page the relay operator.
- More than two unexpected Machine restarts in 10 minutes: page the relay operator.
- Ticket consume failure rate above 0.5% for 15 minutes with at least 20 attempts: SEV-2.
- Any smoke test that accepts an unauthenticated WebSocket: SEV-1.
- Development relay failure: maintenance alert after three consecutive failures, not a page.
Route production alerts to the primary and backup operators. Test the notification route quarterly. Do not place relay tickets, share codes, session identifiers, or terminal content in alert bodies.
- Confirm Fly app config:
apps/relay/fly.toml(production) orapps/relay/fly.dev.toml(dev) is currentCONVEX_URL(orRELAY_CONVEX_URL) secret is set on the target appfly config validatesucceeds for the config you are about to deploy
- Deploy: push to
dev/main(deploy-relay.yml) or runworkflow_dispatch. Dev ->wrapper-relay-dev, prod ->wrapper-relay-prod(see ENVIRONMENTS.md) - Verify post-deploy smoke:
/healthzreportsok: true- unauthenticated
/wscloses with4001or4003 - Fly reports the Machine and service check healthy
- Watch logs and the external monitor for at least 10 minutes after production deploys.
The deploy workflow intentionally passes --ha=false. Do not remove it or increase the
Machine count without explicit budget approval; Fly HA can incur additional cost.
If relay attach fails:
- Declare the incident and confirm whether dev, production, or both are affected.
- Run
fly status,fly machine list, andfly checks listfor the affected app. - Run
fly logs --app <app-name>and look for boot failures, crashes, andticket rejected. Redact identifiers before copying any output. - Confirm the Fly
CONVEX_URLsecret points to the matching deployment:wrapper-relay-dev->https://sleek-echidna-539.convex.cloudwrapper-relay-prod->https://confident-fox-458.convex.cloud
- Run the smoke script:
RELAY_URL="https://<app>.fly.dev" bun run --cwd apps/relay smoke- If health succeeds but valid tickets fail, inspect Convex health and recent backend or secret changes. Do not weaken ticket validation to restore service.
- If the Machine or health check is bad after a deploy, roll back. Because there is one Machine, prioritize a known-good rollback over prolonged in-place debugging.
- List releases:
fly releases --app <app-name>- Roll back to previous stable image:
fly deploy --image <image-ref> --app <app-name>- Re-run the smoke check, verify Fly health, and monitor logs and external checks for 10 minutes.
- Record the bad and restored image references in the incident record.
Run all canonical page and relay probes:
gh workflow run synthetic-health.yml --ref dev
gh run list --workflow synthetic-health.yml --limit 5This is useful after DNS, TLS, Fly, Convex, legal-page, or documentation changes. GitHub scheduled workflows are best-effort and are not a substitute for the external SLO monitor.