Skip to content

Public · no sign-in required

Verify a certificate

Enter a serial. The edge checks an HMAC signature computed over the certificate's immutable fields — holder, title, academy, issue date, score. If any of those fields were changed in the database after issue the signature no longer matches, and this page says tampered rather than quietly showing you a valid-looking result.

Serials are printed at the foot of every issued certificate and are safe to share — they reveal nothing beyond what the certificate itself states.

Issue and verification

Signed at issue, checked at read

A certificate is not a PDF that happens to live in a bucket. It is a signed row; the rendered file is a projection of that row and is regenerated on demand.

Immutable fields only

The HMAC covers serial, holder public id, holder display name, academy, title, issue date and score. Presentation — wording, layout, logo — is not signed, so the design can be improved without invalidating a decade of certificates.

PDF where it can, SVG where it must

In edge mode the Worker writes a real PDF when every character of the holder's name is Latin-1 representable, because embedding a full Unicode font at the edge is not affordable. An Arabic or Chinese name receives a crisp SVG instead. After cutover the origin renders PDF for everyone. The page tells you which you are getting rather than silently transliterating a person's name.

Cached immutably

Rendered artefacts land in R2 under a content-addressed key with an immutable cache header. A certificate is generated once; every later download is a free bucket read. Revocation does not delete the artefact — it changes the verdict this page returns.

The four verdicts

Anything that is not verified is stated plainly, with the reason.
VerdictMeaning
verified Row exists, signature matches, not revoked, not expired.
revoked Row exists and the signature matches, but the issuer withdrew it. The reason is shown.
tampered Row exists and the signature does not match. Field values are withheld — showing them would help an attacker tune the forgery.
not found No row with that serial. Deliberately indistinguishable from a serial that never existed.

Why not a blockchain

The property people actually want is the issuer cannot silently rewrite history. An HMAC gives tamper-evidence against everyone who does not hold the key, which covers every third party who will ever check a certificate. It does not protect against the issuer re-signing a changed row — and neither does an anchored hash, unless the verifier independently kept the original. Buying a distributed ledger to solve a problem you have not actually solved is a poor trade.

What does help, and is in the design: every issue, revoke and re-issue writes an audit_log row that the issuer cannot delete through the console, and the console shows the operator the audit entry before it is written.

Claiming your own certificate

Certificates are claimed, not granted automatically, so that the issue date means something. Claim once and only once — a second claim returns the same serial rather than minting a duplicate.

  1. Finish the series. Every item at 100%, and the assessment passed.
  2. Press claim in your hub. The edge re-checks completion against progress rows; it does not trust the button.
  3. Refusals are specific. Not "criteria not met" — the exact items still incomplete, by name.
  4. Issue is idempotent. A unique constraint on (user, series) means a double-click produces one certificate.

What a refusal looks like

Live from the certificate service, showing this account's current state.

Go to your certificates