checkfleet

Output formats

Pick one with --output. Every format renders the same findings, sorted worst-first.

Fan out to several sinks

--output takes a comma-separated list, so one run emits to several sinks at once — the checks run only once:

# print JSON locally and push the report to Slack
checkfleet check all --config checkfleet.yml --output json,slack

# a file format plus two chat sinks
checkfleet check all --config checkfleet.yml --output markdown,slack,teams --out-file report.md

With multiple sinks each one is isolated: an unset env var or a down webhook is reported on stderr but doesn’t stop the other sinks or fail the run (the finding gate is separate). With a single --output, a sink error still aborts the command as before. --out-file applies to the format renderer; combining several file formats in one run isn’t meaningful (the last wins).

text (default)

For the terminal. One line per finding with a colored status glyph, then a summary line.

🔴 BAD   http     https://example.com/health   HTTP 404 (want 200), 151ms
🟢 OK    certs    example.com:443              expires in 41 days (2026-09-02, CN=*.example.com)

2 checks: 1 OK, 0 WARN, 1 BAD, 0 ERROR (in 227ms)

The finding order is a de-facto API and applies to every output format: findings are sorted worst-first, then by check, then by target, with a stable sort (equal keys keep their original order). Exact-duplicate findings (same check, target, status and message) are de-duplicated. Tools that parse the output can rely on this ordering.

markdown

An ops-style report you can paste into an incident doc or a PR: a summary, a “Needs attention” section for the non-OK findings, and a full table.

checkfleet check all --config checkfleet.yml --output markdown > report.md

json

Machine-readable. Includes a top-level worst field with the worst status in the run — the field to gate on in a pipeline.

checkfleet check all --config checkfleet.yml --output json | jq '.worst'
{
  "schema": 1,
  "worst": "BAD",
  "findings": [
    { "check": "http", "target": "https://example.com/health", "status": "BAD", "message": "HTTP 404 (want 200), 151ms" }
  ]
}

The schema field is the version of the document itself, so a consumer can tell a format change from a content change. The keys that are safe to depend on — and the fact that message is not one of them, because its wording improves between releases — are listed in Compatibility.

See CI integration for using worst or --exit-on-bad to fail a build.

junit

JUnit XML — one <testcase> per finding, a <failure> for BAD, an <error> for ERROR, WARN kept passing (with a <system-out> note). Feed it to a CI test tab (TeamCity, GitHub Actions test reporters).

checkfleet check all --config checkfleet.yml --output junit > report.xml

html

A self-contained static HTML report (styles inlined, no external resources), themed like this site: the worst-status pill, per-status count tiles, a “Needs attention” section, and the full table. Nice to publish as a CI artifact or attach to an incident.

checkfleet check all --config checkfleet.yml --output html --out-file report.html

github

For GitHub Actions. Emits the findings as workflow commands on stdout, so they become inline annotations on the run and on the PR, and appends the full Markdown report to the job summary ($GITHUB_STEP_SUMMARY) when that variable is set.

checkfleet check all --config checkfleet.yml --output github --exit-on bad
Finding Annotation
BAD, ERROR ::error
WARN ::warning
OK (none)

OK findings are skipped deliberately: GitHub shows at most 10 annotations per level per step, so annotating green targets would hide the real ones. The summary still lists everything.

Because the sink writes the summary file itself, you never pipe its output — which is what makes the CI gate reliable, see CI integration. Outside Actions (no $GITHUB_STEP_SUMMARY) it just prints the annotations, which is handy for eyeballing the format locally.

sarif

SARIF 2.1.0, the interchange format for static-analysis results. Upload it and the findings land in GitHub’s Code scanning / Security tab (and in any other SARIF-aware tool) — see CI integration.

checkfleet check all --config checkfleet.yml --output sarif --out-file checkfleet.sarif
Finding SARIF level
BAD, ERROR error
WARN warning
OK none

Each module becomes a rule (checkfleet/certs, checkfleet/http, …) with its description taken from checkfleet explain; each finding becomes a result.

