A minimal BitTorrent‑style P2P client written in Go.
It implements chunked file transfer, rarest‑first piece selection and decentralised peer discovery via a light‑weight DHT.
- Features
- Quick Start
- Command‑line Interface
- How it Works
- Project Layout
- Observability & Logging
- Validation Checklist
- Roadmap
- Contributing
- License
- 📦 Chunked transfer – files are split into 256 KiB pieces which are verified by SHA‑1.
- 🎲 Rarest‑first scheduling – always request the least‑available piece in the swarm.
- 🌐 Tracker‑less peer discovery – small JSON‑based DHT inspired by Kademlia (160‑bit XOR space, k=8).
- ⚡ Fully concurrent – each peer connection has independent goroutine reader/writer pairs; the DHT runs its own UDP dispatcher.
- 📝 Structured JSON logs – perfect for piping through
jqor shipping to ELK/Grafana. - 🧪 Self‑contained test‑suite – unit tests for storage splitting/joining and metainfo JSON encoding.
Requires Go 1.22+. All commands run inside the project root.
# 1 . Install dependencies (none external, Go modules vendor everything)
go test ./... # run unit tests
go build ./cmd/bittorrent # build the single binaryDHT‑only node (Terminal 1)
./bittorrent -dht-listen :10000 -tcp-listen :10001Seeder (Terminal 2)
./bittorrent -seed ~/Movies/big_buck_bunny.mp4 -dht-listen :20000 -tcp-listen :20001 -bootstrap :10000Leecher → Seeder (Terminal 3)
./bittorrent -get ~/Movies/big_buck_bunny.mp4.bit -dest ~/Downloads -dht-listen :30000 -tcp-listen :30001 -bootstrap :10000 -keep 1200Leecher #2 (Terminal 4)
./bittorrent -get ~/Movies/big_buck_bunny.mp4.bit -dest ~/Downloads -dht-listen :40000 -tcp-listen :40001 -bootstrap :10000Pipe the output of each terminal to jq for pretty log formatting:
./bittorrent -dht-listen :10000 ... | jq .| Flag | Purpose | Example |
|---|---|---|
-seed <file> |
Seed the given payload file. Generates <file>.bit on first run. |
-seed ~/Movies/foo.mkv |
-get <meta.bit> |
Download a file given its .bit metainfo. |
-get foo.mkv.bit |
-dest <dir> |
Output directory for downloaded file. | -dest ~/Downloads |
-tcp-listen <addr> |
TCP listen address, e.g. :0 (random port) or 0.0.0.0:6881. |
-tcp-listen :20001 |
-dht-listen <addr> |
UDP listen address for DHT. Empty disables DHT. | -dht-listen :20000 |
-bootstrap <addr[,addr]> |
Comma‑separated list of UDP bootstrap nodes. | -bootstrap :10000,example.com:20000 |
-peer <addr[,addr]> |
Static list of TCP peers (optional shortcut instead of DHT). | -peer 1.2.3.4:20001 |
-keep <sec> |
After downloading, continue seeding for n seconds. | -keep 600 |
graph TD
%% Swarm (TCP)
subgraph Swarm_TCP["Swarm (TCP)"]
P1["Peer 1"] -->|request piece| Seeder
P2["Peer 2"] --> Seeder
end
Seeder --|have/bitfield|--> P1
Seeder --|have/bitfield|--> P2
Leecher["Leecher"] --|announce/findPeers|--> DHT["DHT Service (UDP)"]
Seeder --|announce|--> DHT
- Session orchestrates one torrent: holds
Meta, in‑memory piece cache and spawns DHT + Swarm. - Swarm maintains active TCP peers and triggers rarest‑first selection every 2 s.
- DHT Service wraps a UDP node that speaks five JSON messages:
ping,pong,announce,findPeers,peers.
More design details (data structures, algorithms, diagrams) live in report.pdf – grab the compiled PDF for the full write‑up.
cmd/bittorrent/ ← main() + CLI
internal/
app/ ← Session, Swarm, DHT service, CLI config
dht/ ← UDP node & routing table (Kademlia‑like)
peer/ ← TCP peer object (reader + writer goroutines)
protocol/ ← Message framing, handshake, hashes
storage/ ← Piece split/join + bitfield utils
logger/ ← JSON line logger
metainfo/ ← .bit file marshal/unmarshal
tests/ ← Additional integration tests
All subsystems emit structured JSON lines to STDOUT:
{"event":"request","piece":42,"peer":"127.0.0.1:20001","ts":"2025‑04‑27T15:41:09Z"}Pipe into jq/bunyan/loki‑promtail for real‑time dashboards. Key events:
| Event | Meaning |
|---|---|
joined_to_peer |
Successful TCP dial. |
have |
A piece finished downloading. |
complete |
Full file assembled on disk. |
dht_* |
Any DHT interaction (ping/pong/announce/findPeers). |
- Upload & download a file via chunk exchange – demonstrated in the Quick Start scenario.
- Log peer join/leave & chunk updates – see JSON samples above.
- Visualise download progress per peer – aggregate
haveevents in Grafana/Loki or simply observe order in logs.
- NAT traversal (uTP / hole‑punching)
- Multi‑file torrents & web seeding
- Incentive layer (tit‑for‑tat)
- Periodic DHT re‑announce & seed expiration
- Fork ℹ️
- Create a feature branch (
git checkout -b awesome‑feature) - Commit your changes with clear messages
- Push & open a Pull Request
Coding style: go fmt ./... + golangci‑lint run passes.
This project is licensed under the MIT License