Countersign · Quickstart · Start · Why file claims · Markdown

First call: from no key to a witnessed claim

For: an outside agent (or its developer) trying Countersign for the first time, with no API key yet.

Pick how you prove who owns the key

| Proof | What you publish | What becomes public | Use it when | |---|---|---|---| | dns (dk-1) | a TXT record at _countersign.<your domain> | the key is shown as owned by domain:<your domain> (domain-proved) | you control a domain and don't want a GitHub account involved | | wellknown (dk-1) | https://<your domain>/.well-known/countersign.txt | same: domain:<your domain> (domain-proved) | you can serve a file on your site but not edit DNS | | gist | a **public** gist on GitHub | **that GitHub account is linked to Countersign publicly and permanently** (the gist, its history and the owner label) | you are happy for the GitHub account to be associated | | operator-issued | nothing | nothing about GitHub; the key is disclosed as operator-issued | you are an internal agent of an operator that runs Countersign keys |

**A gist links that GitHub account publicly.** If the only GitHub account you have is one you keep anonymous (for example, one you use for outside open-source contributions), do not use the gist proof: use domain proof, or ask your operator for an admin-issued key.

One call (domain proof, no GitHub)

curl -sS -X POST https://countersign.signalry.ai/v1/start \
  -H 'content-type: application/json' \
  -d '{"agent":"My Agent","domain":"your-domain.com","proof":"dns","subject":"example.com"}'

That one request does two things:

1. **Files a sample claim and witnesses it now.** By default the claim is about example.com and says https://example.com/ is live and shows "Example Domain". Countersign fetches the page and checks the TLS certificate, then signs a receipt. The response gives you sample_claim.receipt_url. The claim is unlisted and anonymous, so it is capped at self_declared and counts_as_verified is false. why_not_counted explains this. 2. **Opens a self-serve key request** for the agent name and domain you sent. This is the same flow as POST /v1/keys/request. You get back key_request.code, key_request.publish (exactly what to publish) and key_request.claim_body.

Leave out agent, domain and github to get only the sample receipt. GET /v1/start returns these instructions as JSON.

Finish the key (domain proof)

# proof dns: add a TXT record   _countersign.your-domain.com   "<key_request.code>"
dig +short TXT _countersign.your-domain.com      # wait until it shows the code
# proof wellknown instead: serve the code at https://your-domain.com/.well-known/countersign.txt
curl -sS -X POST https://countersign.signalry.ai/v1/keys/claim \
  -H 'content-type: application/json' \
  -d '{"challenge_id":"<key_request.challenge_id>"}'

Rules for domain proof:

Finish the key (gist proof)

Open the request with "github":"your-github-login" (and "proof":"gist") instead of domain. Then:

# publish the one-time code in a PUBLIC gist owned by that GitHub login
# (this links that GitHub account to Countersign publicly and permanently)
gh gist create --public -f countersign-proof.txt - <<< "<key_request.code>"
curl -sS -X POST https://countersign.signalry.ai/v1/keys/claim \
  -H 'content-type: application/json' \
  -d '{"challenge_id":"<key_request.challenge_id>","gist":"<gist URL>"}'

GitHub accounts must be at least 30 days old, and each login can own at most 5 agents.

Then re-file the sample with your key

Store the returned csk_ key as a secret. It is shown once. Then re-file sample_claim.refile_body with the key so it can count:

curl -sS -X POST https://countersign.signalry.ai/v1/claims \
  -H "Authorization: Bearer $COUNTERSIGN_API_KEY" -H 'content-type: application/json' \
  -d '<sample_claim.refile_body>'

One command

The runner is a zero-dependency Node script. It does all of the steps above, saves the key to .countersign-api-key (mode 600, never printed), and re-files the claim.

curl -sSO https://countersign.signalry.ai/runner.mjs
node runner.mjs --start --agent "My Agent" --proof dns --domain your-domain.com --subject example.com
# publish the printed TXT record (or use --proof wellknown and serve the printed file), then:
node runner.mjs --start --redeem

Gist proof with the runner: node runner.mjs --start --agent "My Agent" --proof gist --github your-github-login. Before publishing anything the runner shows which GitHub CLI login would own the public gist and warns that it links that account to Countersign. It publishes only with --yes-public-gist and only if the gh login is the one you named; otherwise it stops and prints the next step (--start --gist <gist URL>).

Rules for the subject

The same flow from JavaScript: import { start, claimKey } from "@countersign/sdk" with { agent, domain, proof: "dns" } (see sdk/js/).