Skip to content

fix: multigateway - #89

Merged
TheGreatAlgo merged 5 commits into
mainfrom
multi-gateway-support
Jul 27, 2026
Merged

fix: multigateway#89
TheGreatAlgo merged 5 commits into
mainfrom
multi-gateway-support

Conversation

@TheGreatAlgo

@TheGreatAlgo TheGreatAlgo commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Added support for configuring multiple HTTP gateways with sequential failover and per-gateway availability handling.
    • Introduced optional CID content verification for downloaded data.
    • Improved range-request robustness, including better handling of empty/unsatisfiable ranges and malformed partial responses.
  • Bug Fixes
    • Reads now recover more reliably from gateway errors and content mismatches by failing over to alternate gateways.
    • Forwarded credentials are better scoped across retries and redirects to prevent leakage to fallback gateways.
  • Documentation/Exports
    • Public API now re-exports additional gateway validation/mismatch symbols.
  • Tests
    • Expanded multi-gateway failover, concurrency, redirect/credential, and content-verification coverage.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@TheGreatAlgo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 32 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b222cd4e-0a98-4a36-9984-1c5d7fe2c17a

📥 Commits

Reviewing files that changed from the base of the PR and between 2643c9d and 8320022.

📒 Files selected for processing (2)
  • py_hamt/store_httpx.py
  • tests/test_k15_multi_gateway_failover.py

Walkthrough

KuboCAS now supports normalized multi-gateway reads with sequential failover, circuit-breaker health tracking, per-gateway concurrency limits, credential-safe redirects, optional CID verification, improved range handling, and expanded tests. Verification exceptions are publicly re-exported.

Changes

Multi-gateway reads

Layer / File(s) Summary
Gateway configuration and validation
py_hamt/store_httpx.py, py_hamt/__init__.py, tests/test_k15_multi_gateway_failover.py
Adds gateway normalization, deduplication, health state, CID verification options, and public verification exception exports.
Gateway capacity and lifecycle
py_hamt/store_httpx.py, tests/test_k15_multi_gateway_failover.py
Adds per-gateway semaphores, capacity-aware ordering, circuit-breaker probing, and cleanup during close and destructor paths.
Sequential gateway loading and failover
py_hamt/store_httpx.py, tests/test_k15_multi_gateway_failover.py
Adds per-gateway retries, credential-scoped redirects, range validation, optional content verification, health updates, failover, and aggregated errors.
Gateway behavior test coverage
tests/test_k15_multi_gateway_failover.py
Adds threaded fake gateways and tests for configuration, failover, circuit breaking, concurrency, credential safety, redirects, and digest verification.

Estimated code review effort: 5 (Critical) | ~100 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant KuboCAS
  participant GatewayA
  participant GatewayB
  Caller->>KuboCAS: load(CID)
  KuboCAS->>GatewayA: Request content with scoped credentials
  GatewayA-->>KuboCAS: Error, invalid range, or mismatched content
  KuboCAS->>GatewayB: Sequential failover request
  GatewayB-->>KuboCAS: Verified content
  KuboCAS-->>Caller: Content or aggregated failures
Loading

Possibly related PRs

Suggested reviewers: 0xswego

Poem

A rabbit hops from gate to gate,
Checks each byte before it’s late.
If one path fails, another tries,
While secrets hide from foreign eyes.
Verified content wins the prize.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the PR, but it is too vague to clearly describe the main change. Rename it to something specific like "fix: add multi-gateway IPFS failover and content verification".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch multi-gateway-support

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.

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

🧹 Nitpick comments (3)
tests/test_k15_multi_gateway_failover.py (2)

487-493: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add an identity-multihash case to cover the last branch of _cid_is_verifiable.

store_httpx.py line 156 returns False for identity multihashes; nothing here exercises it.

💚 Suggested addition
     assert not store_module._cid_is_verifiable(GOOD_CID, 0, None)
     assert not store_module._cid_is_verifiable(GOOD_CID, None, 4)
+    identity_cid = CID("base32", 1, "raw", multihash.digest(BODY, "identity"))
+    assert not store_module._cid_is_verifiable(identity_cid, None, None)
🤖 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 `@tests/test_k15_multi_gateway_failover.py` around lines 487 - 493, Add an
assertion in test_dag_pb_cids_are_not_verifiable covering a CID whose multihash
uses the identity codec, and verify that store_module._cid_is_verifiable returns
False for it. Keep the existing dag-pb and argument-validation assertions
unchanged.

381-381: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move import time to the module imports.

🤖 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 `@tests/test_k15_multi_gateway_failover.py` at line 381, Move the time import
from its current local position to the module-level import section in
tests/test_k15_multi_gateway_failover.py, keeping the existing time usage
unchanged.
py_hamt/store_httpx.py (1)

1009-1014: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Docstring and implementation disagree on response_bytes.

The docstring says stats "accumulates the byte count ... across every gateway attempted", but line 1034 overwrites rather than accumulates (retries do accumulate). If overwrite is intended — reporting only the bytes actually returned to the caller — reword the docstring; otherwise use +=. As written a failing leg's byte count also lingers when a later leg raises before reading a body.

Also applies to: 1034-1034

🤖 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 `@py_hamt/store_httpx.py` around lines 1009 - 1014, Update the gateway-fetch
method’s response_bytes handling and docstring consistently: accumulate bytes
across all gateway attempts with +=, and ensure a failed leg cannot leave stale
byte counts when a later attempt raises before reading its body. Keep retry
accumulation and load’s whole-operation stats behavior aligned with the
resulting implementation.
🤖 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 `@py_hamt/store_httpx.py`:
- Around line 616-620: The shared _gateway_health state uses event-loop-relative
timestamps, causing incorrect cooldown comparisons across loops. Update
record_failure and is_healthy to use process-wide time.monotonic() instead of
asyncio.get_running_loop().time(), and remove any unnecessary async-context
requirement while preserving the existing health and cooldown behavior.
- Around line 1059-1064: Update the load flow surrounding _verify_cid_content so
verification exceptions set stats.status to the appropriate failure status
before propagating through gateway failover. Ensure the final end_cas_load call
reports failure when all gateways return mismatched content, while preserving
the existing success status for verified reads.

In `@tests/test_k15_multi_gateway_failover.py`:
- Around line 300-302: Update the test around the load assertion so the
documented further failure is actually exercised: append another (500, b"")
response to responses before the GOOD_CID load, while preserving the existing
assertions for the returned body and gateway_b hit count.

---

Nitpick comments:
In `@py_hamt/store_httpx.py`:
- Around line 1009-1014: Update the gateway-fetch method’s response_bytes
handling and docstring consistently: accumulate bytes across all gateway
attempts with +=, and ensure a failed leg cannot leave stale byte counts when a
later attempt raises before reading its body. Keep retry accumulation and load’s
whole-operation stats behavior aligned with the resulting implementation.

In `@tests/test_k15_multi_gateway_failover.py`:
- Around line 487-493: Add an assertion in test_dag_pb_cids_are_not_verifiable
covering a CID whose multihash uses the identity codec, and verify that
store_module._cid_is_verifiable returns False for it. Keep the existing dag-pb
and argument-validation assertions unchanged.
- Line 381: Move the time import from its current local position to the
module-level import section in tests/test_k15_multi_gateway_failover.py, keeping
the existing time usage unchanged.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b2864381-668e-412e-b751-9bd2a75dc8e2

📥 Commits

Reviewing files that changed from the base of the PR and between 5a25d35 and 3449ad2.

📒 Files selected for processing (3)
  • py_hamt/__init__.py
  • py_hamt/store_httpx.py
  • tests/test_k15_multi_gateway_failover.py

Comment thread py_hamt/store_httpx.py
Comment thread py_hamt/store_httpx.py
Comment thread tests/test_k15_multi_gateway_failover.py Outdated

@da-code-reviewer da-code-reviewer Bot 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.

Codex Automated Review

Found 3 actionable issues.
Posted 3 inline comment(s).

Comment thread py_hamt/store_httpx.py Outdated
while retry_count <= self.max_retries:
try:
async with semaphore: # Throttle each gateway attempt
response = await client.get(url, headers=headers or None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH
The same configured client—and therefore its default Authorization, auth, and custom headers—is used for every gateway origin. A private primary with a public fallback will leak credentials to that fallback. Bind credentials per gateway or strip sensitive headers when the request origin changes.

Comment thread py_hamt/store_httpx.py Outdated
if cid.codec.code == KuboCAS.DAG_PB_MARKER:
return False
# An identity multihash inlines its content in the CID; nothing was fetched.
return cid.hashfun.name != "identity"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Identity CIDs are excluded from verification even though load() still fetches and returns gateway bytes. A malicious gateway can therefore substitute content despite verify_content=True. Compare the response with cid.raw_digest (or return the embedded bytes) instead of skipping verification.

Comment thread py_hamt/store_httpx.py Outdated
mismatch; we cannot prove the content wrong, so the read is allowed through.
"""
try:
computed = multihash.digest(data, cid.hashfun.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
This computes the hash at its default length, but valid multihashes may be truncated, and variable-output hashes require an explicit size. Correct truncated content is rejected, while the broad exception handler silently skips verification for variable-output hashes. Compute with size=len(cid.raw_digest).

@codecov-commenter

codecov-commenter commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (5a25d35) to head (8320022).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main       #89    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            8         8            
  Lines         3021      3166   +145     
==========================================
+ Hits          3021      3166   +145     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@da-code-reviewer da-code-reviewer Bot 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.

Codex Automated Review

Found one security issue.
Posted 1 inline comment(s).

Comment thread py_hamt/store_httpx.py Outdated
if strip_credentials:
safe_headers = {
name: value
for name, value in client.headers.items()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH
Do not forward arbitrary client headers cross-origin. This copies every header not in the three-name denylist to the fallback. Since KuboCAS supports authentication through arbitrary headers, credentials such as X-API-Key or X-Auth-Token will still be disclosed to a public fallback after the primary fails. Use per-origin header configuration or an explicit allowlist of headers safe to forward.

@da-code-reviewer da-code-reviewer Bot 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.

Codex Automated Review

Found three actionable security and reliability issues in the multi-gateway implementation.
Posted 3 inline comment(s).

Comment thread py_hamt/store_httpx.py Outdated
Comment thread py_hamt/store_httpx.py Outdated
Comment thread py_hamt/store_httpx.py

@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

🤖 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 `@tests/test_k15_multi_gateway_failover.py`:
- Around line 703-706: Update the failover assertions in the test around
fallback host handling to compare fallback["host"] directly with gateway_a’s
parsed host value, rather than using substring membership against gateway_a.url.
Preserve the existing assertion that fallback includes ordinary content
negotiation.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 88111141-8448-444c-96c1-31f0ebd9521a

📥 Commits

Reviewing files that changed from the base of the PR and between 3449ad2 and 2643c9d.

📒 Files selected for processing (3)
  • py_hamt/__init__.py
  • py_hamt/store_httpx.py
  • tests/test_k15_multi_gateway_failover.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • py_hamt/init.py
  • py_hamt/store_httpx.py

Comment thread tests/test_k15_multi_gateway_failover.py

@da-code-reviewer da-code-reviewer Bot 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.

Codex Automated Review

Found four actionable issues in credential scoping and gateway recovery.
Posted 4 inline comment(s).

Comment thread py_hamt/store_httpx.py
Comment thread py_hamt/store_httpx.py
Comment thread py_hamt/store_httpx.py Outdated
Comment thread py_hamt/store_httpx.py Outdated
@TheGreatAlgo
TheGreatAlgo merged commit c7d3cd9 into main Jul 27, 2026
3 checks passed

@da-code-reviewer da-code-reviewer Bot 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.

Codex Automated Review

Found one credential-scoping security regression.
Posted 1 inline comment(s).

Comment thread py_hamt/store_httpx.py
origin of its choosing and harvest them. Following each hop ourselves
re-applies the full origin check to the *redirect target*.
"""
if not _carries_credentials(client):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH
_carries_credentials() ignores client cookies and default query parameters. A client using a domainless session cookie or query-token therefore takes this client.get() path, and HTTPX merges those credentials into requests to foreign fallback gateways. Conversely, the manual path constructs a raw Request and drops them even for the primary. Build requests with the client configuration, then explicitly remove cookie/query credentials for non-credentialed origins.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants