# @ctrl/torrent-file

> Parse a torrent file (name, hash, files, pieces)

Latest version **4.5.2** (published 2026-10-04) · MIT license · 0 weekly downloads

## Install

```sh
npm install @ctrl/torrent-file
pnpm add @ctrl/torrent-file
yarn add @ctrl/torrent-file
bun add @ctrl/torrent-file
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 4.5.2 |
| Published | 2026-10-04 |
| First published | 2019-05-18 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM |
| Node | >20 |
| Dependencies | 0 |
| Unpacked size | 41.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 17 |
| Author | Scott Cooper |
| Maintainers | scttcper |

## Links

- npm: https://www.npmjs.com/package/@ctrl/torrent-file
- Repository: https://github.com/scttcper/torrent-file
- Homepage: https://torrent-file.pages.dev
- Issues: https://github.com/scttcper/torrent-file/issues
- npm.io page: https://npm.io/package/@ctrl/torrent-file

## Recent versions

- 4.5.2 (latest) — 2026-10-04
- 4.5.1 — 2026-07-13
- 4.5.0 — 2026-02-16
- 4.4.0 — 2025-10-05
- 4.3.0 — 2025-10-05
- 4.2.0 — 2025-09-16
- 4.1.0 — 2024-09-09
- 4.0.0 — 2024-03-11
- 3.0.0 — 2023-09-15
- 2.0.3 — 2023-05-06
- 2.0.2 — 2022-06-14
- 2.0.1 — 2021-12-20
- 2.0.0 — 2021-12-20
- 1.3.3 — 2021-04-17
- 1.3.2 — 2020-12-14
- … 7 more at https://npm.io/package/@ctrl/torrent-file/versions

## README

# torrent-file [![npm](https://badgen.net/npm/v/@ctrl/torrent-file)](https://www.npmjs.com/package/@ctrl/torrent-file)

> Parse, inspect, and encode BitTorrent metainfo files.

Supports BitTorrent v1 ([BEP 3](https://www.bittorrent.org/beps/bep_0003.html)), v2 ([BEP 52](https://www.bittorrent.org/beps/bep_0052.html)), and hybrid torrent files containing both formats.

This project is based on [parse-torrent](https://www.npmjs.com/package/parse-torrent) and [node-bencode](https://github.com/themasch/node-bencode). It includes a strict [bencode](https://www.bittorrent.org/beps/bep_0003.html#bencoding) encoder and decoder built around `Uint8Array` rather than Node.js `Buffer`.

Demo: https://torrent-file.pages.dev

## Install

```console
npm install @ctrl/torrent-file
```

## API

### `info(file)`

Returns display metadata, trackers, web seeds, and the detected `version`: `'v1'`, `'v2'`, or `'hybrid'`.

```ts
import fs from 'fs';

import { info } from '@ctrl/torrent-file';

const torrentInfo = info(fs.readFileSync('myfile'));
console.log({ torrentInfo });
```

### `files(file)`

Returns file and piece information:

- `length` is the sum of the listed file lengths.
- `offset` is the file's byte offset in the protocol piece space. BEP 52 aligns every non-empty v2 file to a piece boundary, so v2 offsets can contain gaps that are not included in `length`.
- `pieces` contains hexadecimal SHA-1 hashes for v1/hybrid torrents and is absent for v2-only torrents.
- `piecesRoot` is a hexadecimal SHA-256 Merkle root on v2/hybrid files.
- `pieceLayers` maps hexadecimal pieces roots to arrays of hexadecimal SHA-256 hashes.

Paths use the host platform's path separator. For v2 torrents, the advisory torrent `name` is prepended to the paths returned from the BEP 52 file tree.

```ts
import fs from 'fs';

import { files } from '@ctrl/torrent-file';

const torrentFiles = files(fs.readFileSync('myfile'));
console.log({ torrentFiles });
```

### `hash(file)`

Returns the v1 SHA-1 info hash. It throws for a v2-only torrent because that torrent does not have a v1 identity.

```ts
import fs from 'fs';

import { hash } from '@ctrl/torrent-file';

const torrentHash = hash(fs.readFileSync('myfile'));
console.log({ torrentHash });
```

### `hashV2(file)`

Returns the full 32-byte v2 SHA-256 info hash as hexadecimal. It throws for a v1-only torrent.

```ts
import fs from 'fs';

import { hashV2 } from '@ctrl/torrent-file';