Three details that matter when reading the output:

  • BAD and ERROR share the level error because SARIF has no third failure level. The engine’s own status survives in properties.status, so a consumer can still tell “the target is unhealthy” from “the check could not measure”.
  • Results are anchored to the config file, line 1. SARIF is file-oriented and a checkfleet finding is about a network target, so there is no source line to blame; the config is the file that makes the target be checked at all. The real subject is in the message and in properties.target. Pass a repo-relative --config so the alerts attach to the file in the repo.
  • Fingerprints ignore severity (partialFingerprints is built from check + target). A certificate going WARN → BAD stays the same alert getting worse, instead of appearing as a new one.

prometheus

The Prometheus text-exposition format (same metrics as serve), for a one-shot run instead of a scrape. With --out-file it’s written atomically (temp + rename), so it’s safe to drop into the node_exporter textfile collector:

checkfleet check all --config checkfleet.yml --output prometheus \
  --out-file /var/lib/node_exporter/textfile/checkfleet.prom

--out-file works for any printable format (writes to the file instead of stdout).

csv

Emits CSV with a header row — status,check,target,message — worst first, one finding per row. Fields are quoted/escaped (commas and newlines in messages are safe), for spreadsheets or ingestion into another system.

checkfleet check all --config checkfleet.yml --output csv --out-file findings.csv

slack

Posts a Block Kit message to a Slack incoming webhook instead of printing: a header, the summary line, then the non-OK findings (worst first, capped). The webhook URL is read from an environment variable — never passed on the command line or stored in config.

export SLACK_WEBHOOK="https://hooks.slack.com/services/…"
checkfleet check all --config checkfleet.yml --output slack
# or point at a different env var:
checkfleet check all --config checkfleet.yml --output slack --webhook-env SLACK_WEBHOOK_OPS

If the env var is empty the command errors (nothing is sent). A run that posts successfully prints report sent to Slack.

discord / teams

Post to a Discord webhook (a rich embed) or a Microsoft Teams incoming webhook (a MessageCard) — the summary plus the non-OK findings (worst first, capped), colored by the worst status. Same model as slack: the URL comes from --webhook-env, never the command line.

export DISCORD_WEBHOOK="https://discord.com/api/webhooks/…"
checkfleet check all --config checkfleet.yml --output discord --webhook-env DISCORD_WEBHOOK

export TEAMS_WEBHOOK="https://outlook.office.com/webhook/…"
checkfleet check all --config checkfleet.yml --output teams --webhook-env TEAMS_WEBHOOK

telegram

Sends a plain-text message via the Telegram Bot API (sendMessage): the summary line then the non-OK findings (worst first, capped, within the 4096-char limit). The bot token and chat id come from the environment — never the command line — via --telegram-token-env (default TELEGRAM_TOKEN) and --telegram-chat-env (default TELEGRAM_CHAT_ID).

export TELEGRAM_TOKEN="123456:ABC-DEF…"
export TELEGRAM_CHAT_ID="-1001234567890"
checkfleet check all --config checkfleet.yml --output telegram

webhook

POSTs the JSON output to a generic webhook (URL from --webhook-env), for any system that ingests JSON — a Teams/Discord relay, a custom collector, etc.

export MY_HOOK="https://hooks.example/checkfleet"
checkfleet check all --config checkfleet.yml --output webhook --webhook-env MY_HOOK

Pass --template FILE to shape the payload with a Go text/template instead of the default JSON. The template is executed against ,, , ///, and (each with .Check, .Target, .Status, .Message). Unknown fields are an error, so typos don’t ship silently.

checkfleet check all --config checkfleet.yml --output webhook \
  --webhook-env MY_HOOK --template payload.tmpl

otlp

An OTLP/HTTP metrics request in JSON encoding — the same gauges as prometheus (checkfleet.finding.status, .findings.total, .worst.status), hand-built with zero dependencies (no OpenTelemetry SDK). POST it to a collector’s /v1/metrics:

checkfleet check all --config checkfleet.yml --output otlp \
  | curl -s --data-binary @- -H 'content-type: application/json' \
      http://otel-collector:4318/v1/metrics