Skip to content

orb(integrity): the decision-ledger verify endpoint never reconciles record_json against record_digest — the check its own comment promises does not exist #9078

Description

@JSONbored

verifyDecisionLedger (src/review/decision-record.ts ~196-250, exposed at src/api/routes.ts ~1268) walks decision_ledger only. It re-derives each row_hash from (prev_hash, seq, record_id, record_digest, created_at) — but never reads decision_records, never recomputes contentDigest(JSON.parse(record_json)), and never compares it to the stored record_digest.

Consequences:

  1. Content tampering is undetectable. An operator or a bug that rewrites decision_records.record_json while leaving record_digest alone passes verification green. The chain proves a digest existed; nothing proves the stored content still matches it.
  2. Unchained records leave no gap. persistDecisionRecord (~110-136) wraps appendDecisionLedger in the same try/catch as the insert and swallows failures with a console.warn. A record whose chain append exhausted its 3 retries lands unchained — and because seq is last+1, there is no gap to detect. The code comment asserts this is "caught by the verify endpoint's record/ledger reconciliation". That reconciliation does not exist anywhere in the codebase.
  3. Superseded preimages are destroyed. ON CONFLICT(id) DO UPDATE on decision_records overwrites. The chain commits to a digest whose content is gone, so a superseded merge/close decision cannot be reproduced under challenge.
  4. DecisionRecord (~55-80) carries no author identity, so the ledger cannot answer who a decision was for — relevant when the decision is what causes an upstream payout.

This matters more than usual here: the decision ledger is the non-repudiation story behind a publicly advertised statistical guarantee.

Fix

  1. Implement the promised reconciliation: join decision_records, recompute the digest from record_json, and report mismatches and unchained records as distinct break kinds.
  2. Make a failed ledger append raise a dedicated alarm rather than a swallowed warn.
  3. Make decision_records append-only (new row per revision) instead of upserting, so preimages survive.

Refs #9050, #9068.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.orbGittensory Orb related - maintainer self-hosting analytics.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions