Skip to content

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

Open
Gravirei wants to merge 9 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 9 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 anchor verification via GET /api/v1/arweave/verify/:tx_id, returning validity, error details, and optional embedded certificate info.
    • Ref-update anchoring can now embed a signed, chain-linked certificate (including optional HTTP signature evidence).
  • Improvements

    • Permanent anchoring now uses a configurable bundler flow plus gateway-based resolution for verification.
    • Certificate issuance and listing now expose seq, prev, and related optional HTTP signature fields.
    • Arweave anchoring now records per-anchor lifecycle (pending → confirm/fail).
  • Configuration

    • If the bundler URL is unset, startup falls back to the legacy Irys URL with a deprecation warning.

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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change replaces Irys uploads with bundler requests, embeds chained ref certificates and pusher signature metadata, adds anchor lifecycle persistence, and exposes gateway-based Arweave transaction verification.

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/certs.rs
Verified pusher signatures are propagated into append-only certificates with sequence, predecessor hashes, RFC 9421 metadata, and expanded API responses.
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/main.rs, crates/gitlawb-node/src/db/mod.rs, crates/gitlawb-node/src/server.rs
Anchors use bundler /v1/tx requests, configurable bundler and gateway settings, embedded certificates, and pending/confirmed/failed persistence.
Anchor verification API
crates/gitlawb-node/src/arweave.rs, crates/gitlawb-node/src/api/arweave.rs, crates/gitlawb-node/src/server.rs
Gateway payloads are fetched and checked for certificate signatures and predecessor linkage at GET /api/v1/arweave/verify/:tx_id.
Validation and compatibility updates
crates/gitlawb-node/src/arweave.rs, crates/gitlawb-node/src/db/mod.rs, crates/gitlawb-node/src/api/events.rs, crates/gitlawb-node/src/test_support.rs
Tests and fixtures cover bundler routes, gateway failures, append-only certificates, anchor lifecycle transitions, and expanded certificate fields.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant git_receive_pack
  participant issue_ref_certificate
  participant Bundler
  participant verify_anchor_endpoint
  participant verify_anchor
  participant ArweaveGateway
  participant Db
  Client->>git_receive_pack: Authenticated push request
  git_receive_pack->>issue_ref_certificate: Pass pusher signature and proof
  issue_ref_certificate->>Db: Store chained certificate
  git_receive_pack->>Bundler: POST /v1/tx with certificate anchor
  Bundler-->>git_receive_pack: Return transaction ID
  Client->>verify_anchor_endpoint: GET /api/v1/arweave/verify/{tx_id}
  verify_anchor_endpoint->>verify_anchor: Verify transaction ID
  verify_anchor->>ArweaveGateway: GET gateway/{tx_id}
  ArweaveGateway-->>verify_anchor: Return anchored payload
  verify_anchor->>Db: Load predecessor certificate
  verify_anchor-->>Client: Return validity, errors, and certificate
Loading

Possibly related PRs

  • Gitlawb/node#72: Both PRs modify per-ref anchoring payloads and certificate issuance.
  • Gitlawb/node#149: Both PRs modify certificate listing endpoints and certificate response fields.

Suggested labels: sev:high, kind:security, subsystem:attestation, subsystem:api

Suggested reviewers: jatmn, kevincodex1

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements provider-neutral anchoring, embedded signed certificates, pusher signature persistence, seq/prev chaining, and gateway-based verification.
Out of Scope Changes check ✅ Passed The changes stay focused on Arweave anchoring, verification, auth, schema updates, and related tests, with no clear unrelated churn.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title is concise and accurately summarizes the main change: hardening Arweave anchoring and adding verification.
Description check ✅ Passed The description follows the template and covers summary, motivation, change type, implementation details, verification, and signing impact.
✨ 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.

@kevincodex1

Copy link
Copy Markdown
Contributor

@Gravirei please rebase to main and fix conflicts

@Gravirei
Gravirei force-pushed the fix/issue-26-harden-arweave-anchoring-verification branch from c94e8ed to ae4f5fc Compare July 22, 2026 16:28

@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: 4

🧹 Nitpick comments (1)
crates/gitlawb-node/src/test_support.rs (1)

4983-4988: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Make seed_cert produce chain-valid fixtures.

This helper creates the 10- and 55-certificate datasets, but every certificate has seq: 1 and a zero predecessor. The tests therefore cannot catch regressions that ignore monotonic ordering or prev links. Pass sequence/predecessor values through the helper or add a dedicated chained fixture.

🤖 Prompt for 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.

In `@crates/gitlawb-node/src/test_support.rs` around lines 4983 - 4988, Update the
seed_cert fixture helper so generated certificates form a valid chain: assign
increasing sequence values and set each certificate’s prev field to the
preceding certificate’s identifier or digest, with the first certificate using
the chain’s root predecessor. Ensure both the 10- and 55-certificate datasets
exercise monotonic ordering and linked predecessors.
🤖 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 292-293: Update the payload parsing in verify_anchor_endpoint so
serde_json::from_slice failure is handled as an invalid verification result
rather than propagated as an internal error. Return VerifyResult with valid set
to false and an appropriate error string, while preserving the existing JSON
parsing path.
- Around line 281-293: Update the response handling around resp.bytes() in the
verification flow to enforce the 1 MiB limit before unbounded buffering: reject
any Content-Length above 1_048_576, and stream or otherwise cap reads so
responses without a trustworthy length header cannot exceed the limit. Preserve
the existing invalid VerifyResult fields and JSON parsing for accepted payloads.

In `@crates/gitlawb-node/src/db/mod.rs`:
- Around line 923-938: Migration version 13 must backfill distinct
per-repository sequence values before enforcing uniqueness. In the migration’s
`stmts` array, add an update that assigns deterministic, non-colliding `seq`
values to existing `ref_certificates` rows grouped by `repo_id`, then create
`idx_ref_certs_repo_seq`; preserve the existing column additions and append-only
index changes.
- Around line 5157-5162: Ensure each certificate created through make_cert
receives a unique seq value before insertion, either by incrementing it within
make_cert or overriding it at every test call site. Update the affected
certificate setup so list_ref_certificates_respects_limit and
insert_ref_certificate_append_only use distinct sequence numbers and avoid the
(repo_id, seq) uniqueness conflict.

---

Nitpick comments:
In `@crates/gitlawb-node/src/test_support.rs`:
- Around line 4983-4988: Update the seed_cert fixture helper so generated
certificates form a valid chain: assign increasing sequence values and set each
certificate’s prev field to the preceding certificate’s identifier or digest,
with the first certificate using the chain’s root predecessor. Ensure both the
10- and 55-certificate datasets exercise monotonic ordering and linked
predecessors.
🪄 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: 4e15d3f3-7966-4b86-a8fa-640f7c92e10a

📥 Commits

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

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/api/certs.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/main.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 (5)
  • crates/gitlawb-node/src/api/events.rs
  • crates/gitlawb-node/src/server.rs
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/config.rs
  • crates/gitlawb-node/src/cert.rs

