gnutella-bun-client: GBun: A Modern Gnutella Servent Built Like a Library, Not a Fossil
A zero-dependency Bun and TypeScript implementation that turns a legacy peer-to-peer protocol into a testable CLI and an embeddable library, with real support for compression, QRP, and defensive parsing.
- GBun matters because it makes a legacy peer-to-peer protocol legible inside a modern codebase without sanding off its strange parts.
- Its biggest design choice is not speed or scale. It is testability, achieved through collaborator injection, strict typing, and zero production dependencies.
- QRP is the project’s sharpest technical idea because it turns query traffic from a broadcast problem into a selective routing problem.
- The repo’s real value is architectural: it preserves protocol knowledge in a form that can be embedded, inspected, and extended.
The Weird Part Is the Point
GBun is not a nostalgia project wearing a modern coat of paint. It is a servent, a Gnutella node that can both serve and consume traffic, built as a clean TypeScript system that can also be embedded in other apps. That dual identity changes everything about the architecture.
The README puts it plainly: "GBun is a small Gnutella client you can run from the terminal or embed in a TypeScript app." That is the unusual promise here. The code is not just a client. It is a protocol engine with a CLI wrapped around it.
GBun GBun is a small Gnutella client you can run from the terminal or embed in a TypeScript app.
Zero Dependencies, Real Protocol Work
The stack is stripped down on purpose. Bun provides the runtime, TypeScript provides the types, and the project leans on built-ins like node:net, node:zlib, and node:crypto instead of pulling in production dependencies.
That matters because every extra abstraction would blur the protocol story. Here, the important work is visible: binary parsing, socket management, compression, and peer routing. The repository does not outsource the hard parts.
| Dimension | Legacy Gnutella clients | GBun |
|---|---|---|
| Runtime style | Desktop-heavy, often monolithic | Bun-native, scriptable, embeddable |
| Dependency footprint | Large and historically tangled | Zero production dependencies |
| Primary goal | Full client feature set | Protocol clarity and reuse |
| Testability | Often coupled to live sockets | Collaborator injection and mockable network seams |
| Protocol fidelity | Varies by client and era | Explicit support for Gnutella 0.6 features |
| Developer ergonomics | Hard to inspect or reuse | Small surface area with strict TypeScript |
The Servent as a Collaborator Machine
The heart of the project is GnutellaServent, and the design choice worth noticing is collaborator injection. Instead of hardwiring time, scheduling, and sockets, the code passes in a clock, a scheduler, and a netFactory.
That is not just neatness. It makes the protocol layer testable in isolation. You can swap the network layer for an in-memory fake and exercise the same code paths without needing a real TCP peer on the other end.
const collaborators = buildCollaborators({
clock,
scheduler,
netFactory,
});
const servent = new GnutellaServent(collaborators);
await servent.start();
How a Socket Becomes a Gnutella Conversation
Once a peer connects, the runtime attaches the socket, negotiates capabilities, and can enable compression in either direction. Raw bytes are not treated as a blob. They are fed through codecs that turn descriptor frames into typed events.
That pipeline is the project’s real engineering discipline. The code has to survive malformed peers, partial frames, and legacy protocol quirks while still keeping the state machine comprehensible. The defensive limits in the repo exist for a reason: old network code is hostile code.
attachPeer(socket, peerState);
if (capabilities.compressIn) {
socket.pipe(inflater).pipe(peerParser);
}
for (const event of consumePeerBuffer(buffer)) {
handleDescriptor(event);
}
QRP Is the Real Trick
Query Routing Protocol is the part that makes Gnutella feel like a system, not just a swarm. Without a filter, a query can become a broadcast storm. With QRP, peers use a compact routing table to decide whether a search is worth forwarding.
That is the sharpest sign that GBun is doing serious protocol work. It is not merely parsing old packets. It is reconstructing the logic that kept a decentralized network from drowning itself.
const hit = matchQuery(qrpTable, queryTerms);
if (hit) {
forwardQuery(peer, query);
} else {
dropQuery(peer, query);
}
Why This Still Matters
GBun sits in a niche that modern P2P systems usually skip. It is useful for legacy interoperability, protocol study, and embedded experimentation. It is not trying to outgrow IPFS or WebTorrent. It is trying to make an old network dialect accessible in a modern development environment.
| Project class | Strength | Trade-off | Best fit |
|---|---|---|---|
| GBun | Protocol fidelity with clean architecture | Not a broad consumer platform | Developers, researchers, and embedded tooling |
| Legacy Gnutella clients | Historical completeness and network familiarity | Heavy, dated, and harder to reuse | Users of the original ecosystem |
| IPFS and WebTorrent | Modern decentralized distribution models | Different assumptions and APIs | General-purpose distributed apps |
That is why the repository feels unusually durable. The value is not in novelty. It is in preservation through clarity, the kind of engineering that keeps a hard protocol understandable after its first wave of users has moved on.





