feat(ledger): Rekor v2 hashedrekord anchoring backend - #9397
Merged
Conversation
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Contributor
|
Important 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏳ LoopOver is waiting…LoopOver has seen this pull request and is waiting on CI checks to finish before reviewing it. This comment will update once the review runs. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟨 Waiting |
JSONbored
force-pushed
the
feat/ledger-anchor-rekor
branch
from
July 27, 2026 16:12
6d30a11 to
c88544e
Compare
JSONbored
force-pushed
the
feat/ledger-anchor-persistence
branch
from
July 27, 2026 16:15
320257a to
d61f18f
Compare
JSONbored
force-pushed
the
feat/ledger-anchor-rekor
branch
from
July 27, 2026 16:16
c88544e to
1d00704
Compare
5 tasks
JSONbored
force-pushed
the
feat/ledger-anchor-persistence
branch
from
July 27, 2026 16:21
d61f18f to
67ea1fe
Compare
JSONbored
force-pushed
the
feat/ledger-anchor-rekor
branch
from
July 27, 2026 16:22
1d00704 to
bd01dfc
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9397 +/- ##
==========================================
- Coverage 89.52% 88.62% -0.90%
==========================================
Files 840 842 +2
Lines 109777 109835 +58
Branches 26147 26159 +12
==========================================
- Hits 98275 97342 -933
- Misses 10239 11521 +1282
+ Partials 1263 972 -291
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Base automatically changed from
feat/ledger-anchor-persistence
to
feat/ledger-anchor-payload
July 27, 2026 16:44
Bundle ReportBundle size has no change ✅ |
10 tasks
Fourth sub-issue of #9267. Primary anchoring mechanism per the mechanism research: hashedrekord is exactly built for a plain hash + signature — Rekor never sees the anchor payload itself. No Fulcio, no OIDC, no new dependency (ECDSA P-256/SHA-256 is native WebCrypto, the same keypair #9270 already produces). Shard URL is env-configurable with a documented fallback, never hardcoded, per the research's explicit "shards roll annually" warning. Every path — non-2xx response, an unparseable response body, a network exception — records status:'failed' via #9271's persistence rather than throwing past the caller, so #9273's git backend still gets attempted even if this one fails. Caught and fixed a real bug during testing: this module was pre-stringifying its own error before handing it to recordLedgerAnchorAttempt, which then ran errorMessage() a SECOND time on what was now a plain string — and errorMessage only extracts .message from actual Error instances, so every failure path collapsed to the generic "unknown error" fallback regardless of what actually went wrong. Fixed by passing the raw unknown error through and teaching persistence to handle both an already-built string and a genuine Error, so the real diagnostic (a 429, a shape mismatch, "network down") reaches the public listing instead of being silently discarded. Storing the full Rekor TransparencyLogEntry (inclusion proof + signed checkpoint) for fully offline verification is deliberately deferred — online verification via rekor-cli against the stored shard URL + uuid works completely without it today.
…hive (#9273) (#9403) Fifth sub-issue of #9267. Secondary, complementary to Rekor: appends one JSONL line per anchor to a public repo via the GitHub Contents API and the existing makeInstallationOctokit chokepoint -- never a direct fetch to the GitHub API, matching every other GitHub write in this engine. Alone this is weaker than Rekor (GitHub is a trusted third party, force-push rewrites it). It becomes genuinely strong combined with mirrors nobody at LoopOver controls: GH Archive's hourly PushEvent export and Software Heritage's on-demand archival. A rewrite becomes independently detectable by checking an archive this repo doesn't control -- documented as a runnable cross-mirror verification procedure in the module's own header. Commits the identical canonicalized payload + signature Rekor anchors, so the two backends commit to the same fact, never a reshaped copy. Read-modify- write via the file's own sha as a compare-and-swap guard; never throws past the caller (missing repo, auth failure, rate limit, a raced sha all record status:'failed' via #9271's persistence, same posture as the Rekor backend). Also fixes a real bug found while building this: ledger-anchor.ts's re-export statement only carried sha256Hex, dropping canonicalJson entirely -- any consumer importing it (this backend needs it to commit the same payload Rekor anchors) got undefined. Applied at the actual origin (#9270/PR #9392) and rebased through the whole stack, not patched over downstream.
JSONbored
force-pushed
the
feat/ledger-anchor-rekor
branch
from
July 27, 2026 16:59
7f4eda6 to
1c7a6b8
Compare
This was referenced Jul 27, 2026
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
Fourth sub-issue of #9267 (external ledger anchoring), stacked on #9271 (#9395). Primary anchoring mechanism per the mechanism research:
hashedrekordis exactly built for a plain hash + signature — Rekor never sees the anchor payload itself. No Fulcio, no OIDC, no new dependency (ECDSA P-256/SHA-256 is native WebCrypto, the same keypair #9270 already produces forSignedLedgerAnchor).LOOPOVER_LEDGER_ANCHOR_REKOR_SHARD_URL) with a documented fallback, never hardcoded — per the research's explicit "shards roll annually, don't hardcode a log URL" warning.status: 'failed'via ledger: anchor persistence + public attempt log (success and failure both visible) #9271's persistence rather than throwing past the caller, so ledger: git-commit anchoring backend, cross-verified against GH Archive / Software Heritage #9273's git backend still gets attempted even if Rekor is down.TransparencyLogEntry(inclusion proof + signed checkpoint, for fully offline verification) is deliberately deferred — online verification viarekor-cliagainst the stored shard URL + uuid works completely without it today. Documented as an enhancement, not a gap.A real bug caught and fixed during testing, not just a mock passing: this module was pre-stringifying its own error before handing it to
recordLedgerAnchorAttempt, which then ranerrorMessage()a second time on what was now a plain string — anderrorMessageonly extracts.messagefrom actualErrorinstances, so every single failure path collapsed to the generic"unknown error"fallback regardless of what actually went wrong (a 429, a malformed response, a real network exception all looked identical on the public listing). Fixed by passing the rawunknownvalue through and teachingledger-anchor-persistence.tsto handle both an already-built descriptive string and a genuineError.Closes #9272.
Test plan
npx vitest run test/unit/ledger-anchor-rekor.test.ts— 9/9, 100% statement/branch/function/line coveragefetchis always injected — never a real network call to Rekor; the property under test is this module's own request/response/persistence logichashedRekordRequestV002schema (digest, signature content, verifier public key +keyDetails)null(never throws) for 8 distinct malformed shapesstatus: 'failed'row with the real error text (not the "unknown error" bug described above) and to resolve, never reject, past the callerbackendRef(shard URL + log index + key id + uuid)ledger-anchor-persistence.test.ts(8/8) and the two route test files (22/22) still green after the persistence-layer fixnpx tsc --noEmitclean ·npx tsx scripts/check-import-specifiers.tsclean