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.
Scanned 9/9/2026
Install to Claude Code
npx -y skills add yanacuti1121/Yana-AI --skill bittorrent-client-from-scratch --agent claude-codeInstalls 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 automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/yanacuti1121-bittorrent-client-from-scratch)More formats (shields.io, HTML) on the badges page.
---
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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!