Building Embedded IPFS for Python Services #1272
asabya
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
The Vision
Python is everywhere — APIs, data pipelines, ML services, IoT — but when these services need content-addressed data exchange, they typically shell out to a Go IPFS daemon or call an HTTP API. What if any Python process could natively speak IPFS?
py-ipfs-lite is a lightweight, embeddable IPFS peer built entirely on py-libp2p. It's a Python port of ipfs-lite (Go) that gives any Python service the ability to add files, resolve CIDs, and exchange blocks with Go IPFS peers — all in-process, no daemon required.
This post covers what was built, how Go interop was achieved, and the py-libp2p improvements that were made along the way. It demonstrates that py-libp2p is ready to be a foundation for real protocol implementations — and collaboration on getting it there would be welcome.
What works today:
provide()on block add,find_providers()fallback in get_blockThe Road to Go Interop
Getting Python and Go ipfs-lite peers to exchange blocks was the hardest and most rewarding part of this project. The challenge wasn't the protobuf messages — it was understanding how Go's bitswap engine manages streams.
The key insight: Go's bitswap does not respond on the same stream where it receives a wantlist. Instead, it closes the read end immediately and opens a new outbound stream to push blocks back. The initial Python implementation assumed same-stream request/response, which worked fine between two Python peers but silently failed with Go.
Two-phase block fetch: To match Go's behavior, a two-phase negotiation was implemented:
WANT_HAVEis sent to all connected peers; the firstHAVEresponse is awaited (or a direct block if Go decides the block is small enough to skip the HAVE phase)WANT_BLOCKis sent only to peers that responded withHAVE, then block delivery is awaited on a new inbound streamThe
handle_streamhandler also had to be switched to a looping design (reading multiple messages per stream, like Go'shandleNewStream) since Go reuses streams for sequential want messages.The result: a Python peer can now
add_file()locally and a Go peer canget_block()for that CID over the network — and vice versa.What Was Fixed in py-libp2p's Yamux
Building a real protocol on py-libp2p exposed two significant issues in the yamux stream muxer. Both have been fixed in a fork and are being contributed upstream.
1. Frame type correctness (#1271)
SYN, ACK, FIN, and RST control frames were being sent as
TYPE_DATAinstead ofTYPE_WINDOW_UPDATE. This is a spec violation — go-yamux sends all control frames asTYPE_WINDOW_UPDATE, and thelengthfield carries the window delta, not a data payload size. Additionally, concurrent writes could interleave frames on the wire, corrupting messages.Fix: All control frames were changed to
TYPE_WINDOW_UPDATEand a_write_lockwas added to serialize writes. (asabya/py-libp2p#1)2. Receive window auto-tuning (#1270)
py-libp2p uses a fixed 256KB receive window. Go-yamux dynamically grows it up to 16MB based on RTT, which means a 1GB transfer needs ~4,000 round-trips with the fixed window vs ~64 with auto-tuning.
Fix: go-yamux's auto-tuning algorithm was ported — the window doubles each RTT epoch with 50% hysteresis, plus background RTT measurement via ping/pong. (asabya/py-libp2p#2, upstream PR #1269)
What's Next
py-ipfs-lite is functional but early (v0.1.0). Community input and collaboration would be greatly appreciated:
Feedback, questions, and contributions are all welcome. The project is at github.com/asabya/py-ipfs-lite.
Beta Was this translation helpful? Give feedback.
All reactions