A basic LAN messenger that is forum based.
It is laid out like 4chan:
- Board
- Thread
- Post (replies within a thread)
It is a host/client, supports SQLite persistence, and discovers peers via UDP multicast, gossiping events into a self-healing mesh. No accounts, no cloud — just a LAN.
Every Duck node is both a host and a client (inspired by wanwan):
- Discovery — nodes announce themselves over UDP multicast
(
239.255.77.77:45454). - Relay — each node runs a TCP relay on a random free port.
- Gossip — nodes connect to every peer, exchange events (boards, threads, posts), and deduplicate by event id, so the mesh converges on the same history and heals itself when nodes come and go.
- Identity — a random node id is generated once and persisted, so a node is recognizably the same author across restarts. No accounts.
- Persistence — all events and forum data live in a SQLite database.
uv sync
Serve the web UI (also exposes a JSON/SSE API):
uv run duck run --http 127.0.0.1:9080
Start another instance on a second machine (or the same one) and they discover each other within a few seconds. The same UI also runs in a browser on any machine on the LAN that can reach the HTTP port.
duck list # print the boards and their threads
duck create-board Dev --about "..."
duck post <board_id> "body" --title "title (optional)"
duck reply <thread_id> "body"
duck peers # discovered nodes
Flags: --name <display name>, --store <sqlite file> (default duck.db),
--relay-port <p>, --iface <iface>, --peer host:port (manual peers),
--wait <seconds> (dwell after an action so gossip can flush).
GET /api/info node name, id, relay addr, version
GET/POST /api/boards
GET /api/boards/{id}/threads
GET /api/threads/{id}
GET /api/threads/{id}/posts
POST /api/threads {"board_id","body","title"} (title optional, defaults to first chars of body)
POST /api/posts {"thread_id","body"}
GET /api/peers
GET /api/events SSE stream of newly stored events
uv run pytest