Comment thread crates/gitlawb-node/src/arweave.rs Outdated
Comment thread crates/gitlawb-node/src/arweave.rs Outdated
Comment thread crates/gitlawb-node/src/db/mod.rs
Comment thread crates/gitlawb-node/src/db/mod.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] Backfill legacy certificate sequence numbers before adding the unique index
    crates/gitlawb-node/src/db/mod.rs:910
    Migration 12 gives every existing certificate seq = 1, but migration 13 then creates a unique (repo_id, seq) index. Version 10 only deduplicated (repo_id, ref_name), so any existing repository with certificates for two refs has duplicate (repo_id, 1) rows and cannot complete this migration or start. This is also immediately exposed by the changed seed_cert fixture, which inserts ten seq = 1 rows and makes list_certs_respects_limit_param fail. Assign deterministic per-repository sequence/chain values before creating the index and update the fixture.

  • [P1] Enforce the verification response limit before buffering the gateway body
    crates/gitlawb-node/src/arweave.rs:282
    The new unauthenticated verification route calls resp.bytes() and only checks the 1 MiB limit after the whole Arweave object has been downloaded and allocated. A caller can select a huge or chunked gateway item and exhaust node memory/bandwidth before the rejection occurs. Stream into a capped reader (and reject a known excessive content length up front) instead of buffering first.

  • [P1] Restore cryptographic verification to gl cert show
    crates/gl/src/cert.rs:151
    This branch has drifted from its base and removes the base's --verify, --expect-node, Ed25519 verification routine, and tests, although the command is still documented as verifying a certificate. It now only prints a proposed payload and returns success for a modified or self-signed certificate. Please rebase without reverting that fail-closed CLI contract, then update its canonical payload for seq, prev, and pusher_sig.

  • [P2] Hold the certificate-chain lock through allocation and insertion
    crates/gitlawb-node/src/db/mod.rs:2160
    pg_advisory_xact_lock is transaction-scoped, but this standalone pooled query commits before issue_ref_certificate reads the previous certificate or inserts the new one. Concurrent pushes can therefore select the same sequence; with three or more contenders the single retry can collide again, leaving an accepted push without its certificate/anchor evidence. Use one acquired connection/transaction for the lock, predecessor read, and insert (or a robust serialization/retry strategy).

  • [P2] Bind the embedded certificate to the enclosing Arweave anchor
    crates/gitlawb-node/src/arweave.rs:303
    Verification checks the copied certificate signature but never compares the untrusted outer repo, owner_did, ref, SHAs, or node DID to that certificate. An attacker can publish a payload with a valid public certificate while claiming a different ref update and receive valid: true. Reject field mismatches (and match a locally recorded transaction too if this endpoint is meant to validate local anchors).

  • [P2] Keep gl status compatible with the remote created by gl init
    crates/gl/src/status.rs:146
    This branch regresses the base's multi-remote lookup: the status command now accepts only a gitlawb:// fetch URL on origin, while gl init adds the same URL under the gitlawb remote. Immediately after the supported init flow, gl status reports that the repository is not a Gitlawb repo and skips the PR/issue queries. Rebase without reverting the base's lookup for the gitlawb remote and other Gitlawb fetch/push URLs.

  • [P2] Do not hard-code main after a plain git init
    crates/gl/src/init.rs:41
    This branch reverts the base's branch/commit-state handling. Plain git init honors the user's init.defaultBranch, but the command unconditionally instructs git push gitlawb main. On master, feature, detached, or unborn HEADs that instruction either targets a nonexistent/wrong ref or fails before the first commit. Rebase without dropping the previous branch/commit-state handling, or initialize main with the compatibility fallback.

  • [P2] Preserve the legacy command-line spelling during the bundler rename
    crates/gitlawb-node/src/config.rs:75
    The environment fallback runs only after Clap parses arguments. Existing operators invoking gitlawb-node --irys-url … now receive an unknown-argument startup error even though the PR claims compatibility for GITLAWB_IRYS_URL. Add a deprecated long alias or normalize a retained legacy option as well as the environment variable.

  • [P2] Do not remove unrelated CI safety gates from this anchoring change
    .github/workflows/pr-checks.yml:189
    This is stale-base drift rather than part of the Arweave feature: the branch deletes the shipped Windows CLI test lane and the only gitlawb-core dependency-purity gate, together with its allowlist and checker script. That removes platform regression visibility and a supply-chain control for the shared cryptographic core; rebase without deleting these protections, or justify and replace them in a separately scoped change.

Gravirei added 5 commits July 23, 2026 10:39
Gitlawb#26)

- Use configured gateway's data URL (/tx_id) instead of bundler API for verify
- Bound untrusted response to 1 MiB on public verify route
- Bind each anchor to its own ref update certificate (not repo-wide latest)
- Make certificate storage append-only with unique (repo_id, seq) constraint
- Add migration v13 to drop old unique index and add sequence uniqueness
- Allocate chain sequence numbers atomically using per-repo advisory lock
- Fail closed on missing predecessor cert during verification
- Persist full RFC 9421 HTTP Signature context (signature-input, content-digest, path)
- Verify pusher authorization proof in verify_anchor
- Add legacy GITLAWB_IRYS_URL fallback for GITLAWB_BUNDLER_URL
- Expose seq, prev, pusher_sig through certificate API list/get responses
@Gravirei
Gravirei force-pushed the fix/issue-26-harden-arweave-anchoring-verification branch from ae4f5fc to e9a1a5e Compare July 23, 2026 04:39
Gravirei added 2 commits July 23, 2026 11:19
…ias, cross-check cert, transaction lock

- v13 migration: backfill seq values per repo before creating
  unique (repo_id, seq) index to prevent failures on existing data
- verify_anchor: check Content-Length before buffering response body
- config: add deprecated --irys-url alias for bundler_url
- verify_anchor: cross-check outer anchor fields (repo, ref_name,
  old_sha, new_sha, node_did) against embedded certificate
- cert issuance: wrap lock/lookup/insert in a single Postgres
  transaction so pg_advisory_xact_lock is held for the full sequence
