feat(ledger): GET /v1/public/decision-ledger/row/:seq — bind an anchor to the live chain - #9389
Merged
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | d9839be | Commit Preview URL Branch Preview URL |
Jul 27 2026, 03:52 PM |
JSONbored
force-pushed
the
feat/ledger-row-endpoint
branch
from
July 27, 2026 15:35
fa1f0df to
2f972de
Compare
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Bundle ReportChanges will increase total bundle size by 1.8kB (0.02%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: loopover-uiAssets Changed:
|
…r to the live chain (#9269) First and foundational sub-issue of #9267 (external ledger anchoring). Everything else in that epic depends on this existing. The gap: an anchor published to a transparency log or public git repo commits to a (seq, rowHash) pair. On its own that proves only that SOME hash existed somewhere at some time — not that it is still THIS chain's hash at that seq. Nothing today can fetch a single row and recompute its chained hash: verify walks internal self-consistency and returns only the tip plus the first break. With this route a third party fetches the live row and recomputes sha256(prevHash || canonicalJson({seq, recordId, recordDigest, createdAt})) against what was anchored. An operator who deleted the ledger and re-chained from genesis produces a different rowHash at every anchored seq, so every published anchor then fails that comparison independently and publicly — which is exactly the wholesale-rewrite gap migrations/0180_decision_ledger.sql names as its own honest limit. Public-safe by the same argument the verify route already makes: hashes, a seq, a timestamp, and the already-public record id. Never record contents (pinned by test). 404 rather than 200-with-nulls for an unappended seq, so "never appended" stays distinguishable from "appended with empty fields". Auth exemption added in this same commit as the route, so it cannot repeat #9120's drift where a doc comment claimed unauthenticated but the exemption list disagreed.
JSONbored
force-pushed
the
feat/ledger-row-endpoint
branch
from
July 27, 2026 15:48
2f972de to
d9839be
Compare
Contributor
|
Important 🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪 🔍 LoopOver is reviewing…AI analysis is in progress. This comment will update when the review is complete. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟪 Reviewing |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9389 +/- ##
===========================================
+ Coverage 75.38% 88.57% +13.18%
===========================================
Files 275 837 +562
Lines 58023 109650 +51627
Branches 6181 26113 +19932
===========================================
+ Hits 43739 97117 +53378
+ Misses 14014 11561 -2453
- Partials 270 972 +702
Flags with carried forward coverage won't be shown. Click here to find out more.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First and foundational sub-issue of #9267 (external ledger anchoring) — every other sub-issue in that epic depends on this existing.
The gap this closes. An anchor published to a transparency log or a public git repo commits to a
(seq, rowHash)pair. On its own, that proves only that some hash existed somewhere at some time — not that it is still this chain's hash at that seq. Nothing today can fetch a single row and recompute its chained hash:/v1/public/decision-ledger/verifywalks the chain's internal self-consistency and returns only the tip plus the first break.With this route a third party fetches the live row and recomputes
sha256(prevHash || canonicalJson({seq, recordId, recordDigest, createdAt}))against what was anchored. An operator who deleted the ledger and re-chained from genesis produces a differentrowHashat every anchored seq, so every published anchor then fails that comparison independently and publicly — exactly the wholesale-rewrite gapmigrations/0180_decision_ledger.sqlnames as its own honest limit.Public-safe by the same argument the verify route already makes: hashes, a seq, a timestamp, and the already-public record id. Never record contents (pinned by a test asserting the exact key set). 404, not 200-with-nulls, for an unappended seq, so "never appended" stays distinguishable from "appended with empty fields". The
requiresApiTokenexemption is added in the same commit as the route, so this can't repeat #9120's drift where a doc comment claimed unauthenticated but the exemption list disagreed.Closes #9269.
Test plan
npx vitest run test/integration/public-ledger-row-route.test.ts test/integration/public-decision-ledger-routes.test.ts— 22 passedrowHash— the anchor comparison fails, which is the whole point of the epicprevHashequals row N−1'srowHash, so anchors at different seqs chain together{createdAt, prevHash, recordDigest, recordId, rowHash, seq}— no record contentsnpm run ui:openapi:checkclean ·npx tsc --noEmitclean ·openapi.test.tspassing