Skip to content

orb(trust): the decision ledger has no external anchor and verify returns no hash — wholesale re-chaining and tail truncation both pass clean #9122

Description

@JSONbored

Problem

verifyDecisionLedger proves only that the ledger is self-consistent with itself. It cannot detect
the two tampering modes that matter most, because it emits no value an outside party can pin.

1. It returns no hash. The response is { ok, checked, nextAfterSeq, break? }
(src/review/decision-record.ts:233-256). Every hash it computes is discarded — prevHash = row.rowHash
stays a local. An observer polling daily has nothing to persist today and re-compare tomorrow. The chain
begins at a compile-time constant (LEDGER_GENESIS_HASH = "0".repeat(64), :184) and every row hash is
derived from data inside the same database the operator controls. Whoever controls that database can
rewrite any row, re-chain from genesis, and verify returns ok: true forever.

2. Tail truncation passes clean. The loop terminates when results runs out and returns
{ ok: true, checked } with no expected-length check:

246:  for (const row of results) { ...gap / predecessor / row-hash checks... }
255:  return { ok: true, checked, nextAfterSeq: results.length === bounded ? ... : null };

DELETE FROM decision_ledger WHERE seq > 100; — dropping the newest rows, i.e. the most recent and most
disputable decisions — leaves a chain that verifies perfectly. The existing tests cover mid-chain delete,
predecessor rewrite, and row rewrite (test/unit/decision-record.test.ts:202/207/212) but never tip
deletion, which is why this survived.

Why this is the load-bearing gap

migrations/0180_decision_ledger.sql:9-12 is honest about exactly this — "a self-operated chain is
tamper-EVIDENT, not tamper-PROOF — the operator can still rewrite wholesale"
— and says "(module header
repeats this)"
. It does not: neither decision-record.ts's module header (~1-13) nor its ledger section
header (~181-201) carries the caveat. The only surface anyone outside the repo reads is the route comment
(src/api/routes.ts ~1266), which states the unqualified claim: "any observer can confirm no decision
was deleted, reordered, or rewritten."

This is threat T3 in #8136's table — "the maintainer's infrastructure is untrusted outright" — which
that spike classified as covered by neither reproducibility nor attestation, and deferred to #8540 as
demand-triggered. The trigger has fired: an external network is now asking us to substantiate the
trust story, and the decision ledger is the artifact we would point them at.

Anchoring is also what makes attestation worth having. The #8534 epic attests that code ran over data
with a given checksum
; it says nothing about whether the recorded decision history is complete. Without
an anchor, an attested runner still reads from a rewritable ledger.

Requirements

  • Return tipSeq and tipHash (and the total row count) from verifyDecisionLedger, so a third party
    can checkpoint. Add an explicit expected-length check so a short tail is a break, not ok: true.
  • Publish a periodic external anchor of {seq, rowHash, at} to a medium the operator cannot silently
    rewrite. Evaluate at minimum:
  • Verification must be reproducible by a non-JS implementer: publish the row-hash preimage definition and
    the canonicalJson algorithm as a written spec with test vectors. Today the algorithm is correct
    (recursive key sort, array order preserved, throws rather than silently mis-digesting) but exists only
    as TypeScript — nobody can reimplement it against a spec.
  • Propagate 0180's honest-limit paragraph into the module header and the route comment, and correct
    the route comment now, ahead of the anchor landing. An overclaim on a public surface should not wait
    for the fix.

Test Coverage Requirements

99%+ patch coverage, branch-counted. Explicit regression test for tip deletion (the case the current
suite misses), plus both arms of the length check and the anchor-publication path.

Links & Resources

Boundaries

Ledger verification, anchoring, and the honesty fix. No change to the record schema or to what decisions
are recorded (that is a sibling issue), and no TEE/attestation work (#8534 owns that).

maintainer-only — public trust-architecture authority.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions