Signed uptime attestation
An uptime badge is a picture. This is the same claim as JSON, signed with our key — so a program can check it, and so nobody has to take your word for it.
Why a badge is not enough any more
An uptime badge does three things: it reassures a visitor, it puts a third party's name behind the claim, and it does both from your own site. A program reading your page gets none of that. It does not look at an SVG, and the number inside one is unreadable to it.
The obvious fix — publish your uptime as JSON — solves the wrong half. Anyone can serve a file saying they were up 99.99% of the time. The value was never the number; it was that somebody else measured it. So the claim has to be signed by the party that did the measuring, and verifiable without trusting the site serving it.
What you publish
Tick Attest next to a service on your status page and it gets a second address alongside its badge:
https://www.pingdomain.io/attest/<page>/<service>.json
Link to it from your own site, exactly as you would paste a badge — a <link> instead of an <img>:
<link rel="uptime-attestation" type="application/json"
href="https://www.pingdomain.io/attest/acme/api.json">If you would rather an agent found it by guessing a conventional path, redirect /.well-known/uptime.json on your own domain to the same address. The attestation is served with Access-Control-Allow-Origin: *, so a browser or an agent can read it from anywhere.
The format
{
"issuer": "https://www.pingdomain.io",
"subject": "https://shop.example.com/",
"window_days": 30,
"uptime_ratio": 0.9987,
"checks": 8640,
"failed": 11,
"last_check": {
"at": 1758300000,
"healthy": true,
"status": 200,
"ttfb_ms": 24,
"colo": "ARN"
},
"region": "eu",
"retention_days": 30,
"issued_at": 1758300000,
"expires_at": 1758303600,
"signature": "eyJhbGciOiJFZERTQSIsImtpZCI6..."
}A few of these are worth explaining, because they are where a monitoring tool usually shades the truth:
uptime_ratiois null, never 1, when there is no history. Zero successes out of zero checks is arithmetically a hundred per cent and completely untrue — a service we have never checked has not been up for thirty days.window_dayscan never exceedretention_days. The attestation reports a rolling thirty-day window, while we keep a year of check history — so the window is a deliberate choice rather than a limit, and you can tell the two apart. A window longer than the history we hold would be a claim about data that does not exist.statusis null only when the server did not answer at all — a timeout or a connection failure. A 403 or a 429 is a complete HTTP response with a real response time, and it is reported as the code it was.colois where the measurement was actually taken, not where your request landed.
How to verify one
signature is a compact JWS with an attached payload, signed with Ed25519 (alg: "EdDSA"). The claim appears twice on purpose: the readable fields are for people, and the copy inside the signature is the authoritative one. Decode the payload out of the signature and use that — then you never have to reproduce our exact byte ordering, and the whole canonicalisation problem disappears.
The public key is the same one we publish for Web Bot Auth, as a JWKS:
https://www.pingdomain.io/.well-known/http-message-signatures-directory
Match the kid in the JWS header against the key's RFC 8037 thumbprint, then verify:
// Node.js — verify an attestation
import { createPublicKey, verify } from 'node:crypto';
const res = await fetch('https://www.pingdomain.io/attest/acme/api.json');
const { signature } = await res.json();
const [header, payload, sig] = signature.split('.');
const claim = JSON.parse(Buffer.from(payload, 'base64url').toString());
const jwks = await (
await fetch('https://www.pingdomain.io/.well-known/http-message-signatures-directory')
).json();
const key = createPublicKey({ key: jwks.keys[0], format: 'jwk' });
const ok = verify(
null, // Ed25519 takes no digest
Buffer.from(`${header}.${payload}`),
key,
Buffer.from(sig, 'base64url'),
);
// Trust the decoded claim, not the fields beside it — and check the clock.
if (!ok || claim.expires_at < Date.now() / 1000) throw new Error('bad attestation');Check expires_at. An attestation is valid for an hour, which is what stops an old one being replayed as if it described today.
Proving the address is yours
We only attest a hostname whose owner has proved it is theirs. Anyone can point a monitor at somebody else's site; if that were enough to publish a signed claim about it, the signature would mean nothing.
When you tick Attest, we show you a token. Serve it as plain text at:
https://<your host>/.well-known/pingdomain-verify.txt
We fetch it once when you save, and again every day. Redirects are not followed — the proof has to come from the host the attestation names. If it disappears for three days running, the attestation stops being served rather than quietly going stale.
The hostname is frozen when you tick the box. If you later point the monitor at a different host, the attestation stops answering instead of silently vouching for an address nobody proved.
What it does not claim
An attestation says what we measured, from where, and when. It does not say your service is well built, that an outage was your fault, or anything about a window longer than we keep data for. It is deliberately a small, checkable claim — that is what makes it worth signing.
A claim about your uptime that someone else signs
Status pages and badges are on the paid plans; three monitors are free to start with. No card, no sales call.
Get started — free