Cover image for Dev log #14 Hardening the DHT against Eclipse attacks and the endless battle with flaky p2p tests

Yash Kumar Saini

Dived deep into libp2p security this week with a new IP subnet diversity feature for the Kad-DHT, while fighting a few stubborn race conditions in the test suite. 3 PRs open and some automated maintenance on my Neovim setup kept the gears turning.

TL;DR

Security hardening took center stage this week as I focused on making our Kademlia DHT implementation more resilient against Eclipse attacks. Between drafting logic for IP subnet diversity and chasing down race conditions in the py-libp2p test suite, it was a week of high-leverage architectural thinking paired with the granular frustration of de-flaking async code.

The Fight for a More Secure DHT

Most of my brainpower this week went into py-libp2p, specifically around how we handle peer selection in the Kademlia DHT. I opened a significant PR, feat(kad_dht): enforce IP subnet diversity in k-buckets (#1383), which adds 226 lines of new logic to ensure our routing table doesn't get saturated by peers from the same network neighborhood.

If you're not deep in the p2p weeds, here's the deal: an Eclipse attack happens when an attacker manages to surround a target node with their own malicious nodes. If all your "neighbors" in the network are controlled by one person, they can effectively cut you off from the real network or feed you fake data. By enforcing IP subnet diversity, we make it much harder (and more expensive) for an attacker to pull this off because they can't just spin up a thousand nodes on a single cloud provider's subnet and expect to fill up our k-buckets.

It’s one of those features that feels invisible until it’s not there, but it’s critical for the long-term health of the library.

I also kicked off a deep-dive discussion titled Eclipse-attack hardening for kad-dht: our approach, and why we dropped randomized lookup selection (#1383 / #1384). We had originally looked at randomized lookup selection as a defense mechanism, but after some back-and-forth, we decided to drop it in favor of more robust bucket management. It’s always better to realize an approach is suboptimal before you merge the code.

The Tax of Asynchronous Testing

When you're working on a library that relies heavily on asynchronous networking, you eventually have to pay the "flaky test tax." This week, I spent a good chunk of time hunting down two particularly annoying race conditions that have been haunting our CI.

The first was test(timed_cache): de-flake test_expiry_removal (#1408). The issue was a classic: the test was racing against a background sweeper thread. We were using trio.sleep, which is usually fine, but in a CI environment where CPU cycles can be unpredictable, the sweeper wasn't always finishing its cleanup before the test checked the results. I also opened an issue to track this specifically: flaky: test_expiry_removal races the background sweeper thread.

The second one was test(pubsub): de-flake test_gossip_gate_filters_peers (#1401). Similar story here—subscription propagation in Gossipsub is inherently timing-dependent. If the test moves faster than the network (even a mocked one), everything breaks. Fixing these isn't the most glamorous work, but there is a specific kind of satisfaction in seeing a red CI pipeline finally turn green after you've tightened up the timing logic.

Maintenance and Tooling

On the side, I kept my environment healthy. My nvim config saw 6 commits this week, though they were mostly automated chores. I have a CI setup that keeps all my plugins updated to the latest versions. It’s a "set it and forget it" kind of thing, but it ensures that when I sit down to code, my editor is actually ready for me.

With 7 additions and 7 deletions across those 6 commits, it was a perfectly balanced week of maintenance. No new bloat, just keeping the tools sharp.

Tech Stack & Vibe

This week was heavily skewed toward Python, given the work on py-libp2p. With over 96MB of Python code in the mix (thanks to the sheer scale of the libp2p ecosystem), it’s definitely my primary focus right now. But looking at my language stats, the polyglot life is still very much real—TypeScript, Rust, and Lua are all sitting there in the background, ready for when I pivot back to frontend or systems work.

The add/delete ratio was interestingly tight this week (7 up, 7 down in the repo I committed to directly). It reflects a week of refinement rather than raw feature expansion. Even the libp2p PRs, while adding new logic, were very focused on fixing specific behaviors rather than just piling on more code.

What's Next

I have three PRs currently sitting in "OPEN" status on py-libp2p. My priority for next week is getting those merged. The IP subnet diversity work is the big one—I want to make sure the implementation is bulletproof before it hits main. Once those are in, I’m hoping to get back to some net-new feature work, but as anyone in OSS knows, the next flaky test is probably just around the corner.

Catch you in the next update. Keep shipping.


Yash K Saini — Engineer, building in public — AI/ML, low-level (Rust/C/C++), and open source.

GitHub · X · LinkedIn · Portfolio

Generated by DevNotion