CFN Monitor
Purpose: Post-deploy health gate and incident triage aid. Probes a list of HTTP endpoints, checks expected status codes and optional latency budgets, emits a JSON summary to stdout, and exits nonzero if any target is unhealthy.
Not a metrics backend. Not an alerting daemon. A scriptable gate you run after a deploy and a runbook you follow when something breaks.
When to Use
- After every deploy (Fly.io or otherwise) as a go/no-go check before marking the release green.
- As a quick sanity check before opening a pull request that touches HTTP routes.
- As the first step of incident triage: confirm scope and get a baseline.
Inputs
Flags (repeatable, stackable with env)
--target <url>[:<expected_status>[:<latency_budget_ms>]]
Each --target flag adds one probe. Parts are colon-separated after the URL.
Examples:
--target https://myapp.fly.dev/health
--target https://myapp.fly.dev/health:200
--target https://myapp.fly.dev/health:200:2000
--target https://myapp.fly.dev/api/status:200:1500
Defaults when omitted: expected_status=200, latency_budget_ms = no budget (latency reported but not checked).
Environment: CFN_MONITOR_TARGETS
JSON array of target objects. Merged with any --target flags (flags take precedence on conflicts).
[
{ "url": "https://myapp.fly.dev/health", "expected_status": 200, "latency_budget_ms": 2000 },
{ "url": "https://myapp.fly.dev/api/status", "expected_status": 200 }
]
latency_budget_ms is optional per entry. Omit to skip latency checking for that target.
Other env vars
| Var | Default | Purpose |
|---|---|---|
CFN_MONITOR_TIMEOUT_S |
10 |
Per-probe curl timeout in seconds. |
CFN_MONITOR_TARGETS |
(none) | JSON array of targets (see above). |
Outputs
JSON summary to stdout. All status messages go to stderr so stdout stays machine-readable.
{
"timestamp": "2026-06-28T12:00:00Z",
"targets_total": 2,
"targets_pass": 1,
"targets_fail": 1,
"results": [
{
"url": "https://myapp.fly.dev/health",
"expected_status": 200,
"actual_status": 200,
"latency_ms": 312,
"latency_budget_ms": 2000,
"latency_ok": true,
"status": "pass",
"reason": ""
},
{
"url": "https://myapp.fly.dev/api/status",
"expected_status": 200,
"actual_status": 503,
"latency_ms": 4501,
"latency_budget_ms": 1500,
"latency_ok": false,
"status": "fail",
"reason": "status mismatch (expected 200, got 503); latency 4501ms > budget 1500ms"
}
]
}
status per result: pass (status matched, latency within budget if set) or fail.
latency_budget_ms is null in output when no budget was configured for that target.
latency_ok is null when no budget is configured (no check performed).
Exit Codes
| Code | Meaning |
|---|---|
| 0 | All targets healthy (status matched, latency within budget where configured). |
| 1 | One or more targets failed (status mismatch or latency budget exceeded). |
| 2 | No targets configured (nothing to probe). |
| 3 | Configuration parse error (malformed CFN_MONITOR_TARGETS JSON). |
Alert Thresholds
Thresholds are per-target, configured at call time (not a daemon config file):
| Threshold | How configured | Behavior |
|---|---|---|
| Status mismatch | expected_status per target (default 200) |
Marks target fail; included in exit code 1. |
| Latency over budget | latency_budget_ms per target |
Marks target fail; reason includes both values. |
| Consecutive failures | not in single-shot mode (see cfn: note below) | Upgrade path only. |
cfn: single-shot probe only, no consecutive-failure tracking. Upgrade trigger: need for scheduled polling that counts N consecutive failures before alerting (then wire a cron/systemd timer calling this script and maintain a state file for failure counts).
Usage
# Single target
$HOME/.claude/skills/cfn-monitor/execute.sh --target https://myapp.fly.dev/health:200:2000
# Multiple targets via flags
$HOME/.claude/skills/cfn-monitor/execute.sh \
--target https://myapp.fly.dev/health:200:2000 \
--target https://myapp.fly.dev/api/users:200:1500
# From env
export CFN_MONITOR_TARGETS='[{"url":"https://myapp.fly.dev/health","expected_status":200,"latency_budget_ms":2000}]'
$HOME/.claude/skills/cfn-monitor/execute.sh
# Parse the JSON output
$HOME/.claude/skills/cfn-monitor/execute.sh --target https://myapp.fly.dev/health | jq '.results[]'
# Fail-fast in a deploy pipeline
$HOME/.claude/skills/cfn-monitor/execute.sh --target https://myapp.fly.dev/health:200:3000 || { echo "Deploy gate failed. Check output above."; exit 1; }
Dependencies
curl(always present in Fly builder and typical CI environments).jqfor parsingCFN_MONITOR_TARGETSJSON. Degrades: ifjqis absent and only--targetflags are used, the skill works without it.- No new dependencies added.
Incident Triage
See RUNBOOK.md in this directory for the ordered triage procedure when a health check fails post-deploy.
Related
cfn-deployment-lifecycle- deploy orchestration that wraps this gate.cfn-security-review- pre-merge security gate (runs before deploy, not after).RUNBOOK.md- ordered triage steps for incidents flagged by this gate.