Skip to content

Secrets

Monitoring an authenticated endpoint means a check needs a credential. Storing that credential in the checks table would make the database — and any config export, API response, or alert payload — sensitive.

clawdwatch stores a reference instead.

How it works

Declare the values your deployment has:

ts
createMonitor<Env>({
  d1: (env) => env.MONITORING_DB,
  secrets: (env) => ({
    HEALTHCHECK_SECRET: env.HEALTHCHECK_SECRET,
    PARTNER_API_KEY: env.PARTNER_API_KEY,
  }),
});

Reference one by name in a check:

json
{
  "id": "partner-api",
  "url": "https://api.example.com/status",
  "headers": { "X-Api-Key": "${PARTNER_API_KEY}" }
}

What is stored is the literal text ${PARTNER_API_KEY}. Substitution happens at exactly one moment — building the outbound request — and the resolved value exists only for the duration of that fetch.

References work in headers, the body, and the URL.

The write guard

A check containing a real secret value is rejected:

json
{ "error": "Refusing to store a literal secret value in check headers (matches: PARTNER_API_KEY). Use a reference like ${PARTNER_API_KEY} instead." }

This applies to the API, the dashboard, and config imports alike. It is what makes a UI-editable, database-backed system safe to open-source: there is no path by which a value reaches D1.

Values shorter than eight characters are not matched, since a short string would appear everywhere and make the guard useless.

Missing references fail loudly

If a check references a name your config does not provide, the check fails with a clear error rather than sending an empty header and reporting a mysterious 401:

Unresolved secret reference(s): PARTNER_API_KEY. Add them to the `secrets`
option and set the corresponding Worker secret.

One misconfigured check fails on its own. The rest of the run continues.

Whole-domain headers

Some tokens belong to a domain rather than a check — a WAF bypass, or a header your bot protection needs. headerRules applies them to every check on a matching host:

ts
headerRules: [
  {
    host: /(^|\.)example\.com$/,
    headers: { 'x-waf-bypass': '${WAF_BYPASS_TOKEN}' },
  },
],

host accepts an exact string or a pattern. Rules apply after a check's own headers, so a rule can supply something the check does not know about. Checks on other hosts are untouched — the token is never sent somewhere it does not belong.

What leaves the system

Everything outbound is redacted first:

PathWhat appears
GET /api/checks${PARTNER_API_KEY}
GET /api/config${PARTNER_API_KEY} — safe to commit
Alert payloadsno headers at all; only id, name, url, tags, status
Logsreferences, never values
The dashboardreferences, with a note explaining them

A property test asserts this across every combination of secret and placement: no resolved value appears in any outbound representation.

What is never stored

Response bodies are read only to evaluate assertions, then discarded. Only the assertion failure message is kept, truncated to 256 characters. A monitored endpoint returning personal data does not leak it into your monitoring database.