Use when building a BitTorrent client or any peer-to-peer file-sharing protocol from first principles — not when integrating an existing torrent library. Triggers on: 'build a torrent client', 'implement bittorrent protocol', 'peer wire protocol', 'bencode parser', 'write a torrent tracker client', 'piece selection algorithm'. Covers bencode encoding, tracker HTTP/UDP protocol, the peer wire protocol, and piece-selection strategy.
Installs into .claude/skills of the current project.
Are you the author of Bittorrent Client From Scratch?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/yanacuti1121-bittorrent-client-from-scratch)
---
name: bittorrent-client-from-scratch
description: "Use when building a BitTorrent client or any peer-to-peer file-sharing protocol from first principles — not when integrating an existing torrent library. Triggers on: 'build a torrent client', 'implement bittorrent protocol', 'peer wire protocol', 'bencode parser', 'write a torrent tracker client', 'piece selection algorithm'. Covers bencode encoding, tracker HTTP/UDP protocol, the peer wire protocol, and piece-selection strategy."
origin: yana-ai — synthesized from public BitTorrent protocol specification (BEP 3, BEP 15, BEP 5) and community from-scratch tutorials indexed in codecrafters-io/build-your-own-x
license: Apache-2.0
version: 1.0.0
compatibility: yana-ai >= 0.43.2
---
# /bittorrent-client-from-scratch
## When to Use
- Building a minimal BitTorrent client (download-only or full peer) to understand the protocol, not to ship a production downloader.
- Implementing bencode (BitTorrent's serialization format) for a `.torrent` file parser.
- Writing tracker communication (HTTP/UDP announce) or a bare peer-wire-protocol handshake.
- Teaching or learning piece-selection strategy (rarest-first), choke/unchoke algorithms.
## Do NOT use for
- Production file transfer — use an existing library (libtorrent, aria2, WebTorrent) instead of hand-rolling protocol code for a real product.
- General P2P networking unrelated to BitTorrent's specific wire format (see `wamp-pubsub-patterns` or `raft-consensus-patterns` for other distributed patterns).
- DHT-only (trackerless) discovery as a first implementation — build the tracker-based path first (see Step 4), DHT is a documented extension, not the starting point.
---
## Protocol Layers (build in this order)
```
1. Bencode — the serialization format everything else is written in
2. .torrent file — bencoded dict describing the download (info hash, piece hashes, tracker URL)
3. Tracker — HTTP/UDP request that returns a peer list
4. Peer wire — TCP protocol between two peers: handshake, message framing, piece exchange
5. Piece manager — tracks which pieces you have/need, drives requests
6. (Optional) DHT — trackerless peer discovery, BEP 5 — add only after 1-5 work
```
## Step 1: Bencode
Four types only — no schema needed, all self-describing:
```
Integer: i<number>e → i42e = 42
Byte string: <length>:<bytes> → 4:spam = "spam"
List: l<items>e → l4:spami42ee = ["spam", 42]
Dictionary: d<key><value>...e → d3:cow3:mooe = {"cow": "moo"}, keys sorted by raw byte order
```
Write both `decode(bytes) -> value` and `encode(value) -> bytes` — you need `encode` to re-derive the info hash (see Step 2) and to build tracker announce requests.
## Step 2: Parse the .torrent File
A `.torrent` file is a bencoded dict with keys: `announce` (tracker URL), `info` (dict containing `name`, `piece length`, `pieces` — a concatenated string of 20-byte SHA-1 hashes, one per piece, `length` for single-file or `files` for multi-file torrents).
The **info hash** — the torrent's unique identifier everywhere in the protocol — is `SHA1(bencode(info_dict))`, computed over the *raw bencoded bytes* of just the `info` value, not a re-serialization from a parsed struct (re-encoding can silently reorder or reformat and produce the wrong hash — keep the raw byte range from the original decode).
## Step 3: Talk to the Tracker
HTTP GET to the `announce` URL with query params: `info_hash` (20 raw bytes, URL-encoded), `peer_id` (your 20-byte client ID), `port`, `uploaded`, `downloaded`, `left`, `compact=1`. Response is a bencoded dict with `interval` (seconds until re-announce) and `peers` — with `compact=1`, a byte string where every 6 bytes is one peer (4 bytes IP + 2 bytes port, big-endian).
UDP tracker protocol (BEP 15) exists for trackers that don't support HTTP — it's a separate connect/announce handshake, implement it only if a tracker in practice requires it.
## Step 4: Peer Wire Protocol
Handshake (fixed 68 bytes): `\x13BitTorrent protocol` + 8 reserved bytes + 20-byte info hash + 20-byte peer ID. Both sides send this first; a peer that returns a different info hash is not serving your torrent — drop the connection.
After the handshake, all messages are length-prefixed (4-byte big-endian length, then 1-byte message ID, then payload). Core message IDs:
```
0 choke 5 bitfield (which pieces this peer has)
1 unchoke 6 request (index, begin, length)
2 interested 7 piece (index, begin, block data)
3 not interested 8 cancel
4 have (piece idx)
```
State machine per connection: you start `choked` + `not interested`. Send `interested`. Wait for the peer to `unchoke` you — only then can you `request` blocks. A zero-length message (just the 4-byte length field, no ID) is a keep-alive.
## Step 5: Piece Manager
- Track piece state as a bitfield (have / don't have / in-flight).
- Split each piece into blocks (commonly 16KB) — `request` messages are per-block, not per-piece.
- **Rarest-first**: track which peers have which pieces; request pieces that fewest peers have first — this keeps piece availability balanced across the swarm and avoids everyone racing for the same common piece.
- On receiving a full piece, verify its SHA-1 against the hash from the `.torrent` file's `pieces` field before writing to disk or announcing `have` — a mismatched hash means a corrupt/malicious peer, discard and re-request from someone else.
## What NOT to Do
- Don't reorder or reformat the `info` dict before hashing it — you'll compute the wrong info hash and every tracker/peer will reject you.
- Don't request whole pieces in one message — the wire protocol requests blocks (~16KB); requesting a multi-MB piece in one `request` message is invalid per spec and most peers will drop you.
- Don't skip piece-hash verification "to save time" — an unverified piece can poison the rest of the download since later pieces may depend on file layout assumptions from earlier ones.
- Don't build DHT before the tracker path works end-to-end — DHT is a strictly harder distributed-hash-table problem layered on top of a protocol you should already understand.