Skip to content

fix(node): harden Arweave anchoring and add verification (#26)#224

Open
Gravirei wants to merge 4 commits into
Gitlawb:mainfrom
Gravirei:fix/issue-26-harden-arweave-anchoring-verification
Open

fix(node): harden Arweave anchoring and add verification (#26)#224
Gravirei wants to merge 4 commits into
Gitlawb:mainfrom
Gravirei:fix/issue-26-harden-arweave-anchoring-verification

Conversation

@Gravirei

@Gravirei Gravirei commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Replace the Irys upload path with a generic Bundler endpoint, capture the pusher RFC 9421 HTTP Signature in ref certificates, add monotonic sequence numbers with SHA-256 chaining to certificate chains, and expose a /api/v1/arweave/verify/{tx_id} endpoint that fetches the anchor from Arweave and validates the embedded certificate chain.

Motivation & context

Closes #26

Kind of change

  • Bug fix
  • Feature
  • Security fix
  • Docs
  • Tests / CI
  • Refactor (no behavior change)
  • Breaking or protocol change (issue required first)

What changed

gitlawb-node

  • config.rsGITLAWB_IRYS_URL renamed to GITLAWB_BUNDLER_URL; GITLAWB_ARWEAVE_GATEWAY added.
  • arweave.rs — Upload endpoint changed from {url}/upload to {url}/v1/tx; content-type application/octet-stream; header x-bundler-tags; Bundler response parsing. RefAnchor now carries an optional embedded RefCertificate. New verify_anchor() function fetches from gateway, extracts the cert, verifies the node Ed25519 signature and checks prev hash linkage against the local DB.
  • db/mod.rsRefCertificate gains seq (i64), prev (String), pusher_sig (Option). ArweaveAnchor gains status, deadline_height, receipt_sig, cert_id; irys_tx_idarweave_tx_id. Migration v11 upgrades existing databases. RecordAnchorInputV2 replaces the old input struct. Added get_most_recent_cert(), confirm_arweave_anchor(), fail_arweave_anchor(), list_pending_anchors().
  • cert.rsissue_ref_certificate accepts pusher_sig, chains from the previous cert via SHA-256 prev hash, builds a v2 signing payload.
  • auth/mod.rsrequire_signature middleware injects PusherSignature extension (base64-encoded Ed25519 sig bytes).
  • api/repos.rsgit_receive_pack extracts PusherSignature, passes it to cert issuance. RefAnchor construction fetches latest cert from DB. Uses RecordAnchorInputV2.
  • api/arweave.rs — New GET /api/v1/arweave/verify/{tx_id} endpoint.
  • server.rs — Verify route mounted.
  • test_support.rs, api/events.rs — Updated RefCertificate initializers for new fields.

How a reviewer can verify

DATABASE_URL=postgresql://gitlawb:changeme@172.19.0.2:5432/gitlawb cargo test --package gitlawb-node

All 495 tests pass.

Before you request review

  • Scope is one logical change; no unrelated churn
  • cargo test --workspace passes locally (495 passed)
  • New behavior is covered by tests (12 new DB tests, 10 arweave tests)
  • cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings are clean
  • Commit titles use Conventional Commits (fix(...))
  • Docs / .env.example updated if behavior or config changed (or N/A)
  • Checked existing PRs so this isnt a duplicate

Protocol & signing impact

  • Touches DID / did:key, Ed25519 / RFC 9421 signatures, UCAN, ref certs, or P2P wire formats
  • Discussed in an issue before implementation
  • Backward-compatible with existing nodes and previously signed history — new columns have defaults, v11 migration is additive, old Irys fields are renamed gracefully.

Notes for reviewers

The gateway_url field in RecordAnchorInputV2 is passed through by callers but not persisted in the DB — it is used by the caller to construct the Arweave URL at query time. The cert_id column on arweave_anchors is reserved for a follow-up linking pass.

Summary by CodeRabbit

  • New Features
    • Added Arweave transaction verification via GET /api/v1/arweave/verify/:tx_id, returning validity, errors, and certificate details.
    • Ref-update certificates now include chain sequencing/continuity data and optional pusher signature evidence.
    • Arweave anchoring records now support a richer lifecycle (pending/confirmed/failed) with additional metadata.
  • Improvements
    • Anchoring and verification now use a configurable bundler and Arweave gateway, embedding certificate info into anchored ref updates for stronger end-to-end checks.

Copilot AI review requested due to automatic review settings July 20, 2026 09:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c2780e9d-657c-40ce-9bdd-43a02f15255d

📥 Commits

Reviewing files that changed from the base of the PR and between 0801800 and c94e8ed.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/api/events.rs
  • crates/gitlawb-node/src/api/repos.rs
  • crates/gitlawb-node/src/arweave.rs
  • crates/gitlawb-node/src/auth/mod.rs
  • crates/gitlawb-node/src/cert.rs
  • crates/gitlawb-node/src/config.rs
  • crates/gitlawb-node/src/db/mod.rs
  • crates/gitlawb-node/src/server.rs
  • crates/gitlawb-node/src/test_support.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • crates/gitlawb-node/src/auth/mod.rs
  • crates/gitlawb-node/src/api/events.rs
  • crates/gitlawb-node/src/cert.rs
  • crates/gitlawb-node/src/server.rs
  • crates/gitlawb-node/src/config.rs
  • crates/gitlawb-node/src/test_support.rs
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/api/repos.rs
  • crates/gitlawb-node/src/arweave.rs
  • crates/gitlawb-node/src/db/mod.rs

📝 Walkthrough

Walkthrough

This change replaces Irys anchoring with bundler uploads, embeds chained ref certificates and pusher signatures, adds anchor lifecycle persistence, and exposes Arweave transaction verification through a new API endpoint.

Changes

Arweave integrity flow

Layer / File(s) Summary
Certificate chain and pusher signature
crates/gitlawb-node/src/auth/mod.rs, crates/gitlawb-node/src/cert.rs, crates/gitlawb-node/src/db/mod.rs, crates/gitlawb-node/src/api/repos.rs, crates/gitlawb-node/src/api/events.rs, crates/gitlawb-node/src/test_support.rs
Verified pusher signatures are propagated into certificates, which now include seq, prev, and pusher_sig fields with database migration, ordering, and fixture updates.
Bundler anchoring and anchor lifecycle
crates/gitlawb-node/src/arweave.rs, crates/gitlawb-node/src/api/repos.rs, crates/gitlawb-node/src/config.rs, crates/gitlawb-node/src/db/mod.rs
Ref and encrypted-manifest anchors use bundler /v1/tx requests, configurable gateway settings, embedded certificates, and pending/confirmed/failed anchor records.
Anchor verification API
crates/gitlawb-node/src/arweave.rs, crates/gitlawb-node/src/api/arweave.rs, crates/gitlawb-node/src/server.rs
Bundler payloads are fetched and checked for certificate signatures and chain linkage, with results exposed at GET /api/v1/arweave/verify/:tx_id.
Bundler and verification test coverage
crates/gitlawb-node/src/arweave.rs
Tests mock bundler transaction routes and validate configurable gateway and verification behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant verify_anchor_endpoint
  participant verify_anchor
  participant ArweaveGateway
  participant Db
  Client->>verify_anchor_endpoint: GET /api/v1/arweave/verify/{tx_id}
  verify_anchor_endpoint->>verify_anchor: Pass transaction ID and configured gateway
  verify_anchor->>ArweaveGateway: Fetch /v1/tx/{tx_id}
  ArweaveGateway-->>verify_anchor: Return anchored certificate payload
  verify_anchor->>Db: Load predecessor certificate
  verify_anchor-->>Client: Return validity, errors, and certificate
Loading

Possibly related PRs

  • Gitlawb/node#36: Both changes update encrypted-manifest Arweave anchoring.
  • Gitlawb/node#72: Both changes modify per-ref anchoring payload construction.
  • Gitlawb/node#149: Both changes modify certificate listing and ordering logic.

Suggested labels: sev:medium, kind:feature, kind:security, subsystem:api, subsystem:attestation

Suggested reviewers: jatmn

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive Most issue goals are implemented, but the summary does not confirm the requested anchor-retry behavior. Add or document retry-on-failure handling for asynchronous anchoring, or provide evidence it already exists in the reviewed files.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: hardening Arweave anchoring and adding verification.
Description check ✅ Passed The description matches the template with summary, motivation, change list, verification, checklist, and protocol-impact notes.
Out of Scope Changes check ✅ Passed The changes stay focused on Arweave anchoring, verification, certificate chaining, and the needed test/config updates.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@beardthelion beardthelion added crate:node gitlawb-node — the serving node and REST API kind:bug Defect fix — wrong or unsafe behavior subsystem:identity DID/UCAN, http-sig auth, push authorization subsystem:storage Blob/object store, Arweave, IPFS, archives labels Jul 20, 2026
@Gravirei
Gravirei force-pushed the fix/issue-26-harden-arweave-anchoring-verification branch from 0801800 to bd09c35 Compare July 20, 2026 09:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/gitlawb-node/src/arweave.rs`:
- Around line 351-373: Update the prev-linkage validation in verify_anchor to
fetch and hash the local certificate at sequence c.seq - 1, rather than the
newest certificate returned by get_most_recent_cert. Only perform the comparison
when that predecessor exists, while preserving the existing mismatch error
handling and payload hashing behavior.
- Around line 319-343: Update the signature decoding in the certificate
verification flow to use the URL-safe, no-padding base64 engine matching
node_keypair.sign_b64 output, while preserving the existing 64-byte validation
and error-result handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fee43d49-2e4b-4bbd-9921-8248f57fea48

📥 Commits

Reviewing files that changed from the base of the PR and between ad7c2b2 and 0801800.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/api/events.rs
  • crates/gitlawb-node/src/api/repos.rs
  • crates/gitlawb-node/src/arweave.rs
  • crates/gitlawb-node/src/auth/mod.rs
  • crates/gitlawb-node/src/cert.rs
  • crates/gitlawb-node/src/config.rs
  • crates/gitlawb-node/src/db/mod.rs
  • crates/gitlawb-node/src/server.rs
  • crates/gitlawb-node/src/test_support.rs

Comment thread crates/gitlawb-node/src/arweave.rs Outdated
Comment thread crates/gitlawb-node/src/arweave.rs Outdated

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found issues that need to be addressed before this is ready.

Findings

  • [P1] Use the configured gateway's data URL when verifying an anchor
    crates/gitlawb-node/src/arweave.rs:265
    arweave_gateway defaults to https://arweave.net, but this requests /v1/tx/{id}, which is the bundler API used for uploads rather than an Arweave gateway data URL. Consequently every normally uploaded anchor is reported invalid with the documented default configuration. Fetch the item through the gateway's data path (or add a separately named bundler-read configuration), and cover the default configuration rather than a mock of the bundler path.

  • [P1] Bind each anchor to the certificate for its own ref update
    crates/gitlawb-node/src/api/repos.rs:1306
    Certificates are issued once per update above this block, but every iteration subsequently reads the repository-wide latest certificate. In a multi-ref push, all permanent anchors therefore embed the last update's certificate; another completed push can also win the asynchronous race. Since verify_anchor never compares the certificate's repo/ref/old/new fields with the outer anchor, it returns valid for an anchor whose advertised transition was never signed. Preserve the returned certificate per update and reject a mismatch during verification.

  • [P1] Preserve certificate history and fail closed on a missing predecessor
    crates/gitlawb-node/src/db/mod.rs:2013
    The existing (repo_id, ref_name) upsert overwrites the predecessor whenever that ref is pushed again, while the new seq/prev design requires that predecessor to remain available. verify_anchor then silently skips the check when get_cert_by_seq returns None or errors, so an ordinary repeated push produces a truncated chain reported as valid. Make chain entries append-only and treat an unavailable declared predecessor as invalid (or explicitly unverifiable).

  • [P2] Allocate chain sequence numbers atomically
    crates/gitlawb-node/src/cert.rs:31
    Sequence allocation is a read-then-increment with no transaction, lock, or unique (repo_id, seq) constraint. Concurrent successful pushes can receive the same sequence and predecessor; get_cert_by_seq then selects an arbitrary row. This makes a signed chain nondeterministic under normal concurrent traffic. Allocate the sequence transactionally and enforce uniqueness, with retry on collision.

  • [P1] Do not describe raw signature bytes as a verifiable pusher authorization proof
    crates/gitlawb-node/src/auth/mod.rs:252
    Only the 64-byte Ed25519 signature is persisted. The RFC 9421 Signature-Input, covered component values, method/path, and content digest are discarded, and verify_anchor never verifies pusher_sig. A third party therefore cannot reconstruct the signing string or bind these bytes to this push, yet the endpoint can report the anchor valid. Persist a complete verifiable authorization artifact and validate it, or remove the proof/verification claim.

  • [P1] Bound the untrusted response read on the public verification route
    crates/gitlawb-node/src/arweave.rs:279
    The new unauthenticated, unthrottled route buffers the full gateway response with resp.bytes() before attempting JSON parsing. A caller can repeatedly select large data items and force corresponding memory and bandwidth consumption on the node. Apply a strict response-size limit (and a route-appropriate rate limit) before buffering or parsing the body.

  • [P3] Implement the promised anchor failure lifecycle instead of dropping failed uploads
    crates/gitlawb-node/src/api/repos.rs:1324
    Upload failures only log a warning; no anchor row is created, retried, confirmed, or marked failed. The new pending/confirmed/failed methods are unused, and success-only rows are always inserted as pending. This does not meet the linked issue's stated retry/visible-gap acceptance criterion, so transient bundler failures silently leave history unanchored. Persist pending work before upload and drive it through bounded retry and terminal status handling.

  • [P2] Keep the documented anchoring configuration working during the rename
    crates/gitlawb-node/src/config.rs:91
    This removes GITLAWB_IRYS_URL without a fallback, while both .env.example and README.md still instruct operators to set it. Upgrading an existing documented deployment leaves bundler_url empty and silently disables both anchoring paths. Support the legacy variable for a deprecation period or make the migration explicit and update all operator documentation in the same change.

  • [P3] Expose the new signed fields through the certificate API
    crates/gitlawb-node/src/api/certs.rs:45
    The certificate signing payload now includes seq, prev, and pusher_sig, but both list and get responses omit all three fields. Consumers of the established certificate API (including gl cert) therefore cannot reconstruct the signed payload or inspect chain continuity after this change. Serialize the new fields and update the client display/verification path accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crate:node gitlawb-node — the serving node and REST API kind:bug Defect fix — wrong or unsafe behavior subsystem:identity DID/UCAN, http-sig auth, push authorization subsystem:storage Blob/object store, Arweave, IPFS, archives

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden Arweave anchoring and add verification

4 participants