counted

Verify Counted

End-to-end encryption is only worth something if the code running in your browser is the code we say we serve. This page exists so you do not have to take our word for it.

What is published

Every version of Counted is built once, in a frozen environment, and the result is fingerprinted: the server, the WebAssembly module, its JavaScript and every file of the app, as SHA-256. The list is served by the very version that is running:

https://counted.fr/SHA256SUMS.txt

Each line is a file's fingerprint and its path on counted.fr: assets/web_bg-….wasm, assets/web-….js, the fonts and stylesheets. The same list is in every release's notes, away from the server that serves it.

Check what you receive

From a terminal, download the list and every file it names, then compare:

curl -sO https://counted.fr/SHA256SUMS.txt
for f in $(awk '$2 != "server" {print $2}' SHA256SUMS.txt); do
  curl -s --create-dirs -o "$f" "https://counted.fr/$f"
done
sha256sum -c --ignore-missing SHA256SUMS.txt

Every line must end in OK. server is the server executable; it is not served, its fingerprint is there for comparison with a build.

Build it yourself

The build is reproducible: base images and compiler pinned by digest, same paths, same flags. The client's source code is public at github.com/counted-labs/counted, one tag per version, under the AGPL-3.0 licence. From it, three commands rebuild what this site serves and compare:

git clone --branch vX.Y.Z https://github.com/counted-labs/counted.git && cd counted
docker buildx build --platform linux/amd64 -f Dockerfile.client --target sums --output type=local,dest=out .
curl -s https://counted.fr/SHA256SUMS.txt | grep -v '  server$' | diff - out/SHA256SUMS.txt

An empty diff means every file your browser receives is byte for byte what those sources produce. The server line is the native binary: its code is not published, it is skipped from the comparison, and it never sees your data in clear — everything is encrypted on your device before it is sent. Each GitHub release also carries the SHA256SUMS.txt of its version, a copy independent of this server. We run the same rebuild ourselves, without cache, for every release.

Check the signature

Every SHA256SUMS.txt is signed by our build pipeline with Sigstore, and the signature is written to a public append-only log: a release's list cannot be swapped afterwards without it showing. With cosign:

base=https://github.com/counted-labs/counted/releases/download/vX.Y.Z
curl -sLO $base/SHA256SUMS.txt -sLO $base/SHA256SUMS.txt.sigstore.json
cosign verify-blob --bundle SHA256SUMS.txt.sigstore.json \
  --certificate-identity "https://gitlab.com/jbosi/counted//.gitlab-ci.yml@refs/heads/main" \
  --certificate-oidc-issuer https://gitlab.com SHA256SUMS.txt
curl -s https://counted.fr/SHA256SUMS.txt | cmp - SHA256SUMS.txt

Verified OK, then no output from cmp: the list this site serves is exactly the one the build of that release signed. The signature proves where the list comes from, not that the code is safe — that is what the two checks above are for.

What this proves

  • The code your browser runs is the code whose fingerprint is published.
  • Two people checking at the same time receive the same code.
  • A version rebuilt from its sources yields the same files — so nothing was added between the code and what is served.

What this does not prove

  • That the code is correct: a fingerprint attests a file's identity, not its quality.
  • That the browser will refuse a different file: the check is manual. Binding the browser to the fingerprint (SRI) and signing releases (Sigstore) are the next steps.
  • The Android and iOS apps, which the stores sign; they are not covered here.

A fingerprint that does not match? Write to us with the line and the date.