const torrentHashV2 = hashV2(fs.readFileSync('myfile'));
console.log({ torrentHashV2 });
```

### `hashes(file)`

Returns the available v1 (SHA-1) and v2 (SHA-256) info hashes along with the detected torrent version. `infoHash` is present for v1 and hybrid torrents; `infoHashV2` is present for v2 and hybrid torrents.

```ts
import fs from 'fs';

import { hashes } from '@ctrl/torrent-file';

const h = hashes(fs.readFileSync('myfile'));
console.log(h.version); // 'v1', 'v2', or 'hybrid'
console.log(h.infoHash); // SHA-1 (v1/hybrid only)
console.log(h.infoHashV2); // SHA-256 (v2/hybrid only)
```

Info hashes are computed from the exact bencoded `info` dictionary bytes found in the file, as required by [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#trackers) and [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#infohash). They are not computed from a decode/encode round trip.

### Low-level bencode

`decode()` returns bencoded byte strings as `Uint8Array`, including byte strings used as dictionary values. Decoded dictionaries have a null prototype, so keys such as `__proto__`, `constructor`, and `toString` are ordinary own properties rather than inherited JavaScript behavior. `encode()` accepts strings, `Uint8Array`, safe integers, arrays, and dictionaries.

The decoder rejects noncanonical or incomplete bencode, including unordered/duplicate dictionary keys, leading-zero integers, negative zero, unsafe integers, truncated values, and trailing data. The higher-level parsing and hashing functions use the same strict decoder and therefore throw on malformed input.

Dictionary keys can be arbitrary bytes. Decoded dictionaries retain enough internal information for `encode(decode(data))` to preserve binary keys such as BEP 52 pieces roots. Newly constructed textual dictionary keys are UTF-8 encoded.

## Encode

Convert a parsed torrent object back into a `.torrent` file buffer.

```ts
import fs from 'fs';

import { toTorrentFile } from '@ctrl/torrent-file';

// Minimal example: create a .torrent buffer from fields
const buf = toTorrentFile({
  info: {
    // Required info fields
    'piece length': 16384,
    pieces: new Uint8Array(/* 20-byte SHA1 hashes concatenated */),
    name: 'example.txt',
    length: 12345,
  },
  // Optional fields
  announce: ['udp://tracker.publicbt.com:80/announce'],
  urlList: ['https://example.com/example.txt'],
  private: false,
  created: new Date(),
  createdBy: 'my-app/1.0.0',
  comment: 'Generated by @ctrl/torrent-file',
});

fs.writeFileSync('example.torrent', buf);
```

`toTorrentFile()` serializes supplied metadata; it does not read file contents, calculate piece hashes, build v2 Merkle trees, or insert hybrid padding files.

For v2 `pieceLayers`, dictionary keys are raw 32-byte pieces roots rather than hexadecimal text. When constructing them manually, represent each byte with one JavaScript character:

```ts
const rawDictionaryKey = (bytes: Uint8Array) => String.fromCharCode(...bytes);

const buf = toTorrentFile({
  info: v2Info,
  pieceLayers: {
    [rawDictionaryKey(piecesRoot)]: concatenatedLayerHashes,
  },
});
```

The `pieceLayers` returned by `files()` is a display-friendly hexadecimal representation and is not the raw input shape accepted by `toTorrentFile()`.

## Validation scope

The parser validates canonical bencoding and the v1/v2 file-information structure it consumes, including v2 metadata version, piece size, file-tree shape, file lengths, pieces roots, and hash byte lengths.

It does not recompute file hashes, cryptographically verify piece layers against Merkle roots, or prove that the v1 and v2 sides of a hybrid torrent describe identical content. Applications downloading or trusting content must perform those integrity checks separately.

## Demo

Run a local demo UI to drop a `.torrent` file and view parsed output:

```bash
pnpm install
pnpm demo:watch
```

To build the demo for static hosting:

```bash
pnpm demo:build
```

## Specifications and related projects

- [BEP 3 — BitTorrent protocol v1 and bencoding](https://www.bittorrent.org/beps/bep_0003.html)
- [BEP 12 — multitracker metadata](https://www.bittorrent.org/beps/bep_0012.html)
- [BEP 19 — web seeds](https://www.bittorrent.org/beps/bep_0019.html)
- [BEP 47 — padding files and extended file attributes](https://www.bittorrent.org/beps/bep_0047.html)
- [BEP 52 — BitTorrent protocol v2 and hybrid torrents](https://www.bittorrent.org/beps/bep_0052.html)
- [parse-torrent](https://www.npmjs.com/package/parse-torrent)
- [node-bencode](https://github.com/themasch/node-bencode)

---
_Source: https://npm.io/package/@ctrl/torrent-file · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
