Skip to content

feat: Add Kademlia DHT Interoperability Test Suite Architecture#111

Merged
acul71 merged 15 commits into
libp2p:masterfrom
K-21:feat/kad-dht
Jul 1, 2026
Merged

feat: Add Kademlia DHT Interoperability Test Suite Architecture#111
acul71 merged 15 commits into
libp2p:masterfrom
K-21:feat/kad-dht

Conversation

@K-21

@K-21 K-21 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Overview

This draft PR introduces a comprehensive interoperability testing architecture for the Kademlia DHT implementation (located in a new kad-dht/ directory). The goal is to automatically validate node discovery and provider record retrieval across different language implementations of libp2p.

The architecture strictly mirrors the existing design patterns used in the transport/ tests, utilizing dynamic Docker Compose matrix generation and relying on a centralized Redis service for container coordination and discovery state hand-offs.

Architecture Details

  • kad-dht/run.sh: Serves as the primary entry point. It initializes the test environment, spins up the global Redis network, builds necessary Docker images, and drives the test matrix execution.
  • kad-dht/images.yaml & generate-tests.sh: Defines available language implementations and dynamically calculates all valid test permutations (test-matrix.yaml). Each test evaluates three distinct roles: bootstrap, provider, and querier.
  • run-single-test.sh: Dynamically generates docker-compose.yaml configurations to isolate each test execution. It handles injecting necessary environment variables (like TEST_KEY for Redis namespacing) and extracts the final metrics to construct results.yaml.
  • Implementations Scaffolding: Provides initial node.py (for py-libp2p) and Program.cs (for NethermindEth/dotnet-libp2p) applications packaged in multi-stage Dockerfiles.

Current Execution Status

The underlying container orchestration logic, networking, and Redis state sharing are fully operational. The base validation is successful:

  • .NET interoperability (dotnet_x_dotnet_x_dotnet) successfully passes.

Open Implementation Issue (Python):
The test combinations relying on the Python nodes are currently failing. The py-libp2p API structure on PyPI diverges significantly from the standardized libp2p initialization patterns, failing during new_node instantiation.

Purpose of this Draft

The purpose of opening this PR in draft status is to propose the orchestration framework, the Docker isolation patterns, and the generic testing logic. Once the general test matrix structure and test topologies are approved, further commits will address the specific library API variations (beginning with the Python dependencies).

@K-21

K-21 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@seetadev @acul71 @sumanjeet0012
The orchestration layer is complete. We are successfully generating the dynamic test matrix, passing the TEST_KEY for Redis namespaces, and isolating the Docker Compose networks for the bootstrap, provider, and querier roles. The .NET implementation validates this workflow. There is a known issue with the Python container crashing during initialization due to differences in the py-libp2p API, which will be addressed in a follow-up commit

@sumanjeet0012

Copy link
Copy Markdown
Collaborator

@K-21 Thank you for raising this PR.

I noticed a few issues that need to be addressed. Let me break them down for you:

Critical Issues

1 — Incorrect Asynchronous Runtime (asyncio vs trio)

  • File: kad-dht/images/py/node.py
  • Line(s): 203, 308
  • Issue: The script uses asyncio.run(main()) and import asyncio. However, modern py-libp2p (version 0.6.0+, which is pulled by pip install libp2p) is built entirely on the Trio asynchronous framework, NOT asyncio. Attempting to run libp2p networking methods inside an asyncio event loop causes the script to crash immediately with a RuntimeError: must be called from async context deep within Trio's internals.
  • Suggestion: Rewrite the script to use Trio: import trio instead of asyncio, use trio.run(main) instead of asyncio.run(), and use trio.sleep() in place of asyncio.sleep().

2 — Invalid py-libp2p API Usage (new_node vs new_host)

  • File: kad-dht/images/py/node.py
  • Line(s): 205, 224
  • Issue: The author noted the API differs from standardized libp2p and fails during new_node instantiation. Specifically:
    1. The entrypoint factory in py-libp2p is new_host, not new_node.
    2. The factory expects the keyword argument listen_addrs, not listen_multiaddrs.
    3. new_host is a synchronous factory function (cannot be awaited).
    4. Once created, the host must be started using its async context manager: async with host.run(listen_addrs=[...]):.
  • Suggestion: Import new_host instead of new_node. Initialize synchronously: host = new_host(key_pair=key_pair). Then start it using the Trio context manager: async with host.run(listen_addrs=[Multiaddr("/ip4/0.0.0.0/tcp/0")]):.

3 — Blocking IO inside Async Function (Synchronous Redis/Socket)

  • File: kad-dht/images/py/node.py
  • Line(s): 220, 238-241, 250, 260
  • Issue: The script uses synchronous redis.Redis and socket.connect() inside the main coroutine. In Trio, this blocks the main thread event loop, preventing cooperative multitasking.
  • Suggestion: Wrap synchronous network calls in trio.to_thread.run_sync(r.set, key, val) or use an async redis client.

Major Issues

1 — Missing Type Hints

  • File: kad-dht/images/py/node.py
  • Line(s): 210
  • Issue: The public function main() lacks Python type hints, violating the Python Review Rules requirement for all public functions to have return type annotations.
  • Suggestion: Update the signature to async def main() -> None:.

2 — Using print() instead of logging

  • File: kad-dht/images/py/node.py
  • Line(s): 216, 245, 251, 269, 293, 304
  • Issue: The script uses print() for output instead of the standard logging module, violating the Python Review Rules.
  • Suggestion: Import and configure the standard logging module and replace print() calls with logging.info() or logging.error().

Minor Issues

1 — Mid-file imports

  • File: kad-dht/images/py/node.py
  • Line(s): 237
  • Issue: import socket is placed inside the main() function instead of at the top of the file, which violates standard import order conventions (isort).
  • Suggestion: Move import socket to the top of the file alongside the other standard library imports.

2 — Missing Timeout on Network Operations

  • File: kad-dht/images/py/node.py
  • Line(s): 266, 291
  • Issue: await node.connect(info.peer_id, maddr) is called without a timeout. If the bootstrap node is unresponsive, this call will hang indefinitely.
  • Suggestion: Wrap the connection call in asyncio.wait_for(..., timeout=30.0).

@K-21

K-21 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

@sumanjeet0012
Thank you so much for the detailed feedback

I've just pushed a new commit to the PR that addresses all of your points:

  • Completely migrated the script to use trio instead of asyncio.
  • Updated the initialization to correctly use new_host and async with host.run(...).
  • Wrapped all the synchronous Redis and socket calls in trio.to_thread.run_sync() to avoid blocking the event loop.
  • Fixed the typing, replaced print() with the logging module, and added the network timeouts.
  • The Python node now properly leverages the KadDHT class following the example.

Really appreciate you taking the time to review it so thoroughly

@sumanjeet0012

Copy link
Copy Markdown
Collaborator

@K-21 There are two issues left:

1. Invalid attribute listen_addresses on Swarm object

• Issue: The author tried to retrieve the allocated port using host.get_network().listen_addresses . In py-libp2p , the network object returned by get_network() is a Swarm instance, which does
not have a listen_addresses attribute. Calling this will raise an AttributeError .
• Fix: They should use the top-level host method host.get_addrs() instead.

2. Passing too many arguments to host.connect()

• Issue: The author calls await host.connect(info.peer_id, maddr) . However, the connect() method in BasicHost only takes a single argument: a PeerInfo object. Since they already correctly
instantiated info = info_from_p2p_addr(maddr) , passing the individual components will raise a TypeError: connect() takes 2 positional arguments but 3 were given .
• Fix: They need to pass the info object directly: await host.connect(info) .

@sumanjeet0012 sumanjeet0012 marked this pull request as ready for review June 28, 2026 04:29
@sumanjeet0012 sumanjeet0012 requested a review from dhuseby as a code owner June 28, 2026 04:29
@sumanjeet0012 sumanjeet0012 marked this pull request as draft June 28, 2026 04:36
acul71
acul71 previously requested changes Jun 28, 2026

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

Thanks @K-21 for putting this together, and @sumanjeet0012 for the detailed Python feedback. I ran the full suite locally (./kad-dht/run.sh) and read through the results in kad-dht/results/185030-28-06-2026/. The orchestration scaffolding is a solid start and mirrors transport/ reasonably well, but the suite does not yet demonstrate real cross-implementation DHT interoperability. Several tests pass for the wrong reasons.

What works well

  • Orchestration layoutrun.sh, generate-tests.sh, run-single-test.sh, Redis namespacing via TEST_KEY, and per-test Docker Compose isolation follow the same mental model as transport/. This is the right shape for the repo.
  • Python node — After the trio/new_host fixes, py-libp2p is doing real work: host startup, bootstrap registration, DHT provide/find, and put_value/get_value. The direction is correct.
  • Results layout — Timestamped results/<run>/ with logs/, docker-compose/, and results.yaml is useful for debugging (though run.sh should print the output path when finished).

Local test results (8-matrix, py + dotnet)

Test Result Notes
py_x_py_x_py Pass Real DHT interop
py_x_py_x_dotnet Pass Python does DHT; .NET querier does not
py_x_dotnet_x_py Pass* Querier logs status: fail — false positive (see below)
py_x_dotnet_x_dotnet Pass Redis-only; no real P2P
dotnet_x_dotnet_x_dotnet Pass Redis-only; no real P2P
dotnet_x_py_x_py Fail Python provider cannot TCP-connect to bootstrap
dotnet_x_py_x_dotnet Fail Same
dotnet_x_dotnet_x_py Fail Python querier cannot TCP-connect to bootstrap

Score: 5/8 pass, but only py_x_py_x_py is a genuine interoperability success.

Critical: .NET implementation is a stub, not libp2p

kad-dht/images/dotnet/Program.cs does not initialize dotnet-libp2p. It picks a random peer ID and port, publishes a fabricated multiaddr to Redis, and coordinates via Redis flags. No TCP listener is opened.

Consequences:

  1. When .NET is bootstrap — Python nodes read the bootstrap addr from Redis and call host.connect(info). TCP dials fail (Failed to open TCP stream: all attempts to connect to 10.0.0.67:<port> failed) because nothing listens on that port. This explains all 3 failures.
  2. When .NET is provider or querier — The node never joins the DHT. Provider sets provider_done in Redis; querier waits for that flag and prints status: pass without querying the DHT.
  3. dotnet_x_dotnet_x_dotnet passing is misleading — It validates Redis coordination and YAML output, not Kademlia interop.

The PR description says ".NET interoperability successfully passes." That overstates what the code does today. Please either wire up a real dotnet-libp2p host (listen, connect, DHT provide/find) or clearly scope the PR to Python-only and exclude .NET from the matrix until it is implemented.

Test harness: pass/fail can disagree with querier output

run-single-test.sh determines pass/fail solely from the querier container exit code. In py_x_dotnet_x_py, the querier log shows:

Test Failed: No providers found in DHT for key 'interop-test-key'
error: No providers found in DHT for key 'interop-test-key'
status: fail

…but results.yaml records status: pass because the container exited 0. The harness should parse status: pass / status: fail from querier stdout (as transport/ does for latency/error fields) and treat status: fail as a failure regardless of exit code.

Python implementation

@sumanjeet0012's review covered the main API issues (trio vs asyncio, new_host, blocking I/O, typing, logging). The follow-up commits appear to address those — get_addrs() and host.connect(info) are in place.

Remaining concern: cross-impl tests where .NET is provider still won't exercise real DHT propagation until .NET actually provides records. The py_x_dotnet_x_py false positive masks that gap today.

Scope and draft status

The PR opened as a draft to get orchestration approval before polishing implementations. That intent makes sense, but the branch has grown to include substantial Python DHT logic, error reporting, and a simulated .NET node. I'd suggest either:

  • Option A: Narrow scope to orchestration + Python-only matrix until .NET is real, or
  • Option B: Keep the full matrix but mark .NET tests as ignored / expected-fail until Program.cs runs a real peer.

Staying in draft until at least one non-Python implementation can complete a real bootstrap → provide → query cycle would match the stated goal.

Smaller suggestions

  1. Print TEST_PASS_DIR at the end of run.sh — makes finding logs easier.
  2. Align result parsing with transport/ — grep for status: pass|fail in querier logs, not just latency/error lines.
  3. PR description — Update "Current Execution Status" to reflect local matrix results and the .NET stub limitation.
  4. CI — No checks ran on this branch; consider adding a workflow (or documenting that maintainers must run ./kad-dht/run.sh locally).

Summary / action items

Priority Item
Blocker Replace .NET stub with a real libp2p host, or remove .NET from the active test matrix
Blocker Fix harness so status: fail in querier output always counts as failure
Should fix Resolve py_x_dotnet_x_py — either real .NET DHT provide or honest fail
Nice to have Print results directory path; add CI or run docs

Happy to re-review once .NET is real or scoped out, and the harness false-positive is fixed. The orchestration foundation looks mergeable; the interoperability claims are not there yet.

@sumanjeet0012 sumanjeet0012 marked this pull request as ready for review June 28, 2026 17:26
@K-21

K-21 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @acul71 for the detailed review and testing this locally! You were spot on with these catches. I've just pushed a series of commits that address all of the orchestration blockers and suggestions.

Here is a breakdown of what has been fixed:

1. Blocker: .NET implementation is a stub

Resolved. The .NET implementation in Program.cs is no longer a stub! We have fully wired it up to NethermindEth/dotnet-libp2p. It now successfully initializes LocalPeer, opens a real TCP listener, and uses the KadDhtProtocol for AddNode, BootstrapAsync, ProvideAsync, and FindProvidersAsync. We also patched a race condition inside the .NET code that was causing the DHT lookups to hang. Real cross-implementation DHT propagation is now actively being tested.

2. Blocker: Test harness exit-code parsing (False Positives)

Resolved. Great catch on the docker compose exit code masking failures. I updated lib/run-single-test.sh to explicitly parse the standard output of the querier. If the querier prints status: fail anywhere in its logs, the test is now strictly recorded as a failure in both the individual results.yaml and the aggregate summary, completely overriding the container's exit code. This guarantees no more false positives.

3. CI / GitHub Actions

Resolved. I've implemented a full CI/CD pipeline mirroring transport/. I added a custom composite action (.github/actions/run-bash-kad-dht-test/action.yml) and a workflow (kad-dht-interop-pr.yml). The matrix will now automatically run on the self-hosted, linux, x64, ephemeral runners whenever this PR is updated.

4. Minor Fixes

  • Print TEST_PASS_DIR: Added an echo to the bottom of run.sh so the absolute path to the results directory is printed right before exit.
  • Double-Counting Bug: Discovered and fixed a bug in run.sh where status: fail was being double-counted in the summary loop due to matching both the container log and the harness status.

Current Status & Remaining Work

With the harness strictly enforcing failures and the .NET node active, the matrix is now providing highly accurate results.

Because we fixed the .NET stub, the test suite is now exposing underlying network implementation bugs. We currently have 2 active issues remaining in the matrix:

  1. dotnet_x_dotnet_x_dotnet: I just pushed a final commit that adds a small delay to allow AddNode to populate the .NET routing table before BootstrapAsync fires. With this fix, the pure .NET DHT lookups are now successfully passing!
  2. Cross-implementation flakiness (py_x_py_x_dotnet and dotnet_x_py_x_dotnet): These two tests are failing intermittently. Both appear to be caused by a race condition resulting in a multistream handshake bug (Channel closed during multistream hello) that occurs when .NET attempts to dial a Python provider whose address it just pulled from the Kademlia routing table.

I am actively working on resolving this last cross-implementation multistream bug now!

@sumanjeet0012

Copy link
Copy Markdown
Collaborator

@K-21 The Kademlia DHT interoperability testing between dotnet-libp2p and py-libp2p is now working perfectly.
All interoperability test cases are passing successfully.

@acul71 Thank you, Luca, for your detailed feedback and thorough review throughout this process.

@sumanjeet0012

Copy link
Copy Markdown
Collaborator

@K-21 Let me know when the PR is ready for final review and merge.

@acul71 acul71 dismissed their stale review June 29, 2026 01:23

requested mods done

acul71 and others added 2 commits June 29, 2026 03:32
Each test records status twice (harness field and querier_output).
Count only the harness status line, which is always followed by duration.

Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror transport's image build behavior: skip docker build when images exist,
cache dotnet-libp2p clones under .cache/git-repos, and refresh vendored
sources only when the pinned commit changes. Add --help, --list-tests,
--test-select, --force-image-rebuild, and related flags.

Co-authored-by: Cursor <cursoragent@cursor.com>
@acul71

acul71 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

@K-21 — follow-up improvements are now on this PR branch (feat/kad-dht, commits 9c91446 and 032e31e).

Summary

1. Results summary double-counting (9c91446)

run.sh was reporting Passed: 16 for 8 tests because each entry records status: pass twice (harness field + querier_output). Fixed by counting only harness status lines (those followed by duration:).

2. --help and transport-style CLI (032e31e)

./run.sh now supports:

  • --help / -h
  • --list-images, --list-tests, --check-deps
  • --test-select / --test-ignore (filter which tests run)
  • --impl-select / --impl-ignore (filter which images are built)
  • --force-image-rebuild, --cache-dir

3. Docker / dotnet rebuild on every run (032e31e)

Root cause: old run.sh did rm -rf + fresh git clone of dotnet-libp2p on every invocation, invalidating Docker layer cache even when images were unchanged.

Fix: new kad-dht/lib/build-images.sh mirrors transport/ behavior:

  • Skip docker build when kad-dht-py / kad-dht-dotnet already exist (unless --force-image-rebuild)
  • Cache git clones under kad-dht/.cache/git-repos/
  • Copy vendored dotnet-libp2p into the build context only when the pinned commit changes (.kad-dht-source-commit marker)
  • Apply interop-fix.patch via shared apply_patch_if_specified

images.yaml now documents vendorDir, patchPath, and patchFile for the dotnet implementation.

Verify locally

./run.sh --help
./run.sh                    # second run should print "(already built)" for both images
./run.sh --test-select py_x_py_x_py

CI should re-run on this branch automatically. @acul71 can re-review once green.

@acul71

acul71 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

CI failure on run #28343560770 (032e31e)

Result: 7/8 passed — only dotnet_x_dotnet_x_dotnet failed (93s, exit code 1).

This does not appear related to the recent run.sh / image-caching changes. Images built, Redis started, and all cross-language tests (pydotnet) passed.

What failed

Querier output:

error: Test 2 FAILED: No providers found after 10 attempts
error: Test 4 FAILED: Value not found after 10 attempts
status: fail

Provider did succeed earlier in the same test:

Provide announced to 2 nodes for key 696E7465726F702D
Test 1 -> Success
Test 3 -> Success

So the provider announced and stored the value, but the querier never found it in the DHT after 10 retries.

Log signals

From logs/dotnet_x_dotnet_x_dotnet.log:

  • All three nodes start with Routing table updated with 0 peers
  • DHT lookup errors: Not able to dial the peer, FindNeighbours … failed
  • Multistream race: Channel closed during protocol negotiation (dial, selector /yamux/1.0.0)
  • This test runs last in the matrix (~5 min into the job), which may amplify timing sensitivity on the ephemeral runner

Likely cause

Flaky pure .NET DHT propagation — a race between provider ProvideAsync completing and querier FindProvidersAsync / GetValueAsync having a populated, dialable routing table. The existing 1s post-AddNode delay (6d47d54) helps but is not always sufficient under CI load.

Suggested fixes (for @K-21)

  1. Stronger readiness handshake — querier already waits on Redis provider_done; ensure provider only sets that after ProvideAsync + PutValue complete (may already be the case; worth verifying ordering).
  2. Increase querier backoff — more attempts or longer delay between FindProvidersAsync retries (currently 10 × ~1s).
  3. Longer routing-table settle time — bump the post-AddNode delay before BootstrapAsync on the querier (e.g. 2–3s), or poll until routing table has expected peers.
  4. Isolate flaky test — run dotnet_x_dotnet_x_dotnet with a longer compose timeout or run it first in CI to see if order-dependent.
  5. dotnet-libp2p side — investigate the Channel closed during multistream / dial failures during FindNeighbours; may need a fix upstream rather than only in the test harness.

