Watchdog: signed monitoring of x402 endpoints (watchdog_v1)

Published 2026-09-01. Binding specification, frozen after publication.

Watchdog watches an x402 endpoint and sends a signed alert the moment a measurable observable changes: it went unreachable, its payability verdict degraded, the declared payTo / networks / asset in its 402 changed, its 402 became self-inconsistent, or it dropped from the discovery catalogue. Every alert is EIP-191 signed and verifiable offline, so it stands as evidence months later. One paid x402 call is a 30-day subscription for one endpoint at 5 dollars. Free tier: one endpoint per verified channel.

Signer 0x57fF0F084Cba33e6761503f90eEF0Da9F159350c, published in the proof manifest as the watchdog service signer. EIP-191 personal_sign over canonical JSON (json.dumps sort_keys true, compact separators, ensure_ascii), the same discipline as settle and preflight.

Config constants (fixed, published)

Signed bodies

Every signed body carries a top-level type: one of alert, registration_receipt, daily_attestation, subscription_expired. Alerts also carry alert_type. Every body is signed except the fail-closed refusals (400, 503). Every object, including every refusal, is closed to undeclared keys.

Alert types (closed, 9 in v1)

Each alert carries, verbatim in its own limitations field, what it does NOT mean.

  1. endpoint_unreachable - stopped answering for N consecutive checks. Not a business-gone or single-blip claim.
  2. endpoint_recovered - answered again for M consecutive checks. Reachable, not necessarily payable.
  3. verdict_degraded - verdict PAYABLE to NOT_PAYABLE or MALFORMED_402, with http_status. MALFORMED_402 is a catch-all; not "funds lost".
  4. verdict_restored - back to PAYABLE.
  5. payto_changed (per network) - the declared payTo changed. NOT a breach: the recipient changed, often intentional. What and when, never why.
  6. networks_changed - the set of settlement networks changed.
  7. asset_changed - the settlement token for a network changed (not the price).
  8. self_inconsistent_402_detected - 402 body and header carry different challenges. A spec-ambiguity flag, not a definitive break.
  9. listing_disappeared_from_catalog - WEAK (weak_signal true); dropped from the catalogue while still live. Never on absence alone.

price_changed is reserved for v2 (amount extraction is not yet reliable enough to alert without false positives); in v1 it is neither monitored nor attested.

Registration and channel verification

GET|POST /watchdog/register?endpoint=<https-url>&channel=<email|webhook-url>. Verification is mandatory before any alert and before the subscription clock starts, so the 30 days begin at verified_at. Email: a link/code; webhook: a challenge POST with a nonce the webhook must echo with a 200. Until verified: pending_verification, no alerts, no time consumed. An unverified registration is discarded silently after 72h (the one justified exception to no-silent-death; we never message a channel that never confirmed). Free: first endpoint per verified channel. Paid: 5 dollars USDC on Base via the CDP facilitator, one endpoint for 30 days. In v1 the free endpoint is fixed and cannot be swapped without a paid registration. Renewal extends the expiry with no lost days.

Wiring: /watchdog/register is an open route (free tier and validation). When a paid registration is required it returns the standard x402 402 challenge whose resource is /watchdog/register/paid; the paid registration is settled there (5 dollars USDC on Base via the CDP facilitator). The subscriber-visible behaviour is exactly as described; only the internal route split is stated so there is no doc-to-implementation gap.

Webhook delivery security

https only; resolved on public DNS and rejected if it resolves to any private, loopback, link-local (including 169.254.169.254 metadata), unique-local, or reserved range; redirects never followed; response body ignored except the challenge nonce; re-resolved on every send (no cached IP) to defeat DNS rebinding.

What the schema cannot express, verify these yourself

Offline verification

Remove digest and signature, canonicalise (keys sorted, compact separators, non-ASCII escaped), sha256 must equal digest, recover the EIP-191 signature over that string; it must equal signer.

Fixtures and schema

The paid /settle, /preflight, /screen, /payable endpoints are unrelated to this service. Signed report manifest at /proof/index.json.