- Add _tx variants of db methods accepting &mut PgConnection
- Fix test helpers to use atomic counters for unique seq values
- Fix v10 dedup tests to drop v13 index during pre-migration setup
@Gravirei
Gravirei requested a review from jatmn July 23, 2026 05:29
@kevincodex1

Copy link
Copy Markdown
Contributor

@coderabbitai please make an intensive review of this changes

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@kevincodex1 I’ll perform an intensive review of the complete change set, with particular attention to certificate-chain integrity, RFC 9421 signature verification, migration safety, anchoring failure handling, and API/security behavior.

✅ Action performed

Full review finished.

@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: 1

🧹 Nitpick comments (1)
crates/gitlawb-node/src/api/events.rs (1)

429-435: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Use valid per-repository certificate-chain fixtures.

  • crates/gitlawb-node/src/api/events.rs#L429-L435: replace the process-global sequence generator with per-chain fixture state.
  • crates/gitlawb-node/src/api/events.rs#L447-L452: derive prev from the preceding certificate instead of always using the genesis hash.
  • crates/gitlawb-node/src/test_support.rs#L1481-L1486: avoid assigning a global sequence to an otherwise standalone certificate.
  • crates/gitlawb-node/src/test_support.rs#L4967-L4973: make sequence generation scoped to a repository/chain.
  • crates/gitlawb-node/src/test_support.rs#L4990-L4995: generate matching predecessor hashes for multi-certificate fixtures.
🤖 Prompt for 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.

