npm.io
0.1.0 • Published 1 month ago

bt-bencode

Licence
MIT
Version
0.1.0
Deps
1
Size
65 kB
Vulns
0
Weekly
0
DeprecatedThis package is deprecated

bt-bencode

A bencode codec that keeps bytes as bytes, plus a reader for BitTorrent v1, v2 and hybrid torrents.

Install

npm install bt-bencode

Development

npm test        # node --test, runs .ts sources directly (Node >= 22.18)
npm run typecheck
npm run build   # emits ESM + .d.ts to dist/

Layout

src/bencode/   byte-faithful bencode codec; knows nothing about torrents
  bdict.ts     BDict + the Bencode type
  decode.ts    encode.ts
  inspect.ts   read-only render for debugging
  brand.ts     the tag that keeps a render out of a builder
  errors.ts    DecodeError, TypeMismatchError
src/torrent/   BEP 3/12/47/52 knowledge
  schema.ts    known keys, their types and scopes
  torrent.ts   typed read-only view over a decoded torrent
  infohash.ts  hashing a dictionary you built or modified
  errors.ts    InvalidTorrentError

The codec layer never imports the torrent layer.

inspect() output is one-way: it is branded, so a debug render cannot be fed back into BDict.from() at any nesting depth, and encode() does not accept it. A value mangled for display cannot round trip into a file.

Torrent is a different thing -- a read-only projection over a live, mutable BDict. Mutating torrent.raw and re-encoding is the intended way to modify a torrent. Infohashes are computed from the source bytes, so they do not track such mutations; use infoHashV1(info) / infoHashV2(info) to hash a dictionary you have changed.

Test fixtures

The fixtures in src/__fixtures__/ are generated, not hand-authored, and regenerating them requires only Docker:

npm run fixtures            # regenerate + verify against libtorrent
npm run fixtures:generate   # regenerate only

Both generation and verification run in the container defined by docker/Dockerfile, so output is byte-identical across machines and nothing is installed on the host. scripts/make-fixtures.py uses the Python standard library only and is deliberately independent of this package — fixtures built with our own encoder would inherit our own bugs and the roundtrip tests would pass vacuously.

The source tree is synthesised from a hash keystream rather than committed, and file sizes are chosen so each fixture exercises a distinct branch:

File Size Exercises
a.bin 96 KiB 3 whole pieces; no padding file in the hybrid torrent
b.txt 1 KiB under one piece, so it is omitted from piece layers
empty.bin 0 no pieces root at all — the case that breaks a type requiring one
sub/c.bin 48 KiB 1.5 pieces, so the final piece subtree needs zero-padding

Six fixtures are produced: v1, v2 and hybrid over that tree, plus single-v1, single-v2 and single-hybrid — single-file torrents use info.length instead of info.files and are probably the most common shape in existence.

Verification also writes src/__fixtures__/expected.json, holding libtorrent's own infohashes for every fixture. Nothing else in the repo knows what those should be, so without an independently derived oracle an off-by-one in slicing the info dictionary would go unnoticed.

Piece length is 32 KiB — two 16 KiB blocks per piece, so v2 piece subtrees have real structure. At the 16 KiB minimum the merkle tree collapses into a flat list and tests nothing.

Verification (scripts/verify-fixtures.py) checks each fixture twice against libtorrent 2.x: parsing validates piece layers against each file's pieces root, and a forced recheck against the real source tree validates pieces root for files smaller than one piece, which have no piece layer to cross-check against. BEP 52 does not state whether a sub-piece file's leaf count rounds to the next power of two or to a full piece, so that reading is confirmed empirically rather than assumed.

Keywords