BTPC Documentation
Development documentation: This site documents the current
mainbranch before the first stable release.
BTPC is a high-performance BitTorrent metainfo toolkit with a Rust core, a native
btpc command-line interface, and typed Python bindings. It reads, validates,
creates, inspects, edits, and verifies v1, v2, and hybrid torrents while preserving
raw protocol bytes where identity matters.
Start here
- Follow the CLI guide to create and inspect torrents from a terminal.
- Use the Python API guide for typed creation, parsing, editing, magnet generation, and verification.
- Read the Rust API guide to embed
btpc-core. - Review compatibility and security policies before publishing or integrating BTPC.
BTPC source is hosted on GitHub and is available under the MIT License.
Getting started
Choose the interface that matches your application. All three interfaces use the same Rust protocol core and produce compatible canonical metainfo.
- Install development builds before the first public release.
- Follow the CLI quick start for terminal workflows.
- Follow the Python quick start for typed application code.
- Follow the Rust quick start to embed
btpc-core.
Development documentation: Commands and APIs describe the current
mainbranch and may change before 1.0.
Installation
BTPC is pre-1.0 and is not published to package registries yet. Install it from a source checkout with the locked toolchains.
CLI
cargo build --release -p btpc-cli
./target/release/btpc --version
The binary is target/release/btpc (btpc.exe on Windows).
Python
uv sync --all-groups --locked
uv run maturin develop --release
uv run python -c "import btpc; print(btpc.__version__)"
Python supports CPython 3.11 through 3.14. Wheels currently require the CPython GIL and do not support subinterpreters.
After the first PyPI release, install the library in your Python environment:
python -m pip install btpc
python -c "import btpc; print(btpc.__version__)"
Until publication, build a wheel from the checkout or download a validated release
candidate artifact. Install the wheel file with python -m pip install /path/to/btpc.whl. The wheel contains the Python library and Rust extension. It
does not install the native btpc CLI.
The release workflow builds wheels for each CPython minor version on these platforms:
| Platform | Architectures | Requirement |
|---|---|---|
| Linux | x86-64, AArch64 | glibc 2.28 or later |
| macOS | Intel, Apple Silicon | An OS version compatible with the wheel’s platform tag |
| Windows | x86-64 | Standard CPython for x86-64 |
PyPy, musl Linux, Windows ARM64, and free-threaded wheels are outside this tested
wheel matrix. Use --only-binary=:all: when you want installation to fail if a
compatible wheel is absent. Without this option, pip can try to build the source
distribution. A source build requires a Rust toolchain at or above MSRV 1.85 and
the platform’s compiler/linker tools. For a reproducible development build, use
the pinned toolchain in rust-toolchain.toml.
Long native operations release the GIL. Progress callbacks reacquire it. A free-threaded CPython runtime must enable the GIL to import this extension. Use threads in one main interpreter or separate processes; subinterpreters are not supported.
Rust
Add btpc-core as a path dependency while working from the checkout. Registry
installation instructions will be added when the crate is published.
CLI quick start
Create a tiny payload and build all three torrent modes:
mkdir payload
printf 'hello torrent\n' > payload/hello.txt
btpc create payload -o payload-v1.torrent
btpc create payload --mode v2 --piece-length 16384 -o payload-v2.torrent
btpc create payload --mode hybrid --piece-length 16384 -o payload-hybrid.torrent
Inspect and verify the hybrid result:
btpc inspect payload-hybrid.torrent
btpc validate payload-hybrid.torrent
btpc magnet payload-hybrid.torrent
btpc verify payload-hybrid.torrent payload
Use --json for versioned machine output. Human diagnostics and progress are sent
to stderr. Continue with the complete CLI guide.
Python quick start
from btpc import CreateOptions, TorrentMode, create
result = create(
"payload",
"payload-hybrid.torrent",
options=CreateOptions(
mode=TorrentMode.HYBRID,
piece_length=16_384,
creation_date=0,
threads=1,
),
)
torrent = result.metainfo
assert torrent.verify("payload").is_valid
print(torrent.magnet())
torrent.edit(comment="reviewed").write("payload-reviewed.torrent")
Textual inputs use Python str; parsed protocol bytes and torrent paths remain
lossless bytes. See the Python API guide for editing,
callbacks, cancellation, and errors.
Rust quick start
#![allow(unused)]
fn main() {
extern crate btpc_core;
use btpc_core::create::{CreateMode, CreateOptions, Creator, NoProgress, PieceLength};
let options = CreateOptions::builder()
.mode(CreateMode::Hybrid)
.piece_length(PieceLength::Exact(16_384))
.creation_date(0)
.build()?;
let result = Creator::new("payload").options(options).create(&NoProgress)?;
println!("{} bytes", result.bytes().len());
Ok::<(), btpc_core::Error>(())
}
Protocol logic, traversal, hashing, canonical serialization, and verification live
in btpc-core. Continue with the Rust API guide.
Guides
Task-oriented guides explain how to complete workflows; generated command and API references describe individual options and objects.
- Create torrents
- Inspect and validate metainfo
- Verify payloads
- Edit metadata safely
- Use configuration and presets
Creating torrents
Select v1 for broad legacy compatibility, v2 for per-file Merkle verification, or hybrid when both representations are required.
btpc create payload --mode hybrid --piece-length 16384 -o payload.torrent
Creation traverses paths deterministically and streams payload data with bounded memory. For reproducible output, set a fixed creation date, explicit thread count, and explicit piece length where the protocol permits it. Tracker tiers preserve their order; dictionary keys are always encoded in canonical raw-byte order.
Keep payload files unchanged during creation. BTPC checks file identity, size, and available timestamps before and after hashing. These checks do not provide an atomic filesystem snapshot. An in-place edit can escape detection if the filesystem reports the same metadata.
Use btpc create --json for automation or the typed Python create/
create_bytes functions when embedding BTPC.
Inspecting and validating metainfo
Inspection reports validated metadata without reading payload files:
btpc inspect payload.torrent
btpc inspect payload.torrent --field hash-v1 --format plain
btpc inspect payload.torrent --tree --pretty
Validation checks protocol structure and can require canonical source encoding:
btpc validate payload.torrent --canonical --warnings-as-errors
Human output decodes UTF-8 only when safe. JSON represents uncertain byte strings with an explicit UTF-8 or hexadecimal encoding marker.
Verifying payloads
btpc verify payload.torrent payload
btpc verify payload.torrent payload --fail-fast --extra-files --json
v1 hashes the logical concatenated file stream. v2 verifies each file through BEP 52 Merkle roots. Hybrid torrents verify both domains. Mismatches are deterministic; unsafe paths and symlinks are never followed. Verification opens the selected payload as a filesystem capability, walks descendants relative to opened directory handles, and hashes retained no-follow file handles. Concurrent path replacement is reported as unsafe or missing rather than allowing verification to leave the selected payload root.
A completed mismatch report exits with code 6. I/O, policy, and malformed
metainfo failures retain their category-specific exit codes.
Editing metainfo
Create a validated copy by default:
btpc edit payload.torrent --output edited.torrent --comment "reviewed"
Use --in-place only when atomic replacement is intended. Top-level fields such as
comment and creator preserve info hashes. Changes inside the info dictionary,
including private state, source, and file attributes, change applicable hashes.
Top-level-only edits preserve the exact original info byte slice, even when its
source encoding is noncanonical. Canonical serialization remains an explicit
output choice. Hybrid real-file attribute edits update both v1 and v2 file
representations atomically; padding attributes remain v1-only.
Python uses one three-state parameter per optional field: UNCHANGED preserves,
None removes, and a typed value replaces.
Configuration and presets
BTPC resolves creation settings in a deterministic order: built-in defaults, global
configuration, selected presets, then explicit command arguments. Use
--no-config for a config-free run.
btpc config path
btpc config init
btpc config preset list
btpc config explain create --preset private ./payload
btpc create ./payload --preset private -o payload.torrent
Configuration supports tracker aliases, tracker groups, and inherited ordered
presets. Inspect the resolved plan with config explain before a large creation.
Concepts
Understand the protocol choices that affect compatibility and identity:
BitTorrent v1
v1 stores SHA-1 hashes for fixed-size pieces over the logical concatenation of all
payload files. Multi-file boundaries do not reset piece hashing. Its info
dictionary contains either a single length or an ordered files list.
The v1 info hash is SHA-1 over the exact bencoded source slice of the info
dictionary. Re-serializing semantically equivalent data can therefore change
identity when the source was not canonical.
BitTorrent v2
BEP 52 represents files in a nested file tree and hashes each file with a SHA-256 Merkle tree. Large files may require top-level piece layers. Paths and dictionary keys are raw byte strings, not guaranteed UTF-8 text.
The v2 exact-topic value uses the SHA-256 info hash encoded as a multihash in magnet URIs.
Hybrid torrents
A hybrid torrent carries matching v1 and v2 representations for the same payload. BTPC creates and verifies both hash domains. Alignment padding may appear in the v1 view so file boundaries satisfy the v2 layout.
Hybrid magnets include both btih and btmh exact-topic parameters in deterministic
order.
Piece length
Piece length balances metadata size, hashing granularity, and client behavior. v1 accepts power-of-two piece sizes. v2 and hybrid creation also require BEP 52 rules and file-tree consistency.
Leaving piece length automatic uses BTPC’s deterministic policy for the payload size. Set an explicit value when reproducing a known torrent or comparing tools.
Reproducibility and raw bytes
Torrent identity is defined by bytes. BTPC preserves the exact source info slice
for hashing, treats protocol byte strings as bytes, and decodes text only at explicit
API or presentation boundaries.
Canonical output sorts dictionary keys by unsigned raw-byte order. Reproducible
creation also requires stable traversal, piece length, metadata fields, creation
date, and tool configuration. A fixed creation_date=0 and explicit thread count
are useful for tests and release fixtures.
CLI
title: CLI guide
CLI Guide
The generated command reference documents every visible command and option directly from the Clap command model.
btpc is a thin adapter over btpc-core. Run btpc --help or use the checked-in
reference. Generated completion files and the manual page
are outputs of the same Clap command definition used by the executable.
Commands
| Command | Purpose | Machine output |
|---|---|---|
create INPUT | Scan and hash a file/directory, then atomically write canonical metainfo | --json: btpc.create.v2 |
inspect TORRENT | Display validated metadata without reading payload files | --json: btpc.inspect.v1 |
validate TORRENT | Validate bencode and protocol fields only | --json: btpc.validate.v1 |
verify TORRENT PAYLOAD | Check structure and every applicable hash domain | --json: btpc.verify.v2 |
edit TORRENT | Safely edit typed metainfo fields without reading payloads | --json: btpc.edit.v2 |
magnet TORRENT | Print a deterministic magnet URI | URI only |
config ... | Locate, validate, explain, and safely update TOML configuration | selected subcommands support JSON |
completion ... | Generate, install, or uninstall shell completion source | completion source |
manpage | Generate btpc(1) roff | roff source |
Every metainfo-reading command accepts --max-input-bytes and
--max-owned-bytes; --max-integer-digits independently bounds decimal digit
runs. Defaults are the same conservative limits as btpc-core; violations use
invalid-data exit code 4.
Path-bearing v2 JSON uses btpc.filesystem-path.v2: Unix paths carry exact bytes
as hexadecimal and Windows paths carry exact UTF-16 code units. display is safe
for presentation and escapes control characters. Deprecated output_display and
path_display aliases remain through v2 and are removed in v3; consumers should
read the exact object now. Batch creation emits one v2 create object per input in
input order.
Configuration
BTPC loads the explicit global --config PATH, then BTPC_CONFIG, then the
platform user configuration path ($XDG_CONFIG_HOME/btpc/config.toml or the
equivalent user config directory). It never searches the current directory.
--no-config disables every implicit and environment-selected file. Values merge
in this order: built-in defaults, global [create], each selected preset in CLI
order (including extends parents), then explicit CLI flags.
version = 1
[trackers.primary]
url = "https://tracker.example/announce"
[tracker_groups.release]
trackers = ["primary"]
[presets.base]
mode = "hybrid"
piece_length = 1048576
creation_date = 0
[presets.private]
extends = ["base"]
tracker_groups = ["release"]
private = true
Use config path|init|show|check, config tracker ..., config preset ..., and
config explain create ... to manage and inspect configuration. Secret-bearing
URLs are redacted by default. Configuration writes are atomic and user-private.
Creation
btpc create payload --mode hybrid --piece-length 16384 \
--tracker https://tracker.example/announce \
--web-seed https://seed.example/payload/ \
--creation-date 0 --threads 1 --json \
-o payload.torrent
Important controls:
--mode v1|v2|hybridselects the representation; v1 is the default.--piece-lengthfixes the piece size. Omit it for the deterministic automatic policy. It accepts bytes,KiB/MiB, or2^N; v2/hybrid require BEP 52-compatible lengths of at least 16 KiB.--target-pieceswith--max-piece-lengthselects a bounded core policy.--threads 0selects conservative automatic concurrency,1selects the sequential oracle, andNselects bounded per-operation workers.--forceis required to replace an existing destination. Writes are atomic.--durablealso syncs the destination directory after publication where the platform supports directory syncing.- Repeated
--trackervalues create separate tiers; repeated comma-delimited--tracker-tiervalues preserve a tier. - Config tracker aliases/groups are available through
--tracker-aliasand--tracker-group;--publicexplicitly writes private=false. - Metadata has symmetric clear flags,
--creation-dateacceptsnow|none|UNIX|RFC3339, and--entropyis explicit (none, reproducible hex, or opt-in OSrandom). - New torrents include
created by = btpc/<version>by default. Use--created-by TEXTto override it or--no-created-byto omit it; the legacy--clear-created-byspelling remains an alias. --dry-runscans and validates the plan without hashing or writing. Repeat--print path|info-hash-v1|info-hash-v2|magnetfor stable script lines in the requested order.- Multiple inputs are accepted directly; use
--output-dirfor inferred outputs.--batch jobs.tomlaccepts schema version 1 with[[jobs]],input, optionaloutput, presets, mode, piece length, and threads. All destinations are preflighted before hashing and results remain in input/manifest order. The current conservative scheduler executes one job at a time, so the total hashing worker budget never multiplies across jobs. --include,--exclude, hidden/empty policies, root naming, symlink policy, and special-file policy are deterministic on supported operating systems. Include/exclude globs are UTF-8 text filters and fail if they encounter a non-UTF-8 payload path rather than matching a lossy replacement string.
Equivalent batch input uses a versioned manifest:
version = 1
[[jobs]]
input = "payload-a"
output = "torrents/payload-a.torrent"
presets = ["private"]
[[jobs]]
input = "payload-b"
mode = "v1"
threads = 1
Inspection and Bytes
Human output decodes UTF-8 where safe. JSON never guesses: byte strings are
objects with encoding set to utf-8 or hex and a value. inspect and
validate never infer or read a payload path.
Default summaries remain compact. --pretty enables aligned, symbol-prefixed
human output, while --quiet suppresses human summaries. Human inspect uses a
stable Torrent info: summary with IEC sizes, applicable hashes, a safe magnet,
and grouped tracker/web-seed URLs. --pretty adds exact-byte and validation
details; -v adds optional metadata, warnings, bounded unknown fields, and a real
nested file trie for multi-file torrents. --tree requests the trie directly and
honors --offset, --limit, and path encoding. Machine formats remain unchanged,
unstyled, and do not receive progress output.
btpc inspect payload.torrent --json
btpc inspect payload.torrent --field hash-v1 --format plain
btpc inspect payload.torrent --files --offset 0 --limit 100 --format tsv
btpc inspect payload.torrent --tree --path-encoding escaped --format json-pretty
btpc validate payload.torrent --json
btpc validate payload.torrent --canonical --warnings-as-errors
inspect --field is repeatable and preserves selector order. Available fields
cover mode/name/size/pieces/files, both info hashes, private state, trackers,
web seeds, nodes, comment/creator/date/source, canonicality, warnings, and unknown
top-level fields. One selected scalar with --format plain writes only its value.
Selected or paginated JSON uses schema btpc.inspect.selection.v1; unselected
--json remains the compatible btpc.inspect.v1 schema. File paths support
lossless escaped and hex encodings when UTF-8 is unavailable.
Verification
btpc verify payload.torrent payload
btpc verify payload.torrent payload --fail-fast --extra-files --json
A completed report with mismatches exits 6; operational errors use their normal
category. --fail-fast stops at the first deterministic mismatch. --extra-files
reports regular files not represented in metainfo. Unsafe symlink paths are
reported and never followed.
Magnets
btpc magnet payload.torrent
btpc magnet payload.torrent --no-display-name --no-trackers --no-web-seeds
v1 magnets use btih, v2 magnets use multihash btmh, and hybrid magnets include
both exact-topic parameters in deterministic order.
Editing
btpc edit INPUT writes <stem>.edited.torrent by default. Use --output for a
different copy or explicit --in-place for atomic replacement. --dry-run
validates without writing; --diff or -v includes old/new hashes. Typed set and
clear flags cover trackers (including config aliases/groups), web seeds, nodes,
comment, creator, creation date, private state, source, and file attributes.
Top-level-only edits retain info hashes, while info-dictionary edits report the
applicable changed hash domains.
Exit Codes and Streams
| Code | Meaning |
|---|---|
0 | Success / valid payload |
1 | Internal error |
2 | Usage or argument parsing error |
3 | I/O or path error |
4 | Invalid bencode, metainfo, or resource limit |
5 | Unsupported policy or filesystem object |
6 | Payload verification mismatch |
130 | Cancellation or interrupt |
Machine output is written to stdout. Human diagnostics and progress are written
to stderr. Styling and the single create/verify progress display are disabled for
non-terminals, quiet mode, machine output, and when NO_COLOR is set. Explicit
--color always forces diagnostic styling. Errors retain stable categories and
exit codes while adding available path, field, byte-offset, and remediation
context; URLs are redacted before diagnostic rendering.
Versioned JSON schemas retain their existing fields. New incompatible schema
shapes require a new schema identifier rather than silently changing an existing
one. The legacy hidden completions SHELL command writes the same completion
bytes as completion generate SHELL; its warning is stderr-only and suppressed by
--quiet.
Completion and Manual Installation
btpc completion generate bash
btpc completion install bash
btpc completion install --dry-run fish
btpc completion uninstall zsh
btpc manpage > ~/.local/share/man/man1/btpc.1
When the shell argument is omitted, BTPC uses an unambiguous SHELL or
PowerShell environment hint; otherwise it asks for an explicit shell. Installation
uses standard per-user Bash, Zsh, Fish, PowerShell, and Elvish completion
directories, creates only the required parent directories, writes atomically, and
never edits shell startup files. Existing unrelated files require --force, and
uninstall removes only BTPC-marked content. The hidden completions SHELL alias is
retained for compatibility for one minor release. Pre-generated artifacts live in
the shell completion guide and
reference/btpc.1.
CLI configuration
The user-scoped TOML configuration file is versioned and can define global output settings, tracker aliases, tracker groups, and creation presets.
btpc config path
btpc config show
btpc config check
btpc config explain create --preset private ./payload
Use btpc config init to create a starter file and --no-config to ignore implicit
and environment-selected configuration.
Shell completion and manual pages
Generate or install completions without editing shell startup files:
btpc completion generate bash
btpc completion install zsh --dry-run
btpc completion uninstall fish
BTPC supports Bash, Zsh, Fish, PowerShell, and Elvish user completion directories.
Generate the manual page with btpc manpage and redirect it into a local manpath.
btpc
BitTorrent metainfo toolkit
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc [OPTIONS] <COMMAND>
Subcommands
| Command | Description |
|---|---|
btpc create | Create canonical v1, v2, or hybrid metainfo from a file or directory |
| btpc inspect | Inspect validated metainfo without reading payload files |
| btpc validate | Validate metainfo structure without reading payload files |
| btpc verify | Verify payload files against metainfo hashes |
| btpc edit | Edit metainfo metadata without reading payload files |
| btpc magnet | Print a deterministic magnet URI |
| btpc config | Locate, inspect, validate, and update configuration |
| btpc completion | Generate, install, or uninstall shell completions |
| btpc manpage | Generate the btpc(1) manual page on stdout |
Deprecated aliases
btpc completions— Deprecated alias forbtpc completion generate
Options
| Option | Description |
|---|---|
-h, --help | Print help |
-V, --version | Print version |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc --help
btpc create
Create canonical v1, v2, or hybrid metainfo from a file or directory
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc create [OPTIONS] [INPUTS]...
Arguments
| Argument | Description |
|---|---|
<INPUTS> | Payload files or directories |
Options
| Option | Description |
|---|---|
--batch, <BATCH> | Versioned TOML batch manifest |
--mode, <MODE> | Torrent protocol representation Values: v1, v2, hybrid. |
-o, --output, <OUTPUT> | Destination .torrent path (defaults beside the payload) |
--output-dir, <OUTPUT_DIR> | Write batch outputs beneath this directory |
--jobs, <JOBS> | Maximum concurrent batch creation jobs Default: 1. |
--fail-fast | Stop scheduling batch jobs after the first failure Default: false. |
-f, --force | Replace an existing destination Default: false. |
--durable | Sync the destination directory after atomic publication where supported Default: false. |
--preset, <PRESETS> | Apply a named creation preset; may be repeated |
--piece-length, <PIECE_LENGTH> | Explicit piece length in bytes |
--target-pieces, <TARGET_PIECES> | Target approximate number of pieces for automatic selection |
--max-piece-length, <MAX_PIECE_LENGTH> | Cap target-based automatic piece length |
-a, --tracker, <TRACKERS> | Add a tracker as its own tier; may be repeated |
--clear-trackers | Clear configured and preset trackers before CLI additions Default: false. |
--tracker-tier, <TRACKER_TIER> | Add one comma-separated tracker tier; may be repeated |
--tracker-alias, <TRACKER_ALIASES> | Add a configured tracker alias; may be repeated |
--tracker-group, <TRACKER_GROUPS> | Add a configured tracker group; may be repeated |
--web-seed, <WEB_SEEDS> | Add a web seed URL; may be repeated |
--clear-web-seeds | Clear configured and preset web seeds before CLI additions Default: false. |
--node, <NODES> | Add a DHT node as HOST:PORT; may be repeated |
--private | Set the private flag Default: false. |
--public | Set the private flag to false Default: false. |
--source, <SOURCE> | Set the source field |
--clear-source | Remove configured or preset source metadata Default: false. |
--comment, <COMMENT> | Set the top-level comment |
--clear-comment | Remove configured or preset comment metadata Default: false. |
--created-by, <CREATED_BY> | Set the creator string |
--no-created-by, --clear-created-by | Omit the creator string instead of using the versioned default Default: false. |
--creation-date, <CREATION_DATE> | Include an explicit Unix creation timestamp |
--entropy, <ENTROPY> | Set deterministic, random, or omitted entropy policy |
--name, <NAME> | Override the torrent root name |
--exclude-hidden | Exclude dot-prefixed files and directories Default: false. |
--symlinks, <SYMLINKS> | Symbolic-link policy Values: reject, skip, follow. |
--special-files, <SPECIAL_FILES> | Special-file policy Values: reject, skip. |
--exclude-empty-files | Exclude zero-length files Default: false. |
--reject-empty-directories | Reject empty directories instead of ignoring them Default: false. |
--include, <INCLUDES> | Include only paths matching this glob; may be repeated |
--clear-includes | Clear configured and preset include patterns before CLI additions Default: false. |
--exclude, <EXCLUDES> | Exclude paths matching this glob; may be repeated |
--clear-excludes | Clear configured and preset exclude patterns before CLI additions Default: false. |
--threads, <THREADS> | v1 hashing threads; 0 selects a conservative automatic count, 1 is sequential |
--dry-run | Plan creation without hashing or writing metainfo Default: false. |
--print, <PRINT> | Print selected result fields; may be repeated Values: path, info-hash-v1, info-hash-v2, magnet. |
--json | Emit a versioned JSON result to stdout Default: false. |
--pretty | Use the expanded human completion renderer Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc create --help
btpc inspect
Inspect validated metainfo without reading payload files
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc inspect [OPTIONS] <INPUT>
Arguments
| Argument | Description |
|---|---|
<INPUT> | Metainfo file to read |
Options
| Option | Description |
|---|---|
--json | Emit versioned JSON to stdout Default: false. |
--field, <FIELDS> | Select a field; may be repeated Values: mode, name, total-size, piece-length, piece-count, file-count, hash-v1, hash-v2, private, trackers, web-seeds, nodes, comment, creator, creation-date, source, canonicality, warnings, files, unknown-fields. |
--files | Include the flat file listing Default: false. |
--tree | Render files as a deterministic tree Default: false. |
--path-encoding, <PATH_ENCODING> | Encode raw torrent paths Values: utf8, escaped, hex. Default: utf8. |
--offset, <OFFSET> | Skip this many file rows Default: 0. |
--limit, <LIMIT> | Limit returned file rows |
--format, <FORMAT> | Select output representation Values: human, plain, json, json-pretty, tsv. |
--pretty | Use the expanded human renderer Default: false. |
--max-input-bytes, <MAX_INPUT_BYTES> | Maximum metainfo bytes accepted while loading |
--max-owned-bytes, <MAX_OWNED_BYTES> | Maximum estimated owned allocation while loading |
--max-integer-digits, <MAX_INTEGER_DIGITS> | Maximum decimal digits accepted in one bencode integer |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc inspect --help
btpc validate
Validate metainfo structure without reading payload files
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc validate [OPTIONS] <INPUT>
Arguments
| Argument | Description |
|---|---|
<INPUT> | Metainfo file to validate |
Options
| Option | Description |
|---|---|
--json | Emit versioned JSON to stdout Default: false. |
--format, <FORMAT> | Select output representation Values: human, json, json-pretty. |
--canonical | Require canonical bencode Default: false. |
--warnings-as-errors | Return the warning exit code when validation warnings exist Default: false. |
--pretty | Use the expanded human renderer Default: false. |
--max-input-bytes, <MAX_INPUT_BYTES> | Maximum metainfo bytes accepted while loading |
--max-owned-bytes, <MAX_OWNED_BYTES> | Maximum estimated owned allocation while loading |
--max-integer-digits, <MAX_INTEGER_DIGITS> | Maximum decimal digits accepted in one bencode integer |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc validate --help
btpc verify
Verify payload files against metainfo hashes
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc verify [OPTIONS] <TORRENT> <PAYLOAD>
Arguments
| Argument | Description |
|---|---|
<TORRENT> | Metainfo file to read |
<PAYLOAD> | Payload file or directory to verify |
Options
| Option | Description |
|---|---|
--fail-fast | Stop after the first deterministic mismatch Default: false. |
--extra-files | Report regular files absent from metainfo Default: false. |
--json | Emit versioned JSON to stdout Default: false. |
--pretty | Use the expanded human renderer Default: false. |
--max-input-bytes, <MAX_INPUT_BYTES> | Maximum metainfo bytes accepted while loading |
--max-owned-bytes, <MAX_OWNED_BYTES> | Maximum estimated owned allocation while loading |
--max-integer-digits, <MAX_INTEGER_DIGITS> | Maximum decimal digits accepted in one bencode integer |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc verify --help
btpc edit
Edit metainfo metadata without reading payload files
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc edit [OPTIONS] <INPUT>
Arguments
| Argument | Description |
|---|---|
<INPUT> | Metainfo file to edit |
Options
| Option | Description |
|---|---|
-o, --output, <OUTPUT> | Write the edited metainfo to this path |
--in-place | Replace the input file atomically Default: false. |
-f, --force | Replace an existing output file Default: false. |
--durable | Sync the destination directory after publication where supported Default: false. |
--dry-run | Validate and report changes without writing output Default: false. |
--diff | Print a deterministic field-level change summary Default: false. |
--json | Emit a versioned JSON result to stdout Default: false. |
-a, --tracker, <TRACKERS> | Replace trackers with this tracker tier; may be repeated |
--tracker-alias, <TRACKER_ALIASES> | Add a configured tracker alias; may be repeated |
--tracker-group, <TRACKER_GROUPS> | Add a configured tracker group; may be repeated |
--clear-trackers | Remove all trackers Default: false. |
--web-seed, <WEB_SEEDS> | Replace web seeds with this URL; may be repeated |
--clear-web-seeds | Remove all web seeds Default: false. |
--node, <NODES> | Replace DHT nodes with HOST:PORT; may be repeated |
--clear-nodes | Remove all DHT nodes Default: false. |
--comment, <COMMENT> | Set the top-level comment |
--clear-comment | Remove the top-level comment Default: false. |
--created-by, <CREATED_BY> | Set the creator string |
--clear-created-by | Remove the creator string Default: false. |
--creation-date, <CREATION_DATE> | Set the Unix creation timestamp |
--clear-creation-date | Remove the creation timestamp Default: false. |
--private | Set the private flag Default: false. |
--public | Set the private flag to false Default: false. |
--clear-private | Remove the private field Default: false. |
--source, <SOURCE> | Set the source field |
--clear-source | Remove the source field Default: false. |
--file-attributes, <FILE_ATTRIBUTES> | Set file attributes as PATH=ATTRS; may be repeated |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc edit --help
btpc magnet
Print a deterministic magnet URI
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc magnet [OPTIONS] <INPUT>
Arguments
| Argument | Description |
|---|---|
<INPUT> | Metainfo file to read |
Options
| Option | Description |
|---|---|
--no-display-name | Omit the display name parameter Default: false. |
--no-trackers | Omit tracker parameters Default: false. |
--no-web-seeds | Omit web seed parameters Default: false. |
--max-input-bytes, <MAX_INPUT_BYTES> | Maximum metainfo bytes accepted while loading |
--max-owned-bytes, <MAX_OWNED_BYTES> | Maximum estimated owned allocation while loading |
--max-integer-digits, <MAX_INTEGER_DIGITS> | Maximum decimal digits accepted in one bencode integer |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc magnet --help
btpc config
Locate, inspect, validate, and update configuration
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config [OPTIONS] <COMMAND>
Subcommands
| Command | Description |
|---|---|
btpc config path | Print the selected configuration path |
| btpc config init | Create a minimal configuration file |
| btpc config show | Print configuration with secrets redacted by default |
| btpc config check | Validate schema, references, cycles, conflicts, and permissions |
| btpc config explain | Explain resolved command values without executing the command |
| btpc config tracker | Manage named tracker aliases |
| btpc config preset | Manage named creation presets |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config --help
btpc config path
Print the selected configuration path
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config path [OPTIONS]
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config path --help
btpc config init
Create a minimal configuration file
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config init [OPTIONS]
Options
| Option | Description |
|---|---|
--force | Replace an existing file Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config init --help
btpc config show
Print configuration with secrets redacted by default
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config show [OPTIONS]
Options
| Option | Description |
|---|---|
--resolved | Validate and print the parsed deterministic representation Default: false. |
--show-secrets | Reveal configured secrets Default: false. |
--json | Emit JSON instead of TOML Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config show --help
btpc config check
Validate schema, references, cycles, conflicts, and permissions
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config check [OPTIONS]
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config check --help
btpc config explain
Explain resolved command values without executing the command
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config explain [OPTIONS] <COMMAND>
Subcommands
| Command | Description |
|---|---|
btpc config explain create | Explain effective create values and provenance |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config explain --help
btpc config explain create
Explain effective create values and provenance
Parent: btpc config explain
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config explain create [OPTIONS] [INPUTS]...
Arguments
| Argument | Description |
|---|---|
<INPUTS> | Payload files or directories |
Options
| Option | Description |
|---|---|
--batch, <BATCH> | Versioned TOML batch manifest |
--mode, <MODE> | Torrent protocol representation Values: v1, v2, hybrid. |
-o, --output, <OUTPUT> | Destination .torrent path (defaults beside the payload) |
--output-dir, <OUTPUT_DIR> | Write batch outputs beneath this directory |
--jobs, <JOBS> | Maximum concurrent batch creation jobs Default: 1. |
--fail-fast | Stop scheduling batch jobs after the first failure Default: false. |
-f, --force | Replace an existing destination Default: false. |
--durable | Sync the destination directory after atomic publication where supported Default: false. |
--preset, <PRESETS> | Apply a named creation preset; may be repeated |
--piece-length, <PIECE_LENGTH> | Explicit piece length in bytes |
--target-pieces, <TARGET_PIECES> | Target approximate number of pieces for automatic selection |
--max-piece-length, <MAX_PIECE_LENGTH> | Cap target-based automatic piece length |
-a, --tracker, <TRACKERS> | Add a tracker as its own tier; may be repeated |
--clear-trackers | Clear configured and preset trackers before CLI additions Default: false. |
--tracker-tier, <TRACKER_TIER> | Add one comma-separated tracker tier; may be repeated |
--tracker-alias, <TRACKER_ALIASES> | Add a configured tracker alias; may be repeated |
--tracker-group, <TRACKER_GROUPS> | Add a configured tracker group; may be repeated |
--web-seed, <WEB_SEEDS> | Add a web seed URL; may be repeated |
--clear-web-seeds | Clear configured and preset web seeds before CLI additions Default: false. |
--node, <NODES> | Add a DHT node as HOST:PORT; may be repeated |
--private | Set the private flag Default: false. |
--public | Set the private flag to false Default: false. |
--source, <SOURCE> | Set the source field |
--clear-source | Remove configured or preset source metadata Default: false. |
--comment, <COMMENT> | Set the top-level comment |
--clear-comment | Remove configured or preset comment metadata Default: false. |
--created-by, <CREATED_BY> | Set the creator string |
--no-created-by, --clear-created-by | Omit the creator string instead of using the versioned default Default: false. |
--creation-date, <CREATION_DATE> | Include an explicit Unix creation timestamp |
--entropy, <ENTROPY> | Set deterministic, random, or omitted entropy policy |
--name, <NAME> | Override the torrent root name |
--exclude-hidden | Exclude dot-prefixed files and directories Default: false. |
--symlinks, <SYMLINKS> | Symbolic-link policy Values: reject, skip, follow. |
--special-files, <SPECIAL_FILES> | Special-file policy Values: reject, skip. |
--exclude-empty-files | Exclude zero-length files Default: false. |
--reject-empty-directories | Reject empty directories instead of ignoring them Default: false. |
--include, <INCLUDES> | Include only paths matching this glob; may be repeated |
--clear-includes | Clear configured and preset include patterns before CLI additions Default: false. |
--exclude, <EXCLUDES> | Exclude paths matching this glob; may be repeated |
--clear-excludes | Clear configured and preset exclude patterns before CLI additions Default: false. |
--threads, <THREADS> | v1 hashing threads; 0 selects a conservative automatic count, 1 is sequential |
--dry-run | Plan creation without hashing or writing metainfo Default: false. |
--print, <PRINT> | Print selected result fields; may be repeated Values: path, info-hash-v1, info-hash-v2, magnet. |
--json | Emit a versioned JSON result to stdout Default: false. |
--pretty | Use the expanded human completion renderer Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config explain create --help
btpc config tracker
Manage named tracker aliases
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config tracker [OPTIONS] <COMMAND>
Subcommands
| Command | Description |
|---|---|
btpc config tracker list | List tracker aliases |
| btpc config tracker add | Add or replace a tracker alias |
| btpc config tracker remove | Remove a tracker alias |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config tracker --help
btpc config tracker list
List tracker aliases
Parent: btpc config tracker
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config tracker list [OPTIONS]
Options
| Option | Description |
|---|---|
--show-secrets | Reveal tracker URLs Default: false. |
--json | Emit JSON Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config tracker list --help
btpc config tracker add
Add or replace a tracker alias
Parent: btpc config tracker
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config tracker add [OPTIONS] <NAME> <URL>
Arguments
| Argument | Description |
|---|---|
<NAME> | Alias name |
<URL> | Tracker announce URL |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config tracker add --help
btpc config tracker remove
Remove a tracker alias
Parent: btpc config tracker
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config tracker remove [OPTIONS] <NAME>
Arguments
| Argument | Description |
|---|---|
<NAME> | Alias name |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config tracker remove --help
btpc config preset
Manage named creation presets
Parent: btpc config
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config preset [OPTIONS] <COMMAND>
Subcommands
| Command | Description |
|---|---|
btpc config preset list | List preset names |
| btpc config preset show | Show one preset |
| btpc config preset save | Save or replace a preset |
| btpc config preset remove | Remove a preset |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config preset --help
btpc config preset list
List preset names
Parent: btpc config preset
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config preset list [OPTIONS]
Options
| Option | Description |
|---|---|
--json | Emit JSON Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config preset list --help
btpc config preset show
Show one preset
Parent: btpc config preset
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config preset show [OPTIONS] <NAME>
Arguments
| Argument | Description |
|---|---|
<NAME> | Preset name |
Options
| Option | Description |
|---|---|
--show-secrets | Reveal configured URLs Default: false. |
--json | Emit JSON Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config preset show --help
btpc config preset save
Save or replace a preset
Parent: btpc config preset
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config preset save [OPTIONS] <NAME>
Arguments
| Argument | Description |
|---|---|
<NAME> | Preset name |
Options
| Option | Description |
|---|---|
--extends, <EXTENDS> | Parent preset; may be repeated |
--mode, <MODE> | Torrent protocol representation Values: v1, v2, hybrid. |
--piece-length, <PIECE_LENGTH> | Explicit piece length in bytes |
--private | Set the private flag Default: false. |
--source, <SOURCE> | Set the source field |
--comment, <COMMENT> | Set the top-level comment |
--created-by, <CREATED_BY> | Set the creator string |
--creation-date, <CREATION_DATE> | Set the Unix creation timestamp |
--name, <NAME_OVERRIDE> | Override the torrent root name |
--exclude-hidden | Exclude dot-prefixed files and directories Default: false. |
--symlinks, <SYMLINKS> | Symbolic-link policy Values: reject, skip, follow. |
--special-files, <SPECIAL_FILES> | Special-file policy Values: reject, skip. |
--exclude-empty-files | Exclude zero-length files Default: false. |
--reject-empty-directories | Reject empty directories instead of ignoring them Default: false. |
--tracker, <TRACKERS> | Add a tracker as its own tier; may be repeated |
--tracker-alias, <TRACKER_ALIASES> | Add a configured tracker alias; may be repeated |
--tracker-group, <TRACKER_GROUPS> | Add a configured tracker group; may be repeated |
--web-seed, <WEB_SEEDS> | Add a web seed URL; may be repeated |
--include, <INCLUDES> | Include only paths matching this glob; may be repeated |
--exclude, <EXCLUDES> | Exclude paths matching this glob; may be repeated |
--threads, <THREADS> | v1 hashing threads; 0 selects automatic, 1 is sequential |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config preset save --help
btpc config preset remove
Remove a preset
Parent: btpc config preset
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc config preset remove [OPTIONS] <NAME>
Arguments
| Argument | Description |
|---|---|
<NAME> | Preset name |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc config preset remove --help
btpc completion
Generate, install, or uninstall shell completions
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc completion [OPTIONS] <COMMAND>
Subcommands
| Command | Description |
|---|---|
btpc completion generate | Generate a shell completion script on stdout |
| btpc completion install | Install shell completions in the standard per-user directory |
| btpc completion uninstall | Remove BTPC-generated shell completions |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc completion --help
btpc completion generate
Generate a shell completion script on stdout
Parent: btpc completion
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc completion generate [OPTIONS] <SHELL>
Arguments
| Argument | Description |
|---|---|
<SHELL> | Shell whose completion syntax should be generated Values: bash, elvish, fish, powershell, zsh. |
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc completion generate --help
btpc completion install
Install shell completions in the standard per-user directory
Parent: btpc completion
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc completion install [OPTIONS] [SHELL]
Arguments
| Argument | Description |
|---|---|
<SHELL> | Shell to install; detected from environment hints when omitted Values: bash, elvish, fish, powershell, zsh. |
Options
| Option | Description |
|---|---|
--dry-run | Print the target and generated content without changing files Default: false. |
--force | Replace an unrelated existing completion file Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc completion install --help
btpc completion uninstall
Remove BTPC-generated shell completions
Parent: btpc completion
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc completion uninstall [OPTIONS] [SHELL]
Arguments
| Argument | Description |
|---|---|
<SHELL> | Shell to install; detected from environment hints when omitted Values: bash, elvish, fish, powershell, zsh. |
Options
| Option | Description |
|---|---|
--dry-run | Print the target and generated content without changing files Default: false. |
--force | Replace an unrelated existing completion file Default: false. |
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc completion uninstall --help
btpc manpage
Generate the btpc(1) manual page on stdout
Parent: btpc
See Configuration and global flags and Exit codes and streams.
Synopsis
Usage: btpc manpage [OPTIONS]
Options
| Option | Description |
|---|---|
-h, --help | Print help |
Global options
| Option | Description |
|---|---|
--config, <PATH> | Use this configuration file when configuration loading is enabled |
--no-config | Disable implicit and environment-selected configuration Default: false. |
--color, <COLOR> | Control colored terminal output Values: auto, always, never. |
-v, --verbose | Increase diagnostic verbosity; may be repeated Default: 0. |
-q, --quiet | Suppress human summaries, warnings, and progress Default: false. |
Example
$ btpc manpage --help
Python API Guide
Public Modules
BTPC supports concise root imports and canonical domain imports with identical objects. The stable public modules are:
btpc.errorsfor the exception hierarchy.btpc.typesfor protocol modes, hashes, parse options, and byte/path values.btpc.metainfofor parsed metainfo, files, and validation reports.btpc.creationfor creation options, results, cancellation, and functions.btpc.verificationfor mismatch values, reports, and verification.
For example, btpc.Metainfo is btpc.metainfo.Metainfo. The compiled
btpc._native module and btpc._conversion helpers are private implementation
details and are not exported through btpc.__all__.
The public package is btpc; btpc._native is private. The package is typed,
ships py.typed, supports CPython 3.11–3.14, and delegates parsing, hashing,
traversal, Merkle construction, creation, and verification to Rust.
Workflow Reference
- Create torrent bytes with
create_bytesor publish atomically withcreate, configured byCreateOptions. - Parse with
Metainfo.from_bytesorMetainfo.read, then inspectTorrentFilevalues. - Edit with
Metainfo.edit, serialize withMetainfo.to_bytes, or build a magnet URI withMetainfo.magnet. - Verify through
Metainfo.verifyorverifyand inspect the resultingPayloadVerificationReport.
Public API Index
Root imports are aliases of the canonical objects below. Each object is documented once on its defining-module page.
- Creation:
CancellationToken,CreateMetrics,CreateOptions,CreateResult,create, andcreate_bytes. - Metainfo:
BencodeDictionary,BencodeList,BencodeValue,Metainfo,TorrentFile,UnknownField, andValidationReport. - Verification:
MismatchKind,PayloadMismatch,PayloadVerificationReport, andverify. - Types:
UNCHANGED,HashValue,ParseOptions,TorrentBytes,TorrentMode, andTorrentPath. - Errors:
BencodeError,BtpcError,CancelledError,MetainfoError,PathError,ResourceLimitError,UnsupportedError, andVerificationError.
Package version
btpc.__version__ is the installed package version string. The module names
btpc.creation, btpc.metainfo, btpc.verification, btpc.types, and
btpc.errors are also available from the package root.
Create, Read, Verify
from pathlib import Path
from btpc import CreateOptions, TorrentMode, create
payload = Path("payload")
result = create(
payload,
"payload.torrent",
options=CreateOptions(
mode=TorrentMode.HYBRID,
piece_length=16_384,
threads=1,
creation_date=0,
trackers=(("https://tracker.example/announce",),),
nodes=(("router.example", 6881),),
),
)
torrent = result.metainfo
assert torrent.mode is TorrentMode.HYBRID
assert torrent.verify(payload).is_valid
print(torrent.info_hash_v1, torrent.info_hash_v2)
print(torrent.magnet())
create_bytes() returns the same CreateResult without writing a destination.
result.metainfo creates and caches a validated object on first access without
copying the generated bytes through Python or reading the payload again.
CreateOptions accepts sequences and copies tracker tiers, web seeds, and nodes
into immutable tuples at construction. Invalid sequence shapes and element types
raise TypeError at construction.
Creation includes created by = btpc/<version> by default. Set
CreateOptions(created_by="my-tool") to override it or
CreateOptions(omit_created_by=True) to omit the field. Creator metadata is
top-level and does not change applicable info hashes.
create() writes atomically and requires overwrite=True to replace a file.
Pass durable=True to also sync the destination directory after publication
where the platform supports directory syncing.
Creation options match core defaults: v1, automatic piece length, conservative
automatic threads, no timestamp, and no optional metadata.
Byte-Safe Inspection
Protocol identities are bytes:
from btpc import Metainfo
torrent = Metainfo.read("payload.torrent")
print(torrent.name) # bytes
print(torrent.name_text) # str | None
for file in torrent.files:
print(file.path, file.path_text, file.is_padding)
from_bytes() accepts bytes, bytearray, and contiguous buffer objects. Exact
source bytes remain in original_bytes; to_bytes() returns canonical bencode.
For parseable noncanonical input those byte sequences, and therefore source versus
canonical info hashes, can differ by design.
Metainfo and CreateResult are immutable, non-subclassable facades over owned
native Rust objects. Parsing does not eagerly create Python copies of source or
canonical bytes, file entries, trackers, web seeds, unknown fields, or validation
details. Those values are converted once on first property access and then cached
with stable property identity. Canonical serialization is also lazy in the Rust
core. These native-backed objects are intentionally not picklable; serialize a
Metainfo with to_bytes(canonical=False) or to_bytes(), and serialize a
CreateResult through its bytes property.
The private extension returns typed immutable _NativeMetainfo,
_NativeTorrentFile, _NativeValidationReport, _NativeCreateResult,
_NativePayloadMismatch, and _NativeVerificationReport objects rather than
snapshot dictionaries. Public applications should continue using the btpc
facades; the underscore-prefixed native classes remain implementation details.
Hybrid files includes validated v1 alignment entries. These have
TorrentFile.is_padding == True, a reserved .pad path, and no v2 pieces root.
Both loading methods use conservative native limits by default. Pass
ParseOptions(max_total_input=..., max_owned_allocation=..., max_integer_digits=...) to set explicit limits; violations raise
ResourceLimitError before an oversized input, digit run, or owned snapshot is
accepted.
Editing
Metainfo.edit() returns a newly validated object. Top-level-only edits such as
trackers or comments preserve the exact original info bytes and hashes, including
noncanonical source encoding. Edits inside info, including private, source,
or file attributes, canonicalize the updated dictionary and recompute applicable
hashes. to_bytes() remains the explicit fully canonical output choice.
Save edits with edited.write("edited.torrent"). This publishes atomically and
preserves the edited object’s exact encoding and info hashes by default. Existing
files require overwrite=True; durable=True requests parent-directory syncing.
write(canonical=True) explicitly normalizes bencoding. For noncanonical source
info this can change hashes. The compatibility default of to_bytes() remains
canonical; use to_bytes(canonical=False) for exact bytes without file I/O.
from btpc import UNCHANGED
edited = torrent.edit(comment="release")
assert edited.info_hash_v1 == torrent.info_hash_v1
unchanged = edited.edit(comment=UNCHANGED)
removed = edited.edit(comment=None)
Optional edit fields use a three-state contract: UNCHANGED preserves the current
value, None removes it, and a typed value sets it. The former paired set_*
keywords were intentionally removed before 1.0 rather than silently reinterpreted.
Metainfo equality compares exact original source bytes in native code, including
noncanonical encodings and top-level metadata. Metainfo is intentionally
unhashable before 1.0 so equality never requires copying the full source into a
Python bytes object or committing to a process-hash implementation.
Progress and Cancellation
from btpc import CancellationToken, CreateOptions, create_bytes
cancel = CancellationToken()
def progress(bytes_hashed: int, total_bytes: int, pieces_hashed: int) -> None:
if bytes_hashed > total_bytes // 2:
cancel.cancel()
create_bytes(
"payload",
options=CreateOptions(threads=1),
progress=progress,
cancellation=cancel,
)
Long-running native work releases the GIL. Callbacks reacquire it safely and an exception from a callback cancels the core operation before propagating.
The extension itself currently requires the GIL, including on free-threaded CPython builds. Subinterpreters are rejected by PyO3 and are not supported; see the compatibility guide.
Errors
All public failures derive from BtpcError and may expose offset, field, or
path:
BencodeError: syntax or canonical bencode failure.MetainfoError: invalid protocol field or invariant.PathError: filesystem/path failure.ResourceLimitError: configured parse or ownership budget exceeded.VerificationError: verification-category failure.UnsupportedError: unsupported policy or object.CancelledError: cooperative cancellation.
Native filesystem errors attach a platform-native pathlib.Path directly. Unix
byte paths use Python’s filesystem surrogate handling, while Windows paths retain
their original UTF-16 units without an intermediate UTF-8 conversion.
A normal verification mismatch is returned in PayloadVerificationReport rather
than raised. Inspect mismatches, each containing a stable MismatchKind, path,
and optional piece index.
Optional metadata inspection
Validated inspection exposes trackers, web_seeds, nodes, source,
comment, created_by, and creation_date from the same native optional-metadata
model used by Rust and the CLI. Hosts and textual metadata remain bytes; node
ports and creation dates are integers.
The comment_text, source_text, and created_by_text properties return UTF-8
text or None when absent or undecodable. trackers_text, web_seeds_text, and
nodes_text return decoded immutable collections, or None if any string is not
UTF-8. Empty collections remain empty tuples. Raw properties retain all bytes.
Check the raw property to distinguish absence from a decoding failure before
using a nullable text view in an edit, because None removes a field.
torrent.validation exposes the cached report from construction. validate()
returns the same report for compatibility; it does not repeat validation. Invalid
metainfo raises during construction, so this report’s is_valid is always true.
Unknown extension values
Metainfo.unknown_fields returns immutable UnknownField objects rather than
keys alone. Each object includes its raw byte key, a recursive value made from
Python integers and bytes plus BencodeList/BencodeDictionary, the exact encoded
source bytes for the key/value entry, and its half-open span in original_bytes.
Arbitrary-size integers and non-UTF-8 dictionary keys remain lossless.
Pass the same recursive values to Metainfo.edit(raw_top_level=...) to preserve
or replace extension dictionaries and lists. Reserved protocol keys remain
rejected, and BencodeDictionary rejects duplicate keys while sorting unique raw
keys canonically.
BencodeList supports iteration, indexing, slicing, membership, count, and
index. Slicing returns another immutable BencodeList. BencodeDictionary
supports key iteration, lookup, membership, get, keys(), values(), and
dict(value) conversion. Its existing items attribute remains an immutable
tuple, not a method.
Python examples
The quick start covers creation and verification.
Use Metainfo.from_bytes for contiguous buffers and Metainfo.read for direct path
I/O. Parsed raw values remain bytes, while optional text views return None when
UTF-8 decoding is not lossless.
from btpc import UNCHANGED, Metainfo
torrent = Metainfo.read("payload.torrent")
reviewed = torrent.edit(comment="reviewed", source=UNCHANGED)
reviewed.write("payload-reviewed.torrent")
Top-level edits preserve info hashes; info-dictionary edits change them.
write() preserves the edited object’s encoding and publishes atomically.
Pass overwrite=True to replace an existing destination. Pass canonical=True
to normalize bencoding, which can change info hashes for noncanonical input.
Reuse creation results directly:
from btpc import create_bytes
result = create_bytes("payload")
print(result.metainfo.magnet())
assert result.metainfo.verify("payload").is_valid
Copy a UTF-8 comment to another torrent without replacement decoding:
from btpc import Metainfo
source = Metainfo.read("source.torrent")
destination = Metainfo.read("destination.torrent")
comment = source.comment_text
if source.comment is not None and comment is None:
raise ValueError("source comment is not UTF-8")
destination.edit(comment=comment).write("destination-reviewed.torrent")
Inspect an immutable extension value with ordinary collection operations:
from btpc import BencodeDictionary, BencodeList
values = BencodeList((1, b"raw"))
extension = BencodeDictionary(((b"values", values),))
assert values[1:] == BencodeList((b"raw",))
assert extension[b"values"] == values
assert dict(extension) == {b"values": values}
Creation API
btpc.creation
Create v1, v2, and hybrid torrent metainfo from filesystem payloads.
CancellationToken
class CancellationToken()
Request cooperative cancellation of creation or verification.
The same token may be passed to one or more operations. Calling cancel
is thread-safe; active operations stop at a core cancellation checkpoint and
raise CancelledError.
Examples:
>>> from btpc import CancellationToken
>>> token = CancellationToken()
>>> token.cancel()
>>> token.cancelled
True
cancel
cancel() -> None
Request cancellation.
cancelled
cancelled: bool
Return whether cancellation was requested.
CreateMetrics
class CreateMetrics(scan_ms: float, hash_ms: float, serialize_ms: float)
Record elapsed time for creation phases.
Attributes:
- scan_ms: Filesystem traversal time in milliseconds.
- hash_ms: Payload hashing time in milliseconds.
- serialize_ms: Canonical metainfo serialization time in milliseconds.
scan_ms
scan_ms: float
hash_ms
hash_ms: float
serialize_ms
serialize_ms: float
CreateOptions
class CreateOptions(mode: TorrentMode = TorrentMode.V1, piece_length: int | None = None, threads: int | None = None, trackers: Sequence[Sequence[str]] = (), web_seeds: Sequence[str] = (), nodes: Sequence[tuple[str, int]] = (), private: bool | None = None, source: str | None = None, comment: str | None = None, created_by: str | None = None, omit_created_by: bool = False, creation_date: int | None = None)
Configure deterministic torrent creation.
Tracker tiers, web seeds, and nodes are copied into immutable tuples when options are constructed. Later changes to caller-owned lists have no effect.
Attributes:
- mode: Protocol representation to create. The default is
TorrentMode.V1. - piece_length: Explicit piece length in bytes.
Noneselects the core’s deterministic automatic policy; v2 and hybrid values must satisfy BEP 52 constraints. - threads: Maximum hashing worker count.
Nonelets the core choose from available parallelism. - trackers: Tracker tiers as a sequence of URL-string sequences. Text is encoded as strict UTF-8 at the native boundary.
- web_seeds: Web seed URLs encoded as strict UTF-8.
- nodes: DHT bootstrap nodes as
(host, port)pairs. - private: Optional private flag stored in the info dictionary. Changing it changes the info hash.
- source: Optional source string stored in the info dictionary. Changing it changes the info hash.
- comment: Optional top-level comment. Changing it does not change the info hash.
- created_by: Explicit top-level creator text. When omitted, BTPC writes
btpc/<version>unlessomit_created_byis true. - omit_created_by: Suppress the default creator field. It cannot be combined
with an explicit
created_byvalue. - creation_date: Unix timestamp stored at the top level. Set a fixed value,
commonly
0, for reproducible output.
Examples:
>>> from btpc import CreateOptions, TorrentMode
>>> options = CreateOptions(
... mode=TorrentMode.HYBRID,
... piece_length=16_384,
... creation_date=0,
... threads=1,
... )
>>> options.mode is TorrentMode.HYBRID
True
mode
mode: TorrentMode
piece_length
piece_length: int | None
threads
threads: int | None
trackers
trackers: Sequence[Sequence[str]]
web_seeds
web_seeds: Sequence[str]
nodes
nodes: Sequence[tuple[str, int]]
private
private: bool | None
source
source: str | None
comment
comment: str | None
created_by
created_by: str | None
omit_created_by
omit_created_by: bool
creation_date
creation_date: int | None
CreateResult
class CreateResult
Expose generated metainfo, hashes, and metrics without eager copies.
metainfo
metainfo: Metainfo
Return cached metainfo for inspection, editing, magnets, or verification.
The generated metainfo is parsed once on first access without copying it through Python bytes or reading the payload again.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import create_bytes
>>> with TemporaryDirectory() as directory:
... payload = Path(directory) / "hello.txt"
... _ = payload.write_bytes(b"hello torrent\n")
... result = create_bytes(payload)
... assert result.metainfo.verify(payload).is_valid
... assert result.metainfo is result.metainfo
bytes
bytes: bytes
Return generated metainfo bytes, copied on first request.
mode
mode: TorrentMode
Return the created protocol mode.
info_hash_v1
info_hash_v1: HashValue | None
Return the cached v1 info hash when applicable.
info_hash_v2
info_hash_v2: HashValue | None
Return the cached v2 info hash when applicable.
file_count
file_count: int
Return payload file count.
payload_bytes
payload_bytes: int
Return total payload bytes.
piece_count
piece_count: int
Return created piece count.
piece_length
piece_length: int
Return selected piece length.
piece_length_policy
piece_length_policy: str | None
Return automatic policy identifier for automatic selection.
metrics
metrics: CreateMetrics
Return cached phase timing metrics.
create
create(path: str | PathLike[str], destination: str | PathLike[str], *, options: CreateOptions | None = None, overwrite: bool = False, durable: bool = False, progress: Callable[[int, int, int], None] | None = None, cancellation: CancellationToken | None = None) -> CreateResult
Create canonical metainfo and atomically write a torrent file.
The destination is written through a temporary file and renamed into place.
Existing files are rejected unless overwrite is true. With durable
enabled, BTPC also requests file and parent-directory synchronization before
returning.
Args:
- path: Payload file or directory to traverse deterministically.
- destination: Torrent file to create atomically.
- options: Creation settings.
Noneuses :class:CreateOptionsdefaults. - overwrite: Replace an existing destination when true.
- durable: Request durable file and directory synchronization.
- progress: Optional callback receiving ``(completed_bytes, total_bytes, completed_pieces)`. Callback exceptions propagate unchanged.
- cancellation: Cooperative cancellation token.
Returns:
- **The same result data returned by :**func:
create_bytes.
Raises:
- PathError: If payload or destination I/O fails, including a disallowed overwrite.
- MetainfoError: If options or payload structure violate protocol rules.
- CancelledError: If
cancellationis requested. - Exception: Any exception raised by
progress.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import CreateOptions, create
>>> with TemporaryDirectory() as directory:
... root = Path(directory)
... payload = root / "hello.txt"
... destination = root / "hello.torrent"
... _ = payload.write_bytes(b"hello torrent\n")
... result = create(
... payload,
... destination,
... options=CreateOptions(creation_date=0, threads=1),
... )
... assert destination.read_bytes() == result.bytes
create_bytes
create_bytes(path: str | PathLike[str], *, options: CreateOptions | None = None, progress: Callable[[int, int, int], None] | None = None, cancellation: CancellationToken | None = None) -> CreateResult
Create canonical metainfo without writing a torrent file.
Args:
- path: Payload file or directory to traverse deterministically.
- options: Creation settings.
Noneuses :class:CreateOptionsdefaults. - progress: Optional callback receiving ``(completed_bytes, total_bytes, completed_pieces)`. Exceptions raised by the callback propagate to the caller and stop creation.
- cancellation: Cooperative cancellation token checked during traversal and hashing.
Returns:
The immutable result, including generated bytes, applicable info hashes, selected piece length, and phase metrics.
Raises:
- PathError: If the payload cannot be traversed or read.
- MetainfoError: If options or payload structure violate protocol rules.
- CancelledError: If
cancellationis requested. - Exception: Any exception raised by
progress.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import CreateOptions, create_bytes
>>> with TemporaryDirectory() as directory:
... payload = Path(directory) / "hello.txt"
... _ = payload.write_bytes(b"hello torrent\n")
... result = create_bytes(
... payload,
... options=CreateOptions(creation_date=0, threads=1),
... )
... assert result.file_count == 1
Metainfo API
btpc.metainfo
Parse, inspect, validate, edit, serialize, and verify torrent metainfo.
BencodeDictionary
class BencodeDictionary(items: tuple[tuple[bytes, BencodeValue], ...])
Store an immutable bencode dictionary in canonical raw-key order.
Iteration yields keys in canonical order. Indexing, membership, get,
keys, and values provide dictionary-style access. The items
attribute remains an immutable tuple of entries for compatibility.
Attributes:
- items: Unique raw-byte keys and recursive values.
items
items: tuple[tuple[bytes, BencodeValue], ...]
get
get(key: bytes, default: _DefaultValue | None = None) -> BencodeValue | _DefaultValue | None
Return the value for a raw key, or the supplied default when absent.
keys
keys() -> tuple[bytes, ...]
Return raw keys in canonical order.
values
values() -> tuple[BencodeValue, ...]
Return values in canonical key order.
BencodeList
class BencodeList(values: tuple[BencodeValue, ...])
Store an immutable ordered bencode list.
Attributes:
- values: Recursive values in source order.
values
values: tuple[BencodeValue, ...]
BencodeValue
BencodeValue: TypeAlias
Metainfo
class Metainfo
Represent validated metainfo while preserving its exact source identity.
Parsing retains the original metainfo bytes and computes info hashes from the
exact raw info dictionary slice, not from re-serialization. Equality also
compares exact original bytes. Use write to save while preserving info
hashes, to_bytes for canonical output, and original_bytes when
byte-for-byte source identity is required. Raw
protocol strings and paths remain bytes; optional *_text views decode only
valid UTF-8.
Examples:
>>> from btpc import Metainfo, TorrentMode
>>> data = (
... b"d4:infod6:lengthi1e4:name1:x12:piece lengthi16384e"
... b"6:pieces20:00000000000000000000ee"
... )
>>> torrent = Metainfo.from_bytes(data)
>>> torrent.mode is TorrentMode.V1
True
>>> torrent.original_bytes == data
True
from_bytes
from_bytes(data: object, *, options: ParseOptions | None = None) -> Metainfo
Parse metainfo from a contiguous Python buffer.
Args:
- data:
bytes,bytearray, or another readable contiguous buffer. Non-contiguous memoryviews are rejected. - options: Parser input, allocation, and integer-digit limits.
Returns:
A validated immutable metainfo object retaining exact source bytes.
Raises:
- TypeError: If
datadoes not provide a contiguous buffer. - BencodeError: If bencode syntax is invalid.
- MetainfoError: If decoded fields violate torrent protocol rules.
- ResourceLimitError: If a configured parse limit is exceeded.
Examples:
>>> from btpc import Metainfo
>>> data = (
... b"d4:infod6:lengthi1e4:name1:x12:piece lengthi16384e"
... b"6:pieces20:00000000000000000000ee"
... )
>>> Metainfo.from_bytes(memoryview(data)).name
b'x'
read
read(path: str | PathLike[str], *, options: ParseOptions | None = None) -> Metainfo
Read and parse metainfo directly from a filesystem path.
Args:
- path: Torrent file path.
- options: Parser input, allocation, and integer-digit limits.
Returns:
A validated immutable metainfo object.
Raises:
- PathError: If the file cannot be opened or read.
- BencodeError: If bencode syntax is invalid.
- MetainfoError: If decoded fields violate torrent protocol rules.
- ResourceLimitError: If a configured parse limit is exceeded.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import Metainfo
>>> data = (
... b"d4:infod6:lengthi1e4:name1:x12:piece lengthi16384e"
... b"6:pieces20:00000000000000000000ee"
... )
>>> with TemporaryDirectory() as directory:
... path = Path(directory) / "sample.torrent"
... _ = path.write_bytes(data)
... assert Metainfo.read(path).original_bytes == data
verify
verify(payload: str | PathLike[str], *, fail_fast: bool = False, extra_files: bool = False, progress: Callable[[int, int, int], None] | None = None, cancellation: CancellationToken | None = None) -> PayloadVerificationReport
Verify payload files using every hash domain present in this torrent.
payload is resolved as the torrent’s payload root: a single-file torrent
may point directly to its file, while a multi-file torrent points to the
containing directory. v1 verifies the concatenated piece stream, v2 verifies
per-file Merkle roots, and hybrid verifies both. Mismatches are returned in
deterministic order; filesystem and policy failures raise exceptions.
Args:
- payload: Payload file or root directory.
- fail_fast: Stop after the first mismatch when true.
- extra_files: Report files not represented by the torrent.
- progress: Optional callback receiving ``(completed_bytes, total_bytes, completed_pieces)`. Callback exceptions propagate unchanged.
- cancellation: Cooperative cancellation token.
Returns:
A report containing zero or more deterministic mismatches.
Raises:
- PathError: If required payload paths cannot be read safely.
- VerificationError: If verification cannot be performed as requested.
- CancelledError: If
cancellationis requested. - Exception: Any exception raised by
progress.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import CreateOptions, Metainfo, create_bytes
>>> with TemporaryDirectory() as directory:
... payload = Path(directory) / "hello.txt"
... _ = payload.write_bytes(b"hello torrent\n")
... created = create_bytes(
... payload,
... options=CreateOptions(creation_date=0, threads=1),
... )
... assert Metainfo.from_bytes(created.bytes).verify(payload).is_valid
magnet
magnet(*, display_name: bool = True, trackers: bool = True, web_seeds: bool = True) -> str
Generate a deterministic magnet URI.
Args:
- display_name: Include the decoded display name when valid UTF-8.
- trackers: Include tracker URLs.
- web_seeds: Include web seed URLs.
Returns:
A magnet URI containing every applicable v1 and v2 exact-topic hash.
Examples:
>>> from btpc import Metainfo
>>> data = (
... b"d4:infod6:lengthi1e4:name1:x12:piece lengthi16384e"
... b"6:pieces20:00000000000000000000ee"
... )
>>> magnet = Metainfo.from_bytes(data).magnet(trackers=False)
>>> magnet.startswith("magnet:?xt=")
True
edit
edit(*, trackers: Sequence[Sequence[str]] | None | UnchangedType = UNCHANGED, web_seeds: Sequence[str] | None | UnchangedType = UNCHANGED, nodes: Sequence[tuple[str, int]] | None | UnchangedType = UNCHANGED, private: bool | None | UnchangedType = UNCHANGED, source: str | None | UnchangedType = UNCHANGED, comment: str | None | UnchangedType = UNCHANGED, created_by: str | None | UnchangedType = UNCHANGED, creation_date: int | None | UnchangedType = UNCHANGED, raw_top_level: Mapping[bytes, BencodeValue] | None = None, file_attributes: dict[tuple[bytes, ...], bytes] | None = None) -> Metainfo
Return a validated copy with explicit preserve, remove, or set edits.
Optional fields use three states: :data:btpc.UNCHANGED preserves the
current value, None removes it, and a typed value replaces it. Trackers,
web seeds, nodes, comments, creator, and creation date are top-level fields
and do not affect info hashes. private, source, and file attributes
edit the info dictionary and therefore change applicable info hashes.
Top-level-only edits retain the exact original info bytes, including a
noncanonical source encoding. Use to_bytes for an explicit fully
canonical serialization.
Args:
- trackers: Tracker tiers,
Noneto remove, orUNCHANGED. - web_seeds: Web seeds,
Noneto remove, orUNCHANGED. - nodes: DHT nodes,
Noneto remove, orUNCHANGED. - private: Private flag,
Noneto remove, orUNCHANGED. - source: Source string,
Noneto remove, orUNCHANGED. - comment: Comment,
Noneto remove, orUNCHANGED. - created_by: Creator text,
Noneto remove, orUNCHANGED. - creation_date: Unix timestamp,
Noneto remove, orUNCHANGED. - raw_top_level: Raw recursive bencode extension replacements.
- file_attributes: Raw attributes keyed by raw torrent path components.
Returns:
A newly validated immutable object retaining untouched info bytes.
Raises:
- TypeError: If a textual field receives a non-string value.
- MetainfoError: If an edit would produce invalid metainfo.
Examples:
>>> from btpc import UNCHANGED, Metainfo
>>> data = (
... b"d4:infod6:lengthi1e4:name1:x12:piece lengthi16384e"
... b"6:pieces20:00000000000000000000ee"
... )
>>> torrent = Metainfo.from_bytes(data)
>>> with_comment = torrent.edit(comment="reviewed")
>>> preserved = with_comment.edit(comment=UNCHANGED)
>>> removed = with_comment.edit(comment=None)
>>> preserved.info_hash_v1 == removed.info_hash_v1 == torrent.info_hash_v1
True
>>> torrent.edit(source="release").info_hash_v1 != torrent.info_hash_v1
True
mode
mode: TorrentMode
Return the validated protocol representation.
name
name: bytes
Return raw torrent name bytes.
piece_length
piece_length: int
Return piece length in bytes.
total_length
total_length: int
Return total payload bytes.
piece_count
piece_count: int
Return logical piece count.
files
files: tuple[TorrentFile, ...]
Return cached immutable payload file objects.
trackers
trackers: tuple[tuple[bytes, ...], ...]
Return cached immutable tracker tiers.
trackers_text
trackers_text: tuple[tuple[str, ...], ...] | None
Return decoded tracker tiers, or None if any URL is not UTF-8.
web_seeds
web_seeds: tuple[bytes, ...]
Return cached immutable web seeds.
web_seeds_text
web_seeds_text: tuple[str, ...] | None
Return decoded web seeds, or None if any URL is not UTF-8.
private
private: bool | None
Return the explicit private flag.
nodes
nodes: tuple[tuple[bytes, int], ...]
Return cached immutable DHT bootstrap nodes as raw hosts and ports.
nodes_text
nodes_text: tuple[tuple[str, int], ...] | None
Return decoded DHT nodes, or None if any host is not UTF-8.
source
source: bytes | None
Return raw source bytes from the info dictionary.
source_text
source_text: str | None
Return the source as UTF-8, or None when absent or undecodable.
comment
comment: bytes | None
Return raw top-level comment bytes.
comment_text
comment_text: str | None
Return the comment as UTF-8, or None when absent or undecodable.
created_by
created_by: bytes | None
Return raw top-level creator bytes.
created_by_text
created_by_text: str | None
Return creator text as UTF-8, or None when absent or undecodable.
creation_date
creation_date: int | None
Return the non-negative Unix creation timestamp.
info_hash_v1
info_hash_v1: HashValue | None
Return the cached v1 info hash when applicable.
info_hash_v2
info_hash_v2: HashValue | None
Return the cached v2 info hash when applicable.
original_bytes
original_bytes: bytes
Return exact source bytes, copied only on first request.
unknown_fields
unknown_fields: tuple[UnknownField, ...]
Return cached unknown fields with values and exact source encodings.
name_text
name_text: str | None
Return the name as UTF-8 when valid.
write
write(destination: str | PathLike[str], *, canonical: bool = False, overwrite: bool = False, durable: bool = False) -> None
Atomically save metainfo while preserving its info hashes by default.
Default output retains this object’s exact bytes, including edits already
applied. canonical=True normalizes bencoding and can change info hashes
when the source info dictionary is noncanonical. Payload files are not read.
Args:
- destination: Torrent file to publish.
- canonical: Normalize all bencoding when true. The default preserves this object’s encoding and torrent identity.
- overwrite: Replace an existing destination when true.
- durable: Request file and parent-directory synchronization.
Raises:
- PathError: If publication fails or overwrite is disallowed. A directory synchronization failure can occur after complete publication.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import Metainfo
>>> source = (
... b"d4:infod6:lengthi00e4:name1:x12:piece lengthi16384e6:pieces0:ee"
... )
>>> torrent = Metainfo.from_bytes(source)
>>> with TemporaryDirectory() as directory:
... path = Path(directory) / "reviewed.torrent"
... torrent.edit(comment="reviewed").write(path)
... assert Metainfo.read(path).info_hash_v1 == torrent.info_hash_v1
to_bytes
to_bytes(*, canonical: bool = True) -> bytes
Serialize canonical metainfo or return the exact original bytes.
The compatibility default is canonical output. For noncanonical source
info this can change info hashes on reparse. Use canonical=False or
write to preserve this object’s torrent identity.
Args:
- canonical: Sort dictionary keys by unsigned raw-byte order and normalize
bencode when true. False returns
original_bytesunchanged.
Returns:
Serialized metainfo bytes.
Examples:
>>> from btpc import Metainfo
>>> data = (
... b"d4:infod6:lengthi1e4:name1:x12:piece lengthi16384e"
... b"6:pieces20:00000000000000000000ee"
... )
>>> torrent = Metainfo.from_bytes(data)
>>> torrent.to_bytes(canonical=False) == data
True
>>> reparsed = Metainfo.from_bytes(torrent.to_bytes())
>>> reparsed.info_hash_v1 == torrent.info_hash_v1
True
validation
validation: ValidationReport
Return the construction-time validation report.
validate
validate() -> ValidationReport
Return validation for compatibility; no new check is performed.
TorrentFile
class TorrentFile(length: int, path: tuple[bytes, ...], attributes: bytes, pieces_root: HashValue | None, is_padding: bool = False)
Describe one file in the torrent’s logical payload.
Attributes:
- length: File length in bytes.
- path: Raw torrent path components. Components remain bytes because torrent paths are not required to be UTF-8.
- attributes: Raw BEP 47/BEP 52 file attribute bytes.
- pieces_root: v2 Merkle root when present.
- is_padding: Whether the entry is a padding file.
length
length: int
path
path: tuple[bytes, ...]
attributes
attributes: bytes
pieces_root
pieces_root: HashValue | None
is_padding
is_padding: bool
path_text
path_text: tuple[str, ...] | None
Return UTF-8 components when every component decodes.
torrent_path
torrent_path: TorrentPath
Return the raw-identity typed torrent path.
UnknownField
class UnknownField(key: bytes, value: BencodeValue, encoded: bytes, span: tuple[int, int])
Describe one unknown top-level field and its exact source encoding.
Attributes:
- key: Raw top-level dictionary key.
- value: Recursive semantic bencode value.
- encoded: Exact source bytes covering the encoded key and value.
- span: Half-open offsets for
encodedwithinoriginal_bytes.
key
key: bytes
value
value: BencodeValue
encoded
encoded: bytes
span
span: tuple[int, int]
ValidationReport
class ValidationReport(warnings: tuple[str, ...], canonical: bool, canonical_offset: int | None = None, canonical_message: str | None = None)
Summarize validation and canonical-encoding status.
Attributes:
- warnings: Non-fatal protocol or interoperability warnings.
- canonical: Whether the original source used canonical bencoding.
- canonical_offset: Source offset of the first canonicalization issue.
- canonical_message: Human-readable canonicalization issue.
warnings
warnings: tuple[str, ...]
canonical
canonical: bool
canonical_offset
canonical_offset: int | None
canonical_message
canonical_message: str | None
is_valid
is_valid: bool
Return true because invalid metainfo fails construction.
Verification API
btpc.verification
Verify payload files against v1 pieces and v2 Merkle roots.
MismatchKind
class MismatchKind
Stable payload verification mismatch category.
MISSING
WRONG_SIZE
EXTRA
UNSAFE_PATH
V1_HASH
V2_HASH
PayloadMismatch
class PayloadMismatch(kind: MismatchKind, path: Path, piece: int | None)
Describe one deterministic payload mismatch.
Attributes:
- kind: Stable mismatch category.
- path: Payload-relative filesystem path.
- piece: Zero-based v1 piece index when the mismatch is piece-specific.
kind
kind: MismatchKind
path
path: Path
piece
piece: int | None
PayloadVerificationReport
class PayloadVerificationReport(mismatches: tuple[PayloadMismatch, ...])
Collect deterministic mismatches from a completed verification.
Attributes:
- mismatches: Mismatches in deterministic path and piece order.
mismatches
mismatches: tuple[PayloadMismatch, ...]
is_valid
is_valid: bool
Return whether every enabled check passed.
verify
verify(metainfo: Metainfo, payload: str | PathLike[str], *, fail_fast: bool = False, extra_files: bool = False, progress: Callable[[int, int, int], None] | None = None, cancellation: CancellationToken | None = None) -> PayloadVerificationReport
Verify payload content against all hashes represented by metainfo.
payload points directly to a single-file payload or to the root directory of
a multi-file torrent. v1 verification hashes the logical concatenated file
stream; v2 verification checks each file’s Merkle root; hybrid torrents check
both. Missing files, unsafe paths, and content mismatches are returned in the
report. Operational I/O failures raise exceptions.
Relative roots use the current directory. Torrent paths must match actual directory entries exactly, including on case-insensitive filesystems, and must not cross symlinks. Verification keeps a bounded number of files open and checks saved file identity and state before and after hashing and before it reports successful verification. These checks do not provide an atomic filesystem snapshot. Imported v2 torrents can use BEP 52 piece lengths above the creation policy’s 16 MiB limit.
Args:
- metainfo: Parsed torrent describing the expected payload.
- payload: Payload file or root directory.
- fail_fast: Stop after the first mismatch when true.
- extra_files: Include files absent from the torrent as mismatches.
- progress: Optional callback receiving ``(completed_bytes, total_bytes, completed_pieces)`. Callback exceptions propagate unchanged.
- cancellation: Cooperative cancellation token.
Returns:
A report containing zero or more deterministic mismatches.
Raises:
- PathError: If an operational filesystem read fails.
- VerificationError: If verification cannot be completed under the selected policy.
- CancelledError: If
cancellationis requested. - Exception: Any exception raised by
progress.
Examples:
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from btpc import CreateOptions, Metainfo, create_bytes, verify
>>> with TemporaryDirectory() as directory:
... payload = Path(directory) / "hello.txt"
... _ = payload.write_bytes(b"hello torrent\n")
... created = create_bytes(
... payload,
... options=CreateOptions(creation_date=0, threads=1),
... )
... torrent = Metainfo.from_bytes(created.bytes)
... assert verify(torrent, payload).is_valid
Public Types API
btpc.types
Shared immutable values for protocol modes, hashes, bytes, and paths.
UNCHANGED
HashValue
class HashValue(bytes: bytes)
Store a protocol hash as immutable raw bytes.
Attributes:
- bytes: Raw digest bytes.
bytes
bytes: bytes
hex
hex: str
Return lowercase hexadecimal text.
ParseOptions
class ParseOptions(max_total_input: int | None = None, max_owned_allocation: int | None = None, max_integer_digits: int | None = None)
Limit parser resource use before accepting owned metainfo.
None uses the core’s safe default for each limit. Limits apply equally to
Metainfo.from_bytes and Metainfo.read.
Attributes:
- max_total_input: Maximum encoded metainfo bytes accepted before parsing.
- max_owned_allocation: Maximum cumulative allocation for the owned parse.
- max_integer_digits: Maximum decimal digits in one bencoded integer.
Examples:
>>> from btpc import Metainfo, ParseOptions, ResourceLimitError
>>> options = ParseOptions(max_total_input=4)
>>> try:
... Metainfo.from_bytes(b"d4:infoe", options=options)
... except ResourceLimitError as error:
... assert error.limit == "total input"
max_total_input
max_total_input: int | None
max_owned_allocation
max_owned_allocation: int | None
max_integer_digits
max_integer_digits: int | None
TorrentBytes
class TorrentBytes(raw: bytes)
Preserve a torrent byte string without assuming text encoding.
Attributes:
- raw: Exact protocol bytes.
raw
raw: bytes
text
text: str | None
Return UTF-8 text only when decoding is lossless.
TorrentMode
class TorrentMode
Validated BitTorrent protocol representation.
V1
V2
HYBRID
TorrentPath
class TorrentPath(components: tuple[TorrentBytes, ...])
Preserve and validate a torrent path as raw byte components.
Attributes:
- components: Non-empty safe path components in torrent order.
components
components: tuple[TorrentBytes, ...]
text
text: tuple[str, ...] | None
Return decoded components only when every component is UTF-8.
to_path
to_path() -> Path | None
Return a platform path only when every component decodes safely.
Error API
btpc.errors
Catch structured errors from parsing, creation, editing, and verification.
BencodeError
class BencodeError
Report malformed bencode syntax or an invalid canonical encoding.
BtpcError
class BtpcError(message: str, *, offset: int | None = None, field: str | None = None, path: Path | None = None, limit: str | None = None, actual: int | None = None, maximum: int | None = None)
Base class for stable BTPC error categories.
Inspect the optional attributes instead of parsing the human-readable message. Only attributes relevant to a particular failure are populated.
Attributes:
- offset: Encoded byte offset for syntax or canonicalization failures.
- field: Protocol field associated with a validation failure.
- path: Filesystem path associated with an I/O or safety failure.
- limit: Resource-limit name.
- actual: Observed resource value.
- maximum: Configured maximum resource value.
offset
field
path
limit
actual
maximum
CancelledError
class CancelledError
Report cooperative cancellation requested through a cancellation token.
MetainfoError
class MetainfoError
Report torrent fields that violate v1, v2, or hybrid protocol rules.
PathError
class PathError
Report filesystem I/O, unsafe path, traversal, or destination failures.
ResourceLimitError
class ResourceLimitError
Configured parser or ownership resource limit was exceeded.
raise_exceeded
raise_exceeded(limit: str, actual: int, maximum: int) -> Never
Raise a structured limit error matching the native diagnostic.
UnsupportedError
class UnsupportedError
Report a requested feature or policy that BTPC does not support.
VerificationError
class VerificationError
Report an operational failure that prevents payload verification.
Rust API Guide
btpc-core is the protocol source of truth. It forbids unsafe Rust, preserves raw
bytes, exposes structured errors, and uses per-operation configurable concurrency
rather than an ambient global thread pool. The workspace MSRV is Rust 1.85.
Development documentation: The embedded
btpc-corerustdoc is generated from the currentmainbranch and may change before a stable release. The crate is prepared and validated for crates.io, but the owner has not performed the first publish, so no live docs.rs release page is claimed yet.
Use the crate source for implementation context and the executable consumer examples for independently compiled facade usage.
Quick Start
Parse and Inspect
#![allow(unused)]
fn main() {
extern crate btpc_core;
use btpc_core::{Metainfo, TorrentMode};
let bytes = b"d4:infod6:lengthi0e4:name5:empty12:piece lengthi16e6:pieces0:ee";
let torrent = Metainfo::from_bytes(bytes)?;
assert_eq!(torrent.mode(), TorrentMode::V1);
assert_eq!(torrent.original_bytes(), bytes);
assert!(torrent.validate().is_valid());
Ok::<(), btpc_core::Error>(())
}
Metainfo is owned and immutable. original_bytes() and write_original() retain
source identity; to_bytes() and write_canonical() emit deterministic canonical
bencode. Canonical bytes are materialized and cached only when one of those
canonical-output methods is called, so ordinary inspection does not serialize the
owned bencode tree. Source info hashes always use the exact raw info slice.
Multi-file path graphs reject duplicate and file/directory-prefix collisions.
Hybrid inspection includes validated alignment padding in files() and exposes it
through TorrentFile::is_padding(); callers that need only payload files should
filter that flag explicitly.
Default loading applies conservative input, depth, item, byte-string, parser-tree,
and owned-snapshot limits. Advanced callers can pass ParseOptions to
Metainfo::from_bytes_with_options or Metainfo::from_path_with_options. Path
loading preflights regular-file length and also caps the actual read, so growth
races and non-regular streams cannot bypass max_total_input.
Callers that already own the input buffer can use Metainfo::from_vec or
Metainfo::from_vec_with_options to transfer it into the parsed object without an
additional input copy. The object retains those exact source bytes for original
serialization and source info hashes.
The low-level bencode::Integer borrows the complete signed decimal bytes and
supports fallible to_i64()/to_u64() conversion. OwnedValue::integer_bytes
preserves arbitrary-precision canonical integers without a heavyweight arithmetic
dependency; typed metainfo fields still reject values outside their protocol
domain with field context. ParseLimits::with_max_integer_digits bounds hostile
digit runs independently of numeric conversion.
Create
#![allow(unused)]
fn main() {
extern crate btpc_core;
use btpc_core::create::{
CreateMode, CreateOptions, Creator, HashThreads, NoProgress, PieceLength,
};
let options = CreateOptions::builder()
.mode(CreateMode::Hybrid)
.piece_length(PieceLength::Exact(16_384))
.hash_threads(HashThreads::Exact(1))
.creation_date(0)
.build()?;
let result = Creator::new("payload")
.options(options)
.create(&NoProgress)?;
assert!(result.info_hash_v1().is_some());
assert!(result.info_hash_v2().is_some());
Ok::<(), btpc_core::Error>(())
}
Creation scans deterministically, streams payload files with bounded memory,
selects deterministic automatic piece lengths, and supports v1, v2, and hybrid.
ManifestOptionsBuilder::include and exclude are UTF-8 glob APIs and reject a
non-UTF-8 payload path when active. Use include_raw_paths and
exclude_raw_paths for exact component-by-component byte matching. Top-level
symlinks are rejected by default; skip also reports an error because it cannot
produce a meaningful manifest, while follow retains the selected link name and
still enforces nested escape and cycle checks.
Manifest entries snapshot file type, size, available timestamps, and device/inode on Unix or volume/file identity on Windows. Every hasher validates both the pathname and opened handle before and after reading. This detects replacement, rename-over-open, truncation/regrowth, and most concurrent mutation. Filesystems that reuse identity and restore every exposed timestamp can still defeat metadata snapshotting, so applications requiring a frozen tree should provide one at the filesystem or storage layer.
Use Creator::create_to_path for atomic output with an explicit overwrite policy.
Creator::create_to_path_with_durability additionally accepts
DurabilityPolicy::FileAndDirectory, which syncs the parent directory after
publication where the platform supports directory syncing. Deny mode never
replaces an existing path, including a symlink. Replace mode replaces the
symlink entry rather than its target and preserves permissions when replacing a
regular file.
HashThreads::Exact(1) retains the sequential oracle; automatic and explicit
parallel modes remain bounded to the operation.
Verify
#![allow(unused)]
fn main() {
extern crate btpc_core;
use btpc_core::Metainfo;
use btpc_core::create::NoProgress;
use btpc_core::verify::Verifier;
let torrent = Metainfo::from_path("payload.torrent")?;
let report = Verifier::new(&torrent, "payload").verify(&NoProgress)?;
assert!(report.is_valid());
Ok::<(), btpc_core::Error>(())
}
Verification reports deterministic mismatch values and checks every hash domain applicable to the mode. Symlinks are rejected rather than followed. Options select fail-fast behavior and extra-file reporting.
Progress, Cancellation, and Errors
Implement create::ProgressSink for presentation-independent progress snapshots.
Clone create::CancellationToken into the caller and operation to request
cooperative cancellation. Public functions return btpc_core::Result<T> with a
structured ErrorCategory, contextual byte offset/field/path where applicable,
and preserved I/O sources.
Layering
Applications should depend on btpc-core for protocol behavior. Do not duplicate
bencode parsing, hashing, traversal, Merkle, canonicalization, or verification in
adapters. The CLI and Python packages demonstrate the intended thin-wrapper model.
Stability And Features
The supported namespaces are the crate-root facade plus bencode, create,
edit, magnet, metainfo, and verify. Private implementation modules remain
private, and public signatures use standard-library or BTPC-owned types rather
than exposing hashing, glob, CLI, Python, URL, or executor dependencies.
btpc-core currently has no optional behavior: its explicit default feature set
is empty, and default, no-default-feature, and all-feature builds are equivalent.
New features require a concrete dependency or platform benefit and must remain
additive. tests/rust-consumer compiles documented facade paths as an independent
crate. Pull requests run cargo-semver-checks against the base revision; release
branches run scripts/check_rust_api.sh <accepted-tag-or-revision> before version
publication.
Performance
BTPC measures creation, parsing, verification, and Python-boundary behavior with validated outputs before accepting timings. Performance claims record tool versions, hardware, dataset, cache state, elapsed distribution, throughput, and peak RSS.
The checked-in benchmark harness supports reproducible synthetic fixtures and a canonical Debian ISO preflight. Large cross-tool results are report-oriented rather than flaky per-commit gates. See the repository’s benchmark methodology for commands and accepted baselines.
Compatibility and Release Guide
Supported Development Matrix
- Rust: current stable and MSRV 1.85.
- Operating systems: Linux, macOS, and Windows.
- Python: CPython 3.11, 3.12, 3.13, and 3.14.
- Torrent modes: v1, v2 (BEP 52), and hybrid.
The matrix describes source and CI support. BTPC is pre-1.0 and no public package registry release has been made. Final wheels and native archives must pass clean artifact smoke tests before publication.
The release wheel targets are Linux x86-64/AArch64 with glibc 2.28 or later, macOS Intel/Apple Silicon, and Windows x86-64. The installation guide explains wheel selection and source-build requirements. The Python wheel contains the library; the CLI is distributed separately.
Python Runtime Model
BTPC currently requires the CPython global interpreter lock. The extension
explicitly declares gil_used = true to PyO3, so a free-threaded CPython build
may import it only with the GIL enabled. Long-running Rust work still releases
the GIL, allowing independent BTPC operations to execute concurrently.
Subinterpreters are not supported. PyO3 0.29 rejects initialization from a
second interpreter because extension-module state and cached Python objects are
not yet per-interpreter. Use one main interpreter with threads or separate
processes. Runtime callers can inspect btpc._native.__gil_required__ and
btpc._native.__subinterpreters_supported__.
Interoperability
The committed fixture corpus covers mktorrent 1.1, mkbrr 1.23.0, torf-cli 5.2.1, and torrenttools 0.6.2 in every mode each tool supports. It also records malformed, noncanonical, and prior-fuzz cases with explicit accept/preserve/reject behavior. See the interoperability fixture documentation in the repository.
Stability
Before 1.0, Rust/Python APIs and CLI JSON schemas may evolve, but intentional compatibility changes must update specs, tests, documentation, and release notes. CLI exit categories and currently named JSON schema identifiers are tested. Unknown protocol byte strings remain byte-safe; text convenience views are best-effort only.
Artifact Expectations
Release wheels and archives must be produced from one verified version, install in clean environments, run all three torrent modes, inspect and magnetize output, and verify payloads. Publication should use least privilege and provenance. Normative requirements live in the release specification.
The manual workflow resolves a release tag to one commit before it starts checks and builds. It runs the full CI and documentation gates against that commit. Each wheel runs behavior checks and external Pyrefly/Pyright consumers in a fresh environment outside the checkout. A TestPyPI publication exercises the registry download and install path before the owner selects PyPI. See the release checklist for registry setup and publication steps.
The Cargo workspace package version is the single source. Start a release bump
with make version VERSION=X.Y.Z, update CHANGELOG.md, then validate the tag
with uv run python scripts/check_version.py --tag vX.Y.Z.
Performance Claims
No universal performance claim is valid without the canonical benchmark preflight, pinned competitor versions, repeated randomized measurements, raw result files, and machine/tool metadata. Reports must name limitations. See the benchmark documentation.
Optional metadata validation
BTPC preserves optional metadata as raw bytes while applying one domain policy in
parse, create, edit, CLI, and Python surfaces. Empty tracker or web-seed URLs,
empty tracker tiers, empty DHT hosts, DHT port 0, ports above 65535, malformed
node pairs, non-integer or negative creation dates, and wrong bencode types for
recognized text fields are errors. Ports 1 and 65535, duplicate non-empty
values, empty text values, and non-UTF-8 text bytes are valid and lossless.
For compatibility, an explicitly empty announce-list is ignored in favor of a
valid announce value and produces a validation warning. Creation never emits
that warned form. An empty url-list means no web seeds and is accepted without a
warning.
Repository security operations
Maintainers own dependency, CodeQL, Scorecard, secret-scanning, and private vulnerability alerts. The maintainer handling an alert records severity, affected versions, an owner, remediation plan, and disclosure decision in the corresponding private report or tracking issue.
GitHub settings checklist
- Enable Dependabot alerts and security updates. Dependabot version updates remain review-only; do not enable automatic merging.
- Enable private vulnerability reporting and publish
SECURITY.mdas the reporting policy. - Enable secret scanning and push protection, including alerts for contributors where the repository plan supports them.
- Protect
mainwith the required checks inCONTRIBUTING.md, required approving review, stale-review dismissal, conversation resolution, and current-branch enforcement. Disable force pushes and deletion. - Restrict workflow permissions to read-only by default and allow GitHub Actions to create pull requests only if a reviewed workflow later requires it.
- Review CodeQL and Scorecard SARIF in code scanning. Assign every actionable alert before dismissal and document false-positive rationale.
Dependency exceptions
Dependency Review rejects high or critical vulnerabilities and dependencies outside
the license allowlist shared with deny.toml. An exception requires maintainer
approval in a dedicated issue containing the advisory/license, affected dependency,
rationale, compensating controls, owner, remediation target, and an expiration date
no more than 90 days away. The configuration change must link to that issue. Remove
expired exceptions immediately; renewal requires a new review.
Coverage policy
Rust and Python coverage are informational during pre-1.0 development and are not merged into a misleading cross-language percentage. Artifacts retain the native LCOV and Coverage.py XML formats. A future threshold must be based on an accepted baseline and prevent regression in protocol-critical modules rather than reward low-value line coverage.
Contributing
Run the same focused gates used by CI before opening a pull request:
make check
make docs-check
Use make docs-serve to preview the project-subpath site. Handwritten pages live
in docs/; generated Python and Rust API text comes from source docstrings and
rustdoc, while CLI references come from the Clap model. Keep generated site/
output untracked and never hand-edit generated CLI files or HTML.
make docs-check enforces offline links and anchors, canonical URLs, required
entry points, private-name protection, and budgets of 16,000,000 uncompressed
bytes and 4,500,000 normalized gzip bytes. Budget increases require recorded
artifact measurements.
Implementation contracts are maintained separately for contributors in the normative specifications. Public guides describe supported behavior rather than requirement identifiers.
Release checklist
BTPC has not been published to a package registry. Complete registry setup before the first publication. The workflow keeps publication disabled by default and selects TestPyPI as its default registry.
Registry setup
Sign in to the PyPI account that will own btpc. Enable two-factor authentication.
Confirm that the project name is available or that the account can publish an
existing project. For a new project, add a pending Trusted Publisher. For an
existing project, add the publisher in its Publishing settings. Repeat this setup
on TestPyPI; the two registries have separate accounts and publisher records.
Use these fields for each GitHub Actions publisher:
| Field | PyPI | TestPyPI |
|---|---|---|
| Project name | btpc | btpc |
| Repository owner | burritothief | burritothief |
| Repository name | btpc | btpc |
| Workflow filename | release.yml | release.yml |
| Environment name | pypi | testpypi |
Create the matching pypi and testpypi GitHub environments. Add a required
maintainer reviewer and allow deployment only from version tags matching v*.
For a sole maintainer, the reviewer must be able to approve their own run. With
another available reviewer, enable prevention of self-review. Configure these
rules before dispatching a publishing run. The workflow requests a short-lived
OIDC credential; no PyPI API token belongs in repository secrets.
Protect release version tags from updates and deletion.
See PyPI’s pending publisher guide and publisher usage guide. Record ownership and publisher setup privately. Do not publish account credentials.
Validate a release candidate
- Choose a version and update it with
make version VERSION=X.Y.Z. Update the changelog and its release date when publication is scheduled. Confirm that the README identifies the actual publication state and installation commands. Refresh the local Python build withuv sync --refresh-package btpc --reinstall-package btpcbefore testing. This clears cached Python metadata from the previous Cargo workspace version. - Run the required source gates in
AGENTS.md,make docs-check, andcargo deny check. Confirm that the spec and generated CLI checks pass. - Build a release wheel with
uv run maturin build --release --locked --out dist. Runuv run python scripts/check_wheel.py dist/<wheel>.whl. This gate installs only that wheel in a fresh environment outside the checkout. It checks all torrent modes, edits, hash failures, progress, cancellation, native stub signatures, and external Pyrefly/Pyright consumers. - Create a version-matching tag on the reviewed commit. Run Release artifacts
with that tag and
publish=false. Review every quality, documentation, wheel, sdist, CLI, packaged-crate, API, and assembled-artifact result. Each checkout must use the commit resolved by the version job. - Inspect wheel/sdist metadata, license files, supported platform tags, CLI
archives, the source archive, and
SHA256SUMS. The Python wheel does not contain the CLI. Confirm that all artifacts use the same version.
Rehearse on TestPyPI
Dispatch the workflow from the release tag, with the matching tag input:
gh workflow run release.yml --ref vX.Y.Z -f tag=vX.Y.Z -f publish=true -f repository=testpypi
Approve the testpypi environment after reviewing the candidate. This run
publishes Python artifacts and provenance. It does not publish the Rust crate or
create a GitHub release. The registry check downloads the published CPython 3.11
Linux wheel from TestPyPI and repeats the clean installation checks. Review the
TestPyPI project page, README rendering, files, and ownership.
You can also install it in a fresh environment:
python -m pip install --index-url https://test.pypi.org/simple/ --only-binary=:all: --no-deps btpc==X.Y.Z
python -c "import btpc; print(btpc.__version__)"
BTPC has no Python runtime dependencies. Use only the selected registry for this rehearsal. Uploaded files cannot be replaced, so use a new version for a changed candidate. See the TestPyPI guide.
Publish and inspect
After the TestPyPI rehearsal succeeds, dispatch the same tag with
repository=pypi and publish=true. Leave publish_crate=false for a Python-only
release. Approve the pypi environment. The workflow also prepares a draft GitHub
release.
To publish btpc-core in the same run, select publish_crate=true. Configure the
crates-io environment and its narrowly scoped CRATES_IO_TOKEN first. This
optional job requests a separate approval and runs only for PyPI publication.
Confirm the production registry download check, project ownership, metadata,
README rendering, complete wheel matrix, sdist, and provenance. Check installation
with python -m pip install --only-binary=:all: btpc==X.Y.Z. Update documentation
to describe registry pages as live only after publication succeeds. Review the
draft release notes before making the GitHub release public.
Registry uploads cannot be replaced. If a released artifact is defective, publish a corrected version and consider yanking the defective version with an explicit reason. Do not delete release history or reuse a version number. A failed post-publication check does not roll back an upload.
Documentation and Rust crate checks
- Run
make docs-checkfrom a clean checkout. - Confirm the production documentation exposes Getting Started, CLI, Python, and Rust entry points.
- Verify repository, edit, issue, license, canonical, and sitemap links use the
burritothief/btpcproject and/btpc/Pages subpath. - Confirm the current
mainbranch documentation label remains visible. Do not describe it as versioned release documentation until a separate versioning policy is implemented. - Inspect the successful Documentation workflow and
github-pagesdeployment URL for the release commit. - Smoke-test the custom 404, search index, local assets, and embedded rustdoc over HTTPS before publishing release notes.
- Run
scripts/check_crate_package.sh 1.85.0andscripts/check_crate_package.sh 1.94.1, then inspecttarget/package/btpc-core-<version>.crateforREADME.md,LICENSE, sources, andexamples/inspect.rs. The script downloads locked dependencies before testing the extracted package and an external consumer offline. Confirm that it also passes with an empty Cargo cache on the release runner. - Run
cargo publish -p btpc-core --locked --dry-runand the Rust API compatibility check against the previous release tag when one exists. Install the CI-pinned checker withcargo install cargo-semver-checks --version 0.50.0 --locked. Runscripts/check_rust_api.sh v<previous-version>with Rust 1.94.1 before creating a tag. Check its result for both API changes and tool errors. - Protect the
crates-ioGitHub environment, configure its narrowly scopedCRATES_IO_TOKEN, and approve the manual release job only for an existing version-matching tag. Ordinary pushes never publish the crate. - For the first publish, confirm crate ownership and the resulting crates.io and docs.rs pages manually before adding links that describe either page as live.