Context
Sub-issue of #9267 (external decision-ledger anchoring). This is, per the research posted on #9267, the single piece that actually delivers tamper-proofness — without it, an anchor proves a hash existed somewhere, not that it is this chain's hash at that sequence number. Build this first; every other sub-issue depends on it existing.
Requirements
GET /v1/public/decision-ledger/row/:seq → { seq, recordId, recordDigest, prevHash, rowHash, createdAt }, unauthenticated, matching the existing /v1/public/decision-ledger/verify posture exactly.
- Every field is already public-safe under the ledger row's own contract — nothing here is new exposure, it's the existing chain fields addressable by seq instead of only reachable via a full-chain walk.
- 404 for a seq that does not exist, not a 200 with nulls — a verifier must be able to distinguish "this row was never appended" from "this row has no data."
- OpenAPI registration, matching every other
/v1/public/* route.
- Auth exemption, same pattern as the other public routes.
Deliverables
- Endpoint implemented and registered
- Tests: valid seq returns exact chain fields matching what
appendDecisionLedger wrote; unknown seq is 404; response recomputation (sha256(prevHash || canonicalJson({seq, recordId, recordDigest, createdAt}))) equals the stored rowHash for a fixture chain
Expected outcome
A third party holding only an anchored (seq, rowHash) pair can fetch the live row at that seq and confirm — or refute — that the chain still says what the anchor says it said.
References
Sub-issue of #9267 (blocks every other sub-issue there). Existing pattern: GET /v1/public/decision-ledger/verify (src/api/routes.ts:1280). Ledger internals: src/review/decision-record.ts:309-479.
Context
Sub-issue of #9267 (external decision-ledger anchoring). This is, per the research posted on #9267, the single piece that actually delivers tamper-proofness — without it, an anchor proves a hash existed somewhere, not that it is this chain's hash at that sequence number. Build this first; every other sub-issue depends on it existing.
Requirements
GET /v1/public/decision-ledger/row/:seq→{ seq, recordId, recordDigest, prevHash, rowHash, createdAt }, unauthenticated, matching the existing/v1/public/decision-ledger/verifyposture exactly./v1/public/*route.Deliverables
appendDecisionLedgerwrote; unknown seq is 404; response recomputation (sha256(prevHash || canonicalJson({seq, recordId, recordDigest, createdAt}))) equals the storedrowHashfor a fixture chainExpected outcome
A third party holding only an anchored
(seq, rowHash)pair can fetch the live row at that seq and confirm — or refute — that the chain still says what the anchor says it said.References
Sub-issue of #9267 (blocks every other sub-issue there). Existing pattern:
GET /v1/public/decision-ledger/verify(src/api/routes.ts:1280). Ledger internals:src/review/decision-record.ts:309-479.