https://api.useselo.xyz signs every answer. selo verify checks that signature against keys you trust and checks that the attestation is for the exact release and target you expect. A PASS for one commit cannot open the gate for another.
Prerequisites
- An attestation file, saved by
selo test --attestation <file>. See Run a release test. - A checkout of the Selo repository with
pnpm install --frozen-lockfile. No wallet is needed, and verification never pays. - Selo’s attestation keys: the live URL
https://api.useselo.xyz/.well-known/selo-attestation-keys.json, or a copy you committed.
Verify with the CLI
1
Get the keys
Terminal
Output
publicKey is the raw Ed25519 public key in base64url. The key id is selo-ed25519- followed by the first 16 hex characters of the SHA-256 of the raw key bytes. For CI, save this file once, review it and commit it, for example as selo/attestation-keys.json. Trusting a pinned file is stronger than trusting whatever a URL serves at the time.2
Run selo verify
Terminal
3
Read the result
Verifying the It exits
fixed-v2 attestation from the demo merchant’s Mainnet run against the published key prints:Output
0, the attested PASS.Terminal
2:
Output
What is checked
selo verify checks, in this order, and stops at the first failure:
The signature is Ed25519 over the payload’s canonical JSON: object keys sorted at every level and no whitespace. It covers the payload only, never itself.
--url is normalized the same way as when you tested, so pass the URL you passed to selo test.
deployment.id is the --release you gave selo test, recorded with verification: "asserted_by_client". Selo signs that you named this release when you tested; it does not check which build was deployed at that URL.Exit codes
A promotion step should require exit
0. Exit 1 is still a successful verification: Selo did sign a FAIL for that release.
What an attestation contains
This is the payload of thefixed-v2 attestation, abridged to its first two checks and evidence rows. The full file is docs/evidence/g5-release-gate-fixed-v2-attestation.json in the Selo repository.
selo-attestation.json
The attestation holds no secret, no request body and no report token. It is safe to publish. See Signed attestations for the full schema.
Verify in CI before promotion
Use the GitHub Action inverify mode as the gate of the promote job. The Selo repository’s Release gate demo workflow does exactly this with the committed Mainnet attestations:
.github/workflows/release-gate-demo.yml
ALLOW. On 26 October 2026 the broken-v1 run failed the gate and skipped promotion, and the fixed-v2 run passed and promoted. See Gate deploys in GitHub Actions.
Verify without Selo’s code
The format is plain enough to check with Node’s standard library alone. This script performs the core checks ofselo verify: trusted key, signature, schema version, release, URL and method.
verify-attestation.mjs
Terminal
Output
verdict of the broken-v1 attestation from FAIL to PASS, and the script stops with the signature does not verify.
Troubleshooting
not verified: it is signed by …, which is not among the trusted keys
not verified: it is signed by …, which is not among the trusted keys
Your pinned key file does not contain the key that signed this attestation. Compare it with
https://api.useselo.xyz/.well-known/selo-attestation-keys.json. If Selo published a new key, review it and add it to your file deliberately.not verified: it attests release …, not …
not verified: it attests release …, not …
The attestation is for another release id. In CI, make sure
release is the same value in the test and the verify steps, for example ${{ github.sha }} in both.not verified: it attests POST https://…, not GET https://…
not verified: it attests POST https://…, not GET https://…
The URL or method differs from the tested target. Pass exactly the URL and method you tested.
not verified: the file is not a Selo release attestation
not verified: the file is not a Selo release attestation
The file is missing, is not JSON, or is not an envelope with
payload and signature. Check the path; in CI, check that the artifact was downloaded. The summary file selo-gate.json is not an attestation.The run said attestation none
The run said attestation none
Selo’s answer carried no attestation for that run, so there is nothing to verify. Do not promote on the summary alone.
Next
Signed attestations
The attestation model and every payload field.
selo verify reference
Every flag and exit code.