# Resolve over DNS, v1 (ccr1)

Every domain's CrawlCheck Resolve verdict, as a signed TXT record.

## Ask

    dig +https @crawlcheck.io example.com._v.crawlcheck.io TXT
    curl -s 'https://crawlcheck.io/dns-query?name=example.com._v.crawlcheck.io&type=TXT'

The server speaks DNS-over-HTTPS (RFC 8484): GET /dns-query?dns=<base64url wire message>, POST application/dns-message, or the JSON form above. It answers only for names under _v.crawlcheck.io.

## The record

    v=ccr1;d=example.com;st=measured;read=allow;cite=warn;connect=unsupported;transact=require_confirmation;o=<observed_at>;f=<fresh_until>;l=<log leaf>;k=<key id>;x=<public key>;s=<signature>

- read, cite, connect, transact: the decisions of the domain's Resolve answer (allow, warn, require_confirmation, block, unsupported). A domain with no record says st=not_measured.
- imp=suspected or inc=active appear when an impersonation suspicion or an owner incident blocks connect and transact.
- o and f: when the answer was observed and until when it is fresh. The TTL is the time left until f (60 s to 1 h).
- l: the answer's leaf in the Resolve transparency log (/api/v1/log/proof?leaf=).
- k, x: the signing key id (RFC 7638 thumbprint) and the Ed25519 public key; the key must be one of those published at /.well-known/http-message-signatures-directory.
- s: Ed25519 signature, base64url, over "crawlcheck-dns-v1\n" followed by the record up to (not including) ";s=".

Long records arrive as several TXT strings; join them without spaces.

## Verify

    node crawlcheck-verify.mjs --dns "<record>" [--online]

Checks the key id against the public key, the signature, and that f is in the future; --online also checks the key is published.
