Observed since 20 Oct, 15:27 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
2xPPg (Peer to Peer Playground): dead-simple Python P2P building blocks.
By justharsiz
2xPPgmeans 2x "P" + "Pg":
- Peer + Peer = two equal participants
- Playground = a place to experiment and build
So this package is a Peer to Peer Playground for Python.
2xPPg is a beginner-friendly Python toolkit for building peer-to-peer software without forcing you to become a networking expert first.
Think of it like pyautogui, but for p2p ideas:
You focus on:
2xPPg handles:
One user sends text/data directly to another peer (or broadcasts to many).
Peers host/share files. This package gives easy primitives for sending and receiving files. You can build chunk scheduling, swarm logic, and integrity verification on top.
Each peer stores a shared state map and can periodically sync snapshots. You can layer consensus, signatures, and block rules above this.
Sensor data streaming, collaborative docs, game states, local mesh experiments, and more.
No networking library can magically guarantee universal inbound connectivity without any helper path.
In real life:
2xPPg gives you:
So yes, you can avoid depending on one central always-on server, but practical networks still require traversal strategy.
pip install 2xppg
For local development:
python -m venv .venv
.venv\Scripts\activate
pip install -e .
Imagine a group voice call:
peer_id)join(host, port))send_text, send)on("text", handler))Now replace voice with Python dict payloads.
That is 2xPPg.
QuickChatIf you want the easiest possible start:
from xppg import QuickChat
chat = QuickChat("my-chat")
chat.on_message(lambda user, text: print(f"[{user}] {text}"))
# Optional: connect to another peer
# chat.connect("192.168.1.42")
chat.run()
That is it. No explicit asyncio, no message kinds, no payload dicts.
PlaygroundCreate chat_node.py:
import asyncio
from xppg import Playground
async def main():
pg = Playground(app_name="my-first-chat", port=7337)
nat = await pg.start(open_port=True)
print("I am:", pg.peer_id)
if nat:
print("NAT result:", nat)
pg.on("text", lambda src, kind, payload: print(f"[{src}] {payload['text']}"))
# If this node should join another node:
# await pg.join("192.168.1.42", 7337)
while True:
text = await asyncio.to_thread(input, "> ")
if text.strip().lower() in {"quit", "exit"}:
break
await pg.send_text(text)
await pg.stop()
if __name__ == "__main__":
asyncio.run(main())
Run on two machines in the same LAN (or otherwise reachable):
python chat_node.py
On one machine, uncomment join() and point it at the first.
Playground(app_name, host="0.0.0.0", port=7337)Creates one p2p node.
app_name: your app/protocol namespacehost: local bind hostport: local listening portpeer_id: auto-generated identity stringstate: built-in replicated key/value state (SharedState)QuickChat(room, port=7337, open_port=True)Ultra-simple beginner wrapper around Playground.
chat.on_message(handler) where handler is (user, text)chat.connect(host, port=None) to join another peerchat.run() starts everything and opens an input loopquit, exitawait pg.start(open_port=True)Starts listening server.
open_port=True, tries UPnP port mappingNonepg.quick_start(open_port=True)Sync convenience method for simple scripts that do not want explicit event-loop setup.
await pg.join(host, port)Connects to another peer and performs handshake.
pg.on(kind, handler)Registers a handler for a message type. Handler signature:
def handler(src_peer_id: str, kind: str, payload: dict) -> None:
...
await pg.send(kind, payload, to=None)Send custom message type to one peer (to="peer_id") or broadcast (to=None).
await pg.send_text(text, to=None)Convenience for text messages.
await pg.share_file(path, to=None)Sends a file payload. (In this alpha release, this is whole-file base64 transport. For production-scale torrents, use chunking + verification strategy.)
await pg.request_state(to=None) / await pg.push_state(to=None)State sync helpers.
The SharedState class is the simplest version of a replicated map:
Example:
from xppg import SharedState
state = SharedState()
state.set("height", 1)
state.set("height", 2)
print(state.get("height")) # 2
When peers exchange state snapshots (state_update messages), newer versions win by default.
This is enough to prototype:
For real blockchain logic, add:
Current helper:
share_file(path) sends one encoded blob to target/broadcastTo build torrent-like transfer on top, extend into:
2xPPg is the playground foundation for this.
Most people are behind routers and NAT. Inbound connections can fail unless:
2xPPg currently includestry_upnp_map) for easy casesUse multi-hop peer relay:
This keeps your system decentralized in operation, even when direct routes are blocked.
SharedStatepush_stateIf networking feels hard, remember: You are designing message flow first. Transport details are implementation layers.
This alpha package prioritizes simplicity and education. For production:
import asyncio
from xppg import Playground
async def main():
pg = Playground("ledger-playground", port=7444)
await pg.start()
def on_text(src, _kind, payload):
print(f"[msg:{src}] {payload['text']}")
pg.on("text", on_text)
# toy ledger state
pg.state.set("alice", 100)
pg.state.set("bob", 50)
# join a seed peer (if you have one)
# await pg.join("SEED_IP", 7444)
# periodically sync state snapshots
async def sync_loop():
while True:
await pg.push_state()
await asyncio.sleep(5)
task = asyncio.create_task(sync_loop())
try:
while True:
cmd = await asyncio.to_thread(input, "cmd> ")
if cmd == "quit":
break
if cmd.startswith("pay "):
_, src, dst, amt = cmd.split()
amt = int(amt)
pg.state.set(src, pg.state.get(src, 0) - amt)
pg.state.set(dst, pg.state.get(dst, 0) + amt)
await pg.push_state()
continue
await pg.send_text(cmd)
finally:
task.cancel()
await pg.stop()
if __name__ == "__main__":
asyncio.run(main())
src/xppg/playground.py - main high-level p2p APIsrc/xppg/state.py - replicated shared state mapsrc/xppg/nat.py - UPnP mapping helperpython -m pip install --upgrade build twine
python -m build
python -m twine check dist/*
python -m twine upload --repository testpypi dist/*
python -m twine upload dist/*
pip install 2xppg
MIT
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.2.0 | 17 Apr 2026 | 1 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 15:27 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:92cf3796ba62e15063fb6f7e231f74dabd86fafe57d6864d1fb109efcf13bf24