The open-source verifier
Every Web Reactions count can be recomputed from public data by a tool you can read and run yourself. The
web-reactions-verifier has no privileged access — it sees exactly what you see: the public log, the signed checkpoints, and the public anchors.
What the verifier checks
Point it at the public log and it will, in one run:
- Refetch everything. The full log from the public API, plus every signed checkpoint from the public anchor — nothing is trusted as served.
- Recompute the hashes. Every entry hash and the Merkle root are rebuilt from scratch, and the hash chain is verified to be unbroken.
- Verify the signatures. Each checkpoint's Ed25519 signature is checked against the pinned public key, and the entry count is checked to only ever grow.
- Replay the counts. The log is folded back into per-target totals from scratch and compared against the live counts the API serves — the counters cache is derived, never authoritative.
- Audit the revocations. The public revocation feed is checked against the
op=4entries actually present in the log, and those reversals are subtracted while recomputing the final counters. - Compare the anchors. The checkpoint the API serves is compared against the copy in
web-reactions-log, which catches a "split view" where we would show one history to you and another to everyone else. - Cross-check an independent log. Each checkpoint is confirmed to be present in Sigstore Rekor, a public transparency log run by a different organisation, carrying our exact signed bytes, so a second party we don't operate has independently witnessed each checkpoint. This runs by default; an unreachable Rekor is reported as skipped, never a false failure.
- Walk to Bitcoin. With the optional
--otsdeep audit, the OpenTimestamps proof is walked down to the actual Bitcoin block that seals a checkpoint root, so the replayed history cannot be backdated or rewritten.
What exactly a checkpoint is, and where each piece of data lives, is on the transparency log page.
Verify it yourself
No cloning needed; one command runs the checks above:
npx web-reactions-verify --api https://api.webreactions.app \
--repo https://raw.githubusercontent.com/khasky/web-reactions-log/main \
--target github/1 --target also compares one live count against the replayed total; add --ots for the Bitcoin deep audit. The same checks run daily against production and feed the
public status page.
The raw entries are also mirrored into the public log repository, so the whole audit can run without contacting our API at all — against a clone, a mirror, or the repo itself:
npx web-reactions-verify --entries repo \
--repo https://raw.githubusercontent.com/khasky/web-reactions-log/main In this mode the checkpoint under test comes from the repo's own anchor and every entry from its entries/ shards — nothing we serve live is trusted or even consulted.
Add --stats for a per-day activity report (reactions, distinct pseudonymous authors, revocations) derived from the entries alone — the same series our
status page charts, recomputed without us. The verifier also cross-checks the signed daily statistics files we publish to the log repository against what the log itself contains.
Run your own scheduled audit
The verifier repository ships the same GitHub Actions workflow we use: it runs the full verification daily and reports the verdict. Fork
web-reactions-verifier, enable Actions on your fork, and set the repository variable LOG_PUBKEY to the key below — from then on your copy audits our production log every day, on infrastructure we don't control. The more independent
watchers, the less anyone has to take our word for anything.
The log key
Every checkpoint and daily statistics file is signed with the project's Ed25519 log key. The public half lives in exactly one authoritative place — pinned in the verifier source (and printed in its README) — so the verifier works with no key argument at all. This page deliberately does not restate the value: a copy here could silently drift from the one the tool actually checks against.
Substituting the key would mean tampering with the public verifier repository itself, in the open and under version control, and every historical checkpoint signed with the real key would still expose the swap.
What this proves — and what it doesn't
Strong claims need clear edges. Here is exactly what the cryptography guarantees, and where it stops.
What the math proves
Every count is a replay of public entries — the total is whatever the log adds up to, nothing more. Nothing is removed silently: the only way to change a number is to append a new entry that is signed, anchored, and permanently visible. If the served history ever differs from the anchored one, the verifier fails and tells you where.
What the math can't prove
A revocation carries a public reason_code, but no hash can prove the stated reason is truthful. Cryptography proves what was claimed and when, not that the claim is true. That limit is inherent to any transparency log, and we would rather state it than let you assume otherwise.
Why abuse is still detectable
Like Certificate Transparency, the log is built for detection, not prevention — and detection has teeth here:
- Revocations are whole-account. There is no way to reverse one inconvenient vote. Removing a vote means visibly reversing that account's entire history, and that collateral noise shows up on the public feed and the status page.
- The affected user is a living witness. The extension keeps your reaction history on your device. If your votes vanish from the counts without your request, you hold the evidence.
- Anyone can watch. The revocation feed is public, the status page charts daily revocation counts by reason, and the open-source verifier flags any pseudonym whose history is only partially revoked.
Check it yourself
None of the above asks for belief. The log, the checkpoints, and the tooling are public: point the verifier at them and watch it agree.