Warden

content/config.json is entirely optional and hot-reloads with the rest of content/. Every field below is optional too, with a sensible default filling in whatever's missing.

A config using most of them at once:

content/config.json
json
{
  "title": "Acme Status",
  "titleTemplate": ":title · Acme Status",
  "description": "Uptime and incident history for Acme's public services.",
  "lang": "en",
  "culture": "en-GB",
  "theme": "midnight",
  "author": "Acme Ops",
  "favicon": "🛡️",
  "scrollIndicator": true,
  "noIndex": { "pages": true },
  "socialLinks": [
    { "icon": "github", "url": "https://github.com/acme", "title": "GitHub" }
  ],
  "menu": [
    { "title": "Status", "path": "/" },
    { "title": "About", "path": "/about/" }
  ],
  "footerMenu": [
    { "title": "Status API", "path": "/api/status", "external": true }
  ],
  "footer": "© {year} {author}. Built with [Warden](https://github.com/hawkinslabdev/warden).",
  "monitoring": {
    "intervalSeconds": 60,
    "retentionDays": 90,
    "group": "type",
    "targets": [
      { "id": "site", "name": "Main site", "url": "https://acme.example" }
    ]
  }
}

Site ​

Field Description
title The site name: browser tab, social previews, and the {title} token elsewhere.
titleTemplate Wraps title for the tab text, e.g. ":title · Acme". :title is the placeholder.
description The default meta description, used on any page that doesn't set its own.
brand Text shown next to the logo in the topbar. Falls back to title.
brandImage A logo image, replacing brand in the topbar.
image Default social preview image for pages that don't set their own cover.
favicon An emoji or an image path for the browser tab icon.
author The name in the {author} footer token. organization, organisation, and owner are aliases, checked in that order, ahead of author.

Language and dates ​

Field Description
lang The page's <html lang> attribute and default UI language.
culture Date and number formatting, e.g. "en-GB".
locale Either a bare code string ("nl") or an object: { "culture": "en-GB", "code": "en" }. Merges with culture above.

Appearance ​

Field Description
theme One of the built-in palettes. See the guide for the full list. Add " dark" or " light" to pin a mode and drop the toggle.
scrollIndicator Set false to hide the reading-progress bar at the top of the page.
showOverallUptime Set false to hide the status page's overall-uptime line. /api/status never included it, so nothing changes there.
Field Description
menu Topbar links: [{ "title": "...", "path": "..." }]. An entry with items renders as a dropdown instead of a link. external: true opens it in a new tab.
footerMenu Same format, rendered in the footer.
footer Markdown for the footer note. Tokens: {year}, {author}, {title}. Falls back to a simple copyright line built from title.
socialLinks Icon links in the topbar: [{ "icon": "github", "url": "...", "title": "..." }].
head Raw tags injected into <head>: [{ "tag": "meta", "attrs": { "name": "...", "content": "..." }, "content": "..." }].

Search visibility ​

Field Description
noIndex.pages Set true to noindex every standalone page under content/pages/ at once, without touching each page's own front matter. A page's own noindex: true still applies regardless.
noIndex.status Set true to noindex the status page itself ("/") and drop it from sitemap.xml.
redirectHosts Off-site hosts a page's redirect front matter is allowed to send readers to. Same-host redirects always work without listing anything here.

Monitoring ​

monitoring controls what gets checked and how the status page groups it. See the guide for a walkthrough; this is the full field list.

Field Description
intervalSeconds How often every target is checked. Default 60.
retentionDays How long heartbeat history is kept before it's pruned. Default 30. This value cannot be lower than historyDays to preserve your data.
group "type" sections monitors by type, "custom" sections them by each target's own group field (falling back to its type when unset). Unset renders one ungrouped list.
incidentWindowDays, incidentMaxShown How far back and how many resolved incidents show on the status page. Defaults 7 and 10.
historyDays Days of ticks in each monitor's history bar. Default 90.
degradedBelowPercent A day's tick turns down (not just degraded) when that day's uptime% falls below this. Unset keeps the stricter default: down only when every check that day failed.
incidentUrlPattern "year" gives every incident a /incidents/{year}/{slug}/ URL, "year-month" gives /incidents/{year}/{month}/{slug}/, both taken from the incident's own date, regardless of which folder the file lives in. Unset (default) uses the file's own path under content/incidents/ as-is.
maintenanceWindowDays, maintenanceMaxShown How far ahead and how many upcoming maintenance windows show. Defaults 14 and 10.
webhooks [{ "url": "...", "headers": { "Authorization": "Bearer ..." } }]; sent as POST with JSON body (monitorId, name, status, message, timestamp) once per down/recovery transition, not on every check while a target stays down. headers is optional, for receivers that need auth.
targets The list of monitors, covered below.

Target fields ​

Every target needs id (a stable slug, it's the database key) and name (the display label). Everything else depends on type.

Field Applies to Description
type all http (default), service_backend, ping, tcp, database, ftp, sftp, dns, or ssl.
url http, service_backend The address to request.
host, port everything except http/service_backend Where to connect. Ports default per type: 21 for ftp, 22 for sftp, 443 for ssl.
expectedStatus service_backend HTTP status the response must match. Default 200.
expectedJsonPath, expectedValue service_backend A JSON path into the response body (e.g. $.status) and the value it must equal.
expectedIp dns The IP the hostname must resolve to. Left unset, any successful resolution counts as up.
dnsServer dns Query this resolver directly instead of the OS default.
family dns "ipv4" or "ipv6" to query one record type only. Unset queries both.
warnDaysBefore ssl How many days before certificate expiry the check starts failing. Default 14.
secure ftp Set true to require an explicit AUTH TLS upgrade after connecting.
dbType database Informational only; the check itself is a TCP reachability probe.
insecure http, service_backend Set true to skip TLS certificate validation, for a self-signed internal service.
retries all Consecutive failures allowed before the target is recorded down. Default 0, down on the first failed check.
group all This target's own group heading, used when monitoring.group is "custom".
hidden all Set true to keep checking and recording this target while excluding it from the status page and /api/status - for a backend you track but don't publish.

A few examples across types:

content/config.json
json
{
  "monitoring": {
    "targets": [
      { "id": "site", "name": "Main site", "url": "https://acme.example" },
      { "id": "api", "name": "API health", "type": "service_backend", "url": "https://api.acme.example/healthz", "expectedJsonPath": "$.status", "expectedValue": "ok" },
      { "id": "ci", "name": "CI", "type": "service_backend", "url": "https://api.github.com/repos/OWNER/REPO/actions/workflows/WORKFLOW_FILE/runs?per_page=1&status=completed", "expectedJsonPath": "$.workflow_runs[0].conclusion", "expectedValue": "success" },
      { "id": "db", "name": "Postgres", "type": "tcp", "host": "db.internal", "port": 5432, "retries": 2 },
      { "id": "cert", "name": "Certificate", "type": "ssl", "host": "acme.example", "warnDaysBefore": 21 },
      { "id": "resolver", "name": "DNS", "type": "dns", "host": "acme.example", "expectedIp": "203.0.113.10" }
    ]
  }
}