0.6.3 - 2026-08-11
Fixed
- Release pipeline:
cosign sign-blobnow writes a--bundlefile instead of the deprecated--output-signature/--output-certificateflags, which current cosign silently ignores in favor of its new Sigstore bundle format (0.6.2's release signing failed withcreate bundle file: open : no such file or directoryas a result). The bundle is named*.tar.sigstore.jsonso OpenSSF Scorecard's Signed-Releases check recognizes it. No runtime/library changes.
0.6.2 - 2026-08-11
Fixed
- Release pipeline: publishing to Hex.pm is now idempotent (skips cleanly if the tag's version is already on Hex.pm instead of failing), and cosign's release-signing step retries on transient OIDC-token fetch failures instead of falling through to an interactive device-code flow that stalls CI. No runtime/library changes.
0.6.1 - 2026-08-11
Changed
- Runtime pin moved to Elixir 1.20.3 + OTP 29.0.5 (ERTS 17.0.5)
- Hackney 4.7.2, H2 0.11.0, QUIC 1.8.0, WebTransport 0.4.4 (HTTP stack behind HTTPoison, used by HTTP trackers, scrape, and BEP 19 web seeds)
0.6.0 - 2026-08-04
Added
- BEP 14 (LSD): Local Service Discovery via UDP multicast (
BT-SEARCH) - BEP 19 (WebSeed): HTTP
Rangeseeding fromurl-listviaTorrent.WebSeed - BEP 48: periodic tracker scrape; skip confirmed dead-swarm announce URLs
- BEP 52 (phases 1–2/6): detect v1/hybrid/v2, parse peers' v2 handshake capability; accept hybrid torrents/magnets; reject pure-v2 cleanly
- Lazy piece storage: on-demand piece read/write/verify workers with idle terminate
- Peer.ConnectionManager: outbound dial orchestration with per-family throttle
- Mid-download session checkpoint (bitfield + counters every 30s)
- Magnet
x.pepeers kept as live dial candidates after metadata fetch - Developer quality gate: warnings-as-errors compile, Dialyzer, and all enabled Credo checks at every priority via
mix quality
Changed
Magnet.Connectionsources its BEP 3 choke/unchoke/interested wire ids fromPeer.Constinstead of a local copy- CI publish workflow renamed to
build-and-publish, gated to runmix hex.publishon semver tag pushes
Fixed
- NAT-PMP and UPnP passive UDP receive handling now decodes the datagram payload instead of the source-address tuple
- Global 2 MiB recv-buffer ceiling now bounds every wire frame's declared length, not only the six previously-capped ids
- Per-connection stall watchdog disconnects peers that trickle a declared frame length in forever instead of completing it
- Declared bitfield/piece frame lengths capped ahead of buffering, closing a memory-amplification gap alongside the existing LTEP/BEP 52 caps
- Tracker
startedis now resent on the first announce of every session (e.g. after a cold restart), not only once per info hash ever
0.5.1 - 2026-07-22
Changed
- README: single-row shields.io badges at the top for release, Hex, HexDocs, changelog, source, Web UI, and macOS downloads
0.5.0 - 2026-07-22
Added
- BEP 9/10 (magnet + ut_metadata): magnet URI parsing,
download_magnet/1, LTEP extension protocol, metadata fetch/serve, magnet bootstrap and fetcher supervisors - BEP 11 (ut_pex): peer exchange encode/decode, ingest, and broadcast over LTEP
- BEP 32 (IPv6 DHT): separate IPv4/IPv6 routing tables, compact node/peer codecs, dual-stack KRPC and lookup
- BEP 42: secure DHT node ID generation from the local IPv4 address
- BEP 55 (ut_holepunch): rendezvous/connect/forward hole-punch messages over LTEP;
Peer.Holepunch.Storefor punch state - DHT (BEP 5): routing-table persistence across restarts, dual-stack socket wiring, runtime node-id path fix
- NAT traversal: NAT-PMP and UPnP IGD port-mapping clients, STUN mapping-behaviour detection,
NAT.PortMapperorchestrator - MSE/PE: Message Stream Encryption handshake and RC4 transport layer over
Peer.Transport - uTP (BEP 29): zombie-connection hardening and BEP 29 off-spec bug fixes
- Peer discovery/dial:
Peer.Endpoints, sticky dial backoff with churn and hard-fail escalation,Peer.DialStatsper address-family outcomes - Acceptor: global IPv4/IPv6 address helpers,
IpCacheperiodicgetifaddrssnapshot via:persistent_term, dual-stack listen logging; LTEP handshakes advertise listen port and global addresses - README: BEP 9, 10, 11, 32, 42, and 55 rows in Supported BEPs table;
download_magnet/1in public API table
Changed
- Peer I/O: MSE/PE encryption integrated into the peer transport and sender receive path
- Peer dial: immediate-drop churn backoff, endpoint PID lookup via
Peer.Endpoints.get_pid/3
Fixed
- DHT node ID persistence file path resolved at runtime instead of compile time
Notes
- Started at runtime (via
ElixirTorrentApplication): TCP acceptor (withIpCache),PeerDiscovery, magnet fetcher/bootstrap, andPeer.Holepunch.Store - Implemented and compiled, not started in supervision: mainline DHT server, uTP dispatcher,
NAT.PortMapper,Peer.Endpoints,Peer.DialBackoff, andPeer.DialStats— wiring deferred to a future release - Default peer acquisition remains TCP + tracker (plus magnet metadata bootstrap); DHT announce, uTP fallback dials, NAT mapping, and full hole-punch initiation require the dormant services above
0.4.0 - 2026-07-08
Added
- BEP 29 (uTP):
Peer.Transportover TCP and uTP; uTP packet stack with LEDBAT; connection lifecycle and dispatcher; dial backoff and endpoint registry; outbound TCP-first dial with uTP fallback; inbound uTP handshake entry - BEP 5 (DHT): Mainline DHT KRPC node (ping, find_node, get_peers, announce_peer); k-bucket routing table with bootstrap routers; iterative lookup and announce token workflow; DHT and uTP share one UDP socket via
DHT.send_udp/3 - README: BEP 5 (Mainline DHT) row in Supported BEPs table
Changed
- Peer I/O:
Peer.Senderis the sole socket owner overPeer.Transport;Peer.Receiverremoved; post-handshake socket handoff usesSender.activate/1
Fixed
- Torrent progress and
downloaded/leftcounters reconciled with the on-disk bitfield - Piece pipeline: idempotent piece statistics init, stale
:processingcleanup, exclude-aware picker - In-flight piece download recovery when subpiece requests stall (
waiting: []no longer finishes early) - Incoming piece block bounds validation;
Peer.log_id/1for safe timeout logging - Download scheduler: parallel piece picking with availability checks, resume handoff before controller scheduling, per-piece interested peer assignment
Notes
- uTP (BEP 29) and DHT (BEP 5) are implemented and compiled in, but runtime activation (supervision wiring) is deferred to a future release; the default peer-acquisition/download path remains TCP + tracker-based.
0.3.0 - 2026-06-28
Download location
ElixirTorrent.download/2accepts an optional:download_dirkeyword — base directory for downloaded files (defaults toFile.cwd!/0)- Session snapshots remain under
{File.cwd!()}/.elixir_torrent/state/regardless of:download_dir remove/2withdelete_data: trueremoves files from the torrent's download directory
File layout
- Multi-file torrents whose files share no common top-level folder are written under a directory named after the torrent (sanitized
info.name) - Multi-file torrents that already use a shared root folder (e.g.
dir/a.bin,dir/b.bin) keep their original paths - Single-file torrents are written directly into the download root
Docs
- README About section with project background
0.2.0 - 2026-06-11
Session persistence
- Saved session state under
.elixir_torrent/state/{info_hash}.term(relative toFile.cwd!/0) - On
download/1, an existing session is loaded and the bitfield is verified against disk before resuming remove/2deletes the session file;delete_data: truealso removes downloaded files
Graceful shutdown API
stop_and_serialize/1— stop piece downloads, disconnect peers (BEP 3), send trackerevent=stopped, persist session, then stop the torrent processstop_all_and_serialize/0— same for every active torrentlist/0— returns info hashes for all running torrent processes
Peer disconnect
- Peers receive BEP 3 cancel / not interested / choke before TCP connections close
- Used during shutdown so peers are notified cleanly
0.1.2 - 2026-06-09
ElixirTorrent.list_files/1— file list with per-file download progressElixirTorrent.remove/2— stop a torrent; optionaldelete_data: trueremoves files from disk- Requires Elixir
~> 1.20
0.1.1 - 2026-02-22
- Published HexDocs for the public API (
ElixirTorrent,Torrents) ElixirTorrent.stats/2documented as the preferred way to read runtime stats
0.1.0 - 2026-02-22
First public release — BitTorrent client engine publishable as a Hex dependency.
Public API
ElixirTorrent.download/1— start a download from a.torrentfile on diskElixirTorrent.stats/2— poll name, speeds, and progress for a running torrent- Escript entrypoint for CLI usage
Protocol & networking
- Peer wire protocol (BEP 3) with choking, rarest-first piece selection, and endgame mode
- Fast Extension (BEP 6) —
allowed_fast, reject on choked requests - IPv6 tracker peers (
peers6) and dual-stack listen sockets - Multi-homed HTTP announce (BEP 7) over IPv4 and IPv6
- HTTP and UDP trackers (BEP 15), compact peer lists (BEP 23)
Reliability
- Improved choke recovery, piece availability tracking, and tracker announce handling