In `@crates/gitlawb-node/src/api/events.rs` around lines 429 - 435, Replace the
process-global NEXT_FCERT_SEQ/ref_cert_seq state in
crates/gitlawb-node/src/api/events.rs:429-435 with sequence state scoped to each
certificate chain; update the certificate construction at
crates/gitlawb-node/src/api/events.rs:447-452 to derive prev from the preceding
certificate. In crates/gitlawb-node/src/test_support.rs:1481-1486, leave
standalone certificates without a global sequence; in lines 4967-4973, scope
sequence generation to the repository or chain; and in lines 4990-4995, generate
predecessor hashes that match the preceding certificates in multi-certificate
fixtures.
🤖 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/api/certs.rs`:
- Around line 55-57: The certificate JSON responses in
crates/gitlawb-node/src/api/certs.rs must include complete pusher-signature
metadata. Update both the listed-certificates response at lines 55-57 and the
single-certificate response at lines 98-100 to include signature_input,
content_digest, and request_path alongside the existing pusher_sig fields.

---

Nitpick comments:
In `@crates/gitlawb-node/src/api/events.rs`:
- Around line 429-435: Replace the process-global NEXT_FCERT_SEQ/ref_cert_seq
state in crates/gitlawb-node/src/api/events.rs:429-435 with sequence state
scoped to each certificate chain; update the certificate construction at
crates/gitlawb-node/src/api/events.rs:447-452 to derive prev from the preceding
certificate. In crates/gitlawb-node/src/test_support.rs:1481-1486, leave
standalone certificates without a global sequence; in lines 4967-4973, scope
sequence generation to the repository or chain; and in lines 4990-4995, generate
predecessor hashes that match the preceding certificates in multi-certificate
fixtures.
🪄 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 Plus

Run ID: 5645b021-f503-4733-92c1-424a3ae2349e

📥 Commits

Reviewing files that changed from the base of the PR and between e9da517 and 8103450.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/api/certs.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/main.rs
  • crates/gitlawb-node/src/server.rs
  • crates/gitlawb-node/src/test_support.rs

Comment thread crates/gitlawb-node/src/api/certs.rs Outdated
@kevincodex1

Copy link
Copy Markdown
Contributor

@coderabbitai make an intensive review on this please

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@kevincodex1 I’ll perform an intensive end-to-end review of #224, focusing on certificate-chain correctness, RFC 9421 proof validation, database migrations, anchoring lifecycle/error handling, and API security.

✅ Action performed

Full review finished.

@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.

♻️ Duplicate comments (1)
crates/gitlawb-node/src/arweave.rs (1)

303-304: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Non-JSON gateway payload still returns 500 instead of valid: false. serde_json::from_slice(&body_bytes)? propagates through verify_anchor_endpoint (mapped to AppError::Internal), inconsistent with the other graceful branches and the "could be JSON or raw bytes" comment. Convert a parse failure into VerifyResult { valid: false, .. } with an error string.

🤖 Prompt for 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.

In `@crates/gitlawb-node/src/arweave.rs` around lines 303 - 304, Update the anchor
parsing in verify_anchor_endpoint so serde_json::from_slice failures are handled
as an invalid verification result rather than propagated as AppError::Internal.
Return VerifyResult with valid set to false and an error string for non-JSON
payloads, while preserving the existing successful JSON path and other graceful
branches.
🧹 Nitpick comments (2)
crates/gitlawb-node/src/server.rs (1)

222-228: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider throttling the unauthenticated verify route. Each GET /api/v1/arweave/verify/{tx_id} triggers an outbound gateway fetch plus a DB lookup with no auth or per-IP brake, so it's an amplification/DoS surface (node → gateway) reachable by anonymous callers. Given the other cost-bearing routes here carry a per-IP IpRateLimiter, consider wrapping arweave_routes similarly. (tx_id is a fixed-host path segment, so this is a load concern, not SSRF.)

🤖 Prompt for 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.

In `@crates/gitlawb-node/src/server.rs` around lines 222 - 228, Wrap the
arweave_routes router, including GET /api/v1/arweave/verify/{tx_id}, with the
existing per-IP IpRateLimiter used by other cost-bearing routes. Preserve the
current list_anchors and verify_anchor_endpoint handlers while ensuring
anonymous requests are throttled before triggering gateway or database work.
crates/gitlawb-node/src/db/mod.rs (1)

2834-2870: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

cert_id is never persisted on anchor rows. RecordAnchorInputV2 has no cert_id field and record_arweave_anchor's INSERT omits it, so the cert_id column added in migration v12 stays NULL for every anchor even though list_arweave_anchors/list_pending_anchors project it. The push path in api/repos.rs already has the issued certificate in scope (ref_certs_clone), so the anchor→certificate DB linkage this column was added for is currently unreachable. Consider threading the cert id through so audits can join anchors to their certs. (gateway_url on the input is likewise accepted but ignored by this function.)

🤖 Prompt for 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.

In `@crates/gitlawb-node/src/db/mod.rs` around lines 2834 - 2870, The anchor
record flow does not persist the issued certificate ID. Add a cert_id field to
RecordAnchorInputV2, pass the corresponding ID from the push path using
ref_certs_clone, and include it in record_arweave_anchor’s INSERT and bindings
so cert_id is stored on each anchor row; also remove or persist gateway_url
consistently instead of silently ignoring it.
🤖 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.

Duplicate comments:
In `@crates/gitlawb-node/src/arweave.rs`:
- Around line 303-304: Update the anchor parsing in verify_anchor_endpoint so
serde_json::from_slice failures are handled as an invalid verification result
rather than propagated as AppError::Internal. Return VerifyResult with valid set
to false and an error string for non-JSON payloads, while preserving the
existing successful JSON path and other graceful branches.

---

Nitpick comments:
In `@crates/gitlawb-node/src/db/mod.rs`:
- Around line 2834-2870: The anchor record flow does not persist the issued
certificate ID. Add a cert_id field to RecordAnchorInputV2, pass the
corresponding ID from the push path using ref_certs_clone, and include it in
record_arweave_anchor’s INSERT and bindings so cert_id is stored on each anchor
row; also remove or persist gateway_url consistently instead of silently
ignoring it.

In `@crates/gitlawb-node/src/server.rs`:
- Around line 222-228: Wrap the arweave_routes router, including GET
/api/v1/arweave/verify/{tx_id}, with the existing per-IP IpRateLimiter used by
other cost-bearing routes. Preserve the current list_anchors and
verify_anchor_endpoint handlers while ensuring anonymous requests are throttled
before triggering gateway or database work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d81eba6b-bb9a-4ee3-af1e-5e62923f6b5f

📥 Commits

Reviewing files that changed from the base of the PR and between e9da517 and a01160f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • crates/gitlawb-node/src/api/arweave.rs
  • crates/gitlawb-node/src/api/certs.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/main.rs
  • crates/gitlawb-node/src/server.rs
  • crates/gitlawb-node/src/test_support.rs

…ate limit

- serde_json parse failure in arweave verify returns VerifyResult instead of
  propagating as AppError::Internal
- RecordAnchorInputV2 replaces unused gateway_url with cert_id, persisted in
  arweave_anchors INSERT, sourced from push-time certificate
- arweave routes wrapped with per-IP IpRateLimiter
@Gravirei Gravirei closed this Jul 23, 2026
@Gravirei Gravirei reopened this Jul 23, 2026
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

5 participants