Skip to content
Liorith/Registry

Docs · API

Proof verification

The only endpoint that checks a signature. It answers whether a specific request really came from a registered site.

Endpoint

GET/verify?site=…&host=…&ts=…&nonce=…&sig=…
siterequiredstringSite id the proof was signed for.
hostrequiredstringHost the proof claims. Must match one of the site’s allowed domains.
tsrequirednumberUnix seconds at signing time.
noncerequiredstringRandom value, single use. The registry consumes it, so a proof verifies exactly once.
sigrequiredstringEd25519 signature, base64url (v2). Hex for the legacy HMAC scheme (v1).

All five are required. A missing one is still a registry outcome: malformed in the body, with HTTP 200. The one case where this endpoint does not produce an outcome is unavailable replay protection, covered at the bottom of this page.

What is checked, in order

  1. All five parameters present and ts numeric.
  2. The host is not on the blocklist.
  3. The site exists.
  4. Its state permits verification — suspended, inactive and former are refused here, before any cryptography runs.
  5. A credential is on file for the site.
  6. The timestamp is inside the window and the signature is valid for the exact message.
  7. The nonce has not been used before, and is consumed now.
  8. The host matches one of the site's allowed domains.

The order matters when reading a result: a site_suspended answer means the signature was never examined.

Response

resultstringThe outcome. All fourteen values are listed on the status codes page.
hoststringThe normalised host from the request.
tsnumberThe timestamp as supplied, or 0 if it was unparseable.
siteobject | undefinedPresent once the site was resolved — id, name, canonical_domain, status. Absent for unknown_site, malformed and blacklisted.
Verifiedjson
{
  "result": "verified_signed",
  "host": "demo.liorith.net",
  "ts": 1756800000,
  "site": {
    "id": "demo",
    "name": "Liorith Demo",
    "canonical_domain": "demo.liorith.net",
    "status": "active"
  }
}

Only two results mean yes

verified_signed is the full yes. valid_deprecated is a yes with a caveat: the signature is good, but the entry is on its way out. Treat everything else as no.

Do not accept a result merely because a site object came back. It is present for refusals too — host_mismatch, replay and site_suspended all carry one.

When verification cannot run

Replay protection needs the nonce store. If it is unreachable, the endpoint answers 503 with Retry-After instead of a result.

That is on purpose: without a consumed nonce, a captured proof could be replayed for the rest of its two-minute life. An outage must not quietly downgrade what a verification means, so it refuses to answer at all.