What you can verify
Every claim LoopOver makes, the artifact that proves it, the command that checks it, and the trust assumption behind it — including the things you cannot verify.
Why this page exists
Verify this review walks through one check end to end: re-run the published backtest corpus and compare the numbers. This page is the wider contract — for every claim LoopOver makes, what artifact proves it, who can check it, and what you still have to take on trust.
The last column is the important one. A verification story that only lists what works is marketing; the assumptions and the gaps are what let you decide whether the guarantee is worth anything to you.
The matrix
1. Gate-decision integrity
Claim: the sequence of decisions was not silently reordered, deleted, or rewritten — and, since external anchoring shipped, that a wholesale chain rewrite is independently catchable too, not only tampering within it.
Every persisted verdict appends to a hash-chained ledger — each row's hash covers the previous row's hash, so any edit to history breaks the chain at a point you can locate.
Run this against the instance that reviewed the PR — the host in the review comment's own links.
A ledger belongs to the runtime that made the decisions, so api.loopover.ai is the wrong host to
ask (see "Which host you ask matters" below):
ORB=https://orb.example.org # the instance that reviewed the PR
curl -s "$ORB/v1/public/decision-ledger/verify" | jq
Returns { ok, checked, nextAfterSeq, tipSeq, tipHash, totalCount, prunedRecords }, and a break
object with a 409 status if the chain is inconsistent. No API key — anyone can run it.
Two response fields encode deliberate, published semantics rather than tolerance for tampering:
prunedRecords— decision records (the preimages) are retained for 180 days; ledger rows are kept forever. A ledger row whose hash-chainedcreatedAtis older than that window may therefore reference a pruned record: the chain checks still hold for it, only the content re-check is impossible, and it is counted here instead of reported asmissing_record. The tolerance keys on the ledger row's timestamp, which is inside the hash chain — backdating it to sneak a fresh deletion under the window breaksrow_hash_mismatchfirst. What pruning genuinely gives up: the row's committed digest stays published, but only a challenger holding the original preimage can still prove a historical rewrite of it.- Append grace (5 minutes) — a record and its chain row are two writes moments apart, so a
record younger than the grace window with no chain entry is "append in flight", not a break. Older
than that, it is reported: past the verified tip as
short_tail(the truncated-tail signature), or behind it asunchained_record(the failed-append signature — interior orphans are found by an anti-join over the whole record set, not just the tail).
Trust assumption: tamper-evident, externally anchored. The check above still catches sequence gaps, predecessor and row-hash mismatches, truncated tails, and content drift exactly as it always has. A scheduled job additionally publishes a signed, self-describing checkpoint of the chain's tip — hourly, or every 256 new rows, whichever comes first — to two places the operator does not control: a Sigstore Rekor transparency log, and a git commit cross-mirrored by GH Archive and Software Heritage the moment it's pushed. Rewriting history back past an anchor already published before the tamper now means forging that signature or fabricating matching evidence at an external mirror too — not just editing this repo's own tables. What this does not close: a rewrite made since the last anchor, followed by ordinary appends afterward, gets silently absorbed into every anchor published from then on — see "What you cannot verify" below for the precise boundary. Anchoring bounds how far back an undetected rewrite could reach; it does not make every row individually external-checkable in real time.
Which host you ask matters. A decision ledger belongs to the runtime that made the decisions,
and hosted review execution is retired — only self-host runtimes execute reviews, so each one
writes and anchors its own chain in its own database. api.loopover.ai therefore has no ledger
of its own to anchor and never will: its /v1/public/decision-ledger/* endpoints answer for an
empty chain, and its anchors response says so explicitly via status: "empty_ledger" rather
than an ambiguous empty list.
So every command on this page runs against $ORB, the instance whose reviews you are checking —
never against api.loopover.ai.
This is a deliberate boundary, not a gap. Aggregating every instance's records into one central chain would produce an anchored artifact that looks more authoritative while proving less: the central chain would be a re-chained aggregate nobody's verifier cares about, and each instance's real chain would still be unanchored.
Verify an anchor end to end
a. Fetch the most recent anchor and the currently-published signing key. status tells you which
state you are in before you read the list — anchored, empty_ledger (nothing decided yet),
unconfigured (no signing key provisioned), or pending (a ledger exists and is due its first
anchor):
curl -s "$ORB/v1/public/decision-ledger/anchors?limit=1" | jq '{status, anchor: .anchors[0]}' > anchor.json
curl -s "$ORB/v1/public/decision-ledger/anchor-key" | jq -c '.keys' > anchor-keys.json
b. Fetch the actual signed payload the anchor committed to. For the git-commit backend,
anchor.json's backendRef names the exact commit — the JSONL line it appended is the signed
artifact:
OWNER=$(jq -r '.backendRef.owner' anchor.json)
REPO=$(jq -r '.backendRef.repo' anchor.json)
SHA=$(jq -r '.backendRef.sha' anchor.json)
FILE_PATH=$(jq -r '.backendRef.path' anchor.json)
curl -s "https://raw.githubusercontent.com/$OWNER/$REPO/$SHA/$FILE_PATH" | tail -n 1 > anchor-signed.json
For the Rekor backend, backendRef instead names a transparency-log entry (shardBaseUrl, uuid)
— verify with rekor-cli verify --rekor_server "$SHARD" --uuid "$UUID", or fetch the entry directly
and decode its hashedRekordRequestV002 body.
c. Verify the signature offline, against the published key — zero contact with LoopOver required:
npx tsx -e '
import { readFileSync } from "node:fs";
import { verifyLedgerAnchorSignature, anchorKeyById, parseAnchorPublicKeys } from "./src/review/ledger-anchor.ts";
(async () => {
const signed = JSON.parse(readFileSync("anchor-signed.json", "utf8"));
const keys = parseAnchorPublicKeys(readFileSync("anchor-keys.json", "utf8"));
const key = anchorKeyById(keys, signed.keyId);
console.log(key && (await verifyLedgerAnchorSignature(signed, key.publicKeySpki)) ? "signature OK" : "SIGNATURE INVALID");
})();
'
d. Bind the anchor back to the live chain: fetch the row at the anchored seq and recompute its
hash yourself:
SEQ=$(jq -r '.payload.seq' anchor-signed.json)
curl -s "$ORB/v1/public/decision-ledger/row/$SEQ" | jq > row.json
npx tsx -e '
import { readFileSync } from "node:fs";
import { ledgerRowHash } from "./src/review/decision-record.ts";
(async () => {
const row = JSON.parse(readFileSync("row.json", "utf8"));
const signed = JSON.parse(readFileSync("anchor-signed.json", "utf8"));
const recomputed = await ledgerRowHash(row.prevHash, { seq: row.seq, recordId: row.recordId, recordDigest: row.recordDigest, createdAt: row.createdAt });
console.log(recomputed === row.rowHash && row.rowHash === signed.payload.rowHash ? "row hash OK -- anchor matches the live chain" : "MISMATCH");
})();
'
A mismatch here — a live row whose hash no longer matches what was anchored — is exactly what a wholesale rewrite before this checkpoint would produce. It's public, and anyone can check it, without asking LoopOver anything.
Bittensor on-chain anchoring is optional, separate corroboration — not part of this default
check. A third, Gittensor/SN74-audience-specific
backend (#9277) publishes the same signed
checkpoint as an on-chain commitment. A submitter on the operator's own node infrastructure — never
this Worker, and its dedicated anchor-only hotkey never leaves that infrastructure — fetches
GET /v1/public/decision-ledger/anchor-payload and commits sha256(signingInput) via the
commitments pallet's set_commitment(netuid, Data::Sha256), then reports the attempt (success
and failure, like every other backend) back into the public attempt log, where it appears as
backend: "bittensor". It's additive corroboration for that specific audience, never folded into
the default two-backend claim every verifier above is told to check.
Historical retrieval — read the block, not chain state. The commitments pallet's
CommitmentOf map is overwritten in place: only the latest commitment per (netuid, account)
survives in current chain state. To verify an older anchor, use its backendRef from the attempt
log — {netuid, blockNumber, blockHash, hotkey} — and query archive state at that block
(state_getStorage at blockHash, or the block's events), not the current tip. Then check the
stored commitment's Sha256 bytes equal sha256(signingInput) of the anchor's own
payload_json, and verify the payload's signature against the published anchor keys exactly as in
step (c) above. Any Bittensor archive node can answer this; the operator's own archive is merely
the convenient one — the same trust posture as the git backend's "GitHub hosts it, the mirror
cross-checks it."
2. Decision-record authenticity
Claim: this specific verdict is the document its digest names.
Every review comment prints its decision record's full 64-character digest, and the record itself is public:
curl -s "https://api.loopover.ai/v1/public/decision-records/OWNER/REPO/123" | jq
Returns { record, recordDigest }. The digest is a SHA-256 over the record's canonical JSON — keys
sorted, no whitespace — so you can recompute it yourself and confirm nothing was edited after the
fact. Anyone can run it.
Corrections are visible history, not silent replacement: a superseded verdict for the same commit is stored as a new revision, never an overwrite.
3. Published accuracy numbers
Claim: the precision figures on the fairness report are real computations over real, replayable history — not hand-entered numbers.
This is the walkthrough in Verify this review: export the checksummed
corpus, verify its checksum, re-run the same public scoring functions from @loopover/engine, and
compare. The checksum verification and the scorer replay are pure functions anyone can run over
a snapshot they hold, and the published numbers themselves are fetchable unauthenticated — but the
export step that produces the snapshot reads the deployment's own database and needs that
deployment's credentials, so it is an operator / self-host step today rather than an anonymous one.
Private repositories are the exception. A hosted tenant's review history cannot be published for anonymous third-party re-runs. Per-tenant checksummed export — so you can verify your own numbers even when the public cannot — is tracked and not yet shipped.
4. Attested execution
Claim: the backtest replay ran unmodified, inside genuine AMD SEV-SNP hardware.
When a run carries an attestation envelope, a skeptic verifies it entirely offline — no contact with LoopOver at all, trusting only AMD's published root certificate:
npx tsx scripts/verify-attested-run.ts --envelope envelope.json --expected expected.json
Exit 0 means verified; 1–8 each name a distinct failure class (sample attestation, bad
envelope, malformed report, untrusted chain, bad signature, TCB mismatch, measurement mismatch,
report_data mismatch); 9 is a usage or IO error.
Not reachable in production yet. The verification software is complete and shipped, but no SEV-SNP hardware is deployed, so no production run carries an envelope today. This row describes a path you can audit now and use once the hardware lands — not a guarantee currently in force.
5. What the digests actually pin
Claim: the configuration and prompt in force at decision time are committed to.
Each decision record carries a configDigest (the resolved gate policy), a settingsDigest (raw
effective settings), a promptDigest (the exact system prompt sent), and the model identifiers. A
changed policy or prompt produces a different digest, so silent reconfiguration is detectable.
Digests commit to inputs, never to outputs. Identical model identifiers and an identical
promptDigest can still produce different completions — inference is not deterministic. A digest
match means the same question was asked, never the same answer came back.
What you cannot verify
Stated plainly, because a boundary you discover later is worse than one published up front.
| Not verifiable | Why | What would change it |
|---|---|---|
| Live gate execution | The merge/close calls acting on your PR are not attested. Putting an attestation service in the live request path trades real availability for a proof that replay already provides more cheaply. | Standing design, not a pending gap. Revisited only if a tenant contractually requires attested live decisions. |
| Ground-truth honesty | Accuracy numbers are scored against recorded human-override events on maintainer infrastructure. Attestation proves computation, not data provenance — a perfectly attested run over cherry-picked labels is still cherry-picked. | Provenance at capture time (receipts verifiable by the humans whose overrides they record). Different mechanism entirely. |
| A rewrite made since the last anchor, then re-anchored consistently | An anchor proves a checkpoint existed at a time — it has no independent way to know what the tip would have been absent tampering. A rewrite to rows since the last published anchor, followed by ordinary appends afterward, gets silently absorbed into every anchor published from then on. Row 1's walkthrough only catches a rewrite that reaches back past an anchor already published before the tamper happened. | Tighter cadence (currently hourly or every 256 rows, whichever first) shrinks the window of opportunity; it cannot close it fully — that would mean anchoring every single write, at which point it stops being periodic checkpointing at all. |
| Model behavior | Row 5's limit — no artifact makes a non-deterministic model reproducible. | Nothing planned; this is a property of the models, not of our record-keeping. |
Self-hosting versus hosted
| Self-host operator | Hosted tenant | |
|---|---|---|
| What you verify | Everything, locally — your own database, your own corpus export, your own ledger | Public artifacts: ledger integrity, record authenticity, published numbers (public repos), digest scope |
| Corpus access | Direct, against your own Postgres | Public repos today; private-repo export is tracked and not yet shipped |
| Trust surface | You are the operator — you trust your own infrastructure | You trust LoopOver's operator honesty exactly as far as the tamper-evident limit allows |
Self-hosting genuinely closes more of the gap, and the hosted column is the weaker of the two. That asymmetry is real, and pretending otherwise would defeat the purpose of publishing this contract at all.