Re-running CI to check if this is intermittent.

Document bootstrap/provider/querier naming, N³ matrix growth,
test flow, filtering, results layout, and how to add implementations.

Co-authored-by: Cursor <cursoragent@cursor.com>
@acul71

acul71 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

@K-21 — I added a kad-dht/README.md in commit a104900 to document the suite for reviewers and future contributors.

It covers:

  • The three roles (bootstrap / provider / querier) and the {bootstrap}_x_{provider}_x_{querier} naming convention
  • Why the matrix is N³ and how it grows as implementations are added
  • The per-test flow (Redis keys, pass/fail semantics including status: fail parsing)
  • How to run locally (--help, filtering, image caching)
  • Results layout, CI wiring, and how to add a new implementation
  • A short comparison table vs transport/

Could you skim it and let me know if anything is inaccurate or missing from your intent? Happy to adjust wording or add detail wherever it doesn't match how you designed the harness.

Thanks again for the solid work on this PR — the .NET integration and CI fixes look great.

@K-21

K-21 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @acul71 lot for picking up the follow-up improvements and pushing the commits for the harness, CLI, image rebuild/caching, and related cleanup. Those changes make the test harness much nicer to work with.

I also went through the README you added, and it matches the intent and design of the harness really well. I don't think it needs any changes from my side—thanks for putting that together!

Regarding the remaining failures: I'm seeing one additional test fail locally apart from "dotnet_x_dotnet_x_dotnet", whereas the GitHub workflow is currently passing all tests. I'll investigate both the local setup and the workflow to understand why there's a difference and identify the root cause.

Could you also run the test suite locally once and let me know which tests are failing on your end? That will help us compare the local and CI behavior and narrow down where the discrepancy is coming from.

@K-21

K-21 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@K-21 Let me know when the PR is ready for final review and merge.

@sumanjeet0012
The PR is ready from my side. I just want to spend some time testing it locally and troubleshooting the discrepancy where the tests are passing in the GitHub workflow but failing locally. Once I've identified the root cause (or confirmed it's an environment-specific issue), I'll update the PR accordingly.

@acul71

acul71 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Could you also run the test suite locally once and let me know which tests are failing on your end? That will help us compare the local and CI behavior and narrow down where the discrepancy is coming from.

I've run the full suite 4 times in a row (8 tests x 4 times) and all test passed !

@acul71

acul71 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

@K-21 @sumanjeet0012
I think this can be merged
Eventually there is also the daily test run and this Kad-Dht can be added in the future when is stable.
Daily CI: daily-full-interop.yml runs at 2:00 UTC on self-hosted runners. It covers transport, hole-punch, and perf (skips suites with no changes in the last 24h unless you trigger it manually).

kad-dht: PR-only today (kad-dht-interop-pr.yml). Not in the daily job yet — worth adding once #111 merges if you want the full N³ matrix on a schedule.

Also other type of tests can be run in parallel (like transport) can this eventually be the case for kad-dht ?

@K-21 K-21 changed the title Draft: feat: Add Kademlia DHT Interoperability Test Suite Architecture feat: Add Kademlia DHT Interoperability Test Suite Architecture Jun 30, 2026
@acul71

acul71 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Hi @K-21 , is this ready to be merged ?
Others points can be done in following Issues/PRs

@K-21

K-21 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Hi @K-21 , is this ready to be merged ? Others points can be done in following Issues/PRs

Hi @acul71
Yes, the PR is ready to be merged from my end.
Sure, we can address any remaining points in follow-up issues or PRs if needed.

1 similar comment
@K-21

K-21 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Hi @K-21 , is this ready to be merged ? Others points can be done in following Issues/PRs

Hi @acul71
Yes, the PR is ready to be merged from my end.
Sure, we can address any remaining points in follow-up issues or PRs if needed.

@acul71 acul71 merged commit 0bdbc9a into libp2p:master Jul 1, 2026
1 check passed
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.

3 participants