About KiteTorrent¶
A pure-Kotlin BitTorrent engine for every target. KiteTorrent is a Kotlin Multiplatform reimplementation of libtorrent (Rasterbar): no JNI, no cinterop, no native blob. One Kotlin codebase handles bencoding, hashing, .torrent and magnet parsing, the wire protocol, the piece picker, the DHT, trackers, and the live download and seed session. It runs on Android, iOS, the JVM, and, for the pure core, the browser.
How it is put together¶
The protocol stack is written in Kotlin and runs as the same .kt everywhere Kotlin runs. There is no native binary to build per platform and no bridge layer to keep in sync.
BitTorrent is networking, concurrency and disk I/O, and there is no stdlib-only way to open a socket. So the library splits along that line:
:kitetorrentis the pure core: bencoding, crypto,.torrentparsing, the wire-protocol codec, the piece picker, the DHT data structures, settings, alerts. Its only dependency iskotlin-stdlib, and it targets Android, iOS, JVM, and JS.:kitetorrent-sessionis the live engine: the session loop, sockets, disk, trackers, the DHT node, and µTP. It is built onkotlinx.coroutines,ktor-network, andkotlinx-io, and targets Android, iOS, and JVM. There is no JS target here, because browsers have no raw TCP/UDP sockets.
The core never pretends to do something it cannot. See Platform support for the full matrix.
Current status¶
KiteTorrent is pre-1.0 and actively developed. It downloads, seeds, and starts from magnet links, end-to-end. The JVM path runs 567 tests: 462 in the core's common suite and 105 in the session module. 566 pass. Socks5UdpTest.proxiedUdpSocketWrapsAndUnwrapsThroughRelay times out reproducibly. The suite includes loopback integration tests over real sockets:
- A TCP download from a seeder
- Two KiteTorrent engines exchanging a torrent, in plaintext and with MSE forced, then doing it again entirely over µTP
- A
ut_metadatamagnet metadata fetch, and a full magnet download between two engines - A v2-only torrent exchanged between two engines
- SOCKS4, SOCKS5, SOCKS5-over-UDP and HTTP-CONNECT proxy runs, including a tunnelled tracker announce
- A real-file
FileDiskIoround-trip - Adversarial runs against a scripted peer that withholds blocks, proving the end-game duplication,
cancel, and snubbing machinery recover
Correctness is checked against published reference data:
- FIPS vectors for SHA-1/256/512
- RFC 8032 for ed25519
- Real torrent files, with info-hashes cross-checked against an independent implementation
- BEP golden bytes for the DHT, ut_pex, and the UDP tracker
What end-to-end means here
Two engines, started cold, can hand a torrent to each other over real sockets and verify every piece against its hash. That holds for both TCP and µTP, and for both .torrent and magnet starts. These are local Gradle runs. .github/workflows/ contains only docs.yml, so nothing runs the test suites on push.
Known defects¶
These ship today and fail silently. If you are building against KiteTorrent, read this section first.
- HTTP and HTTPS tracker announces do nothing unless you pass an
HttpTracker.KiteTorrentEngine'shttpTrackerparameter defaults tonull, and both announce paths are null-safe calls. Nothing throws, nothing is logged, no alert is posted. Most public torrents list HTTP trackers, so you see an engine that finds no peers and gives no reason. See Getting started. FileDiskIocannot resume or seed from data already on disk. ItscheckExistingFiles()returns an all-falsearray, and the recheck thataddTorrentruns internally reads exactly that array. Point it at a complete copy and the session reports zero pieces and downloads the whole torrent again over the existing files. Callsession.recheck(full = true)afteraddTorrent. See Seeding.progress()counts pieces, not bytes, and does not exclude filtered pieces. Any file set toIGNOREpriority meansprogress()can never reach1.0, andisSeeding()never becomes true either.FileDiskIo'sdispatcherparameter defaults toDispatchers.Default, putting blocking file syscalls on the CPU dispatcher. PassDispatchers.IO.anonymous_modedoes not force a proxy. It skips the inbound listen socket, skips UPnP and NAT-PMP, and randomizes the per-torrent peer id. Nothing refuses to dial when no proxy is configured.
Current limits¶
These are not defects. They are gaps you may hit, and work that is planned:
- No threaded disk cache.
DiskIois a suspending interface andFileDiskIowrites straight through to the file handle. There is no batching and no write-back cache. - A partial alert catalogue. 51 concrete alert classes exist, so some events have no alert you can observe.
- No path-MTU probing for µTP. The MSS starts at 1400 and only shrinks: 64 bytes after two consecutive timeout rounds, down to a floor of 1212. It never grows back.
- Thin test coverage for the real-file disk layer.
FileDiskIoTesthas one test, and every engine-level integration test usesInMemoryDiskIo. That is how thecheckExistingFilesdefect above went unnoticed. - A failing proxy test.
Socks5UdpTesttimes out after 15 seconds inUdpSocket.receiveagainst its own loopback relay, on every run. - No CI workflow that runs the tests.
Reporting bugs¶
If a download stalls, a torrent fails to parse, or a peer interaction misbehaves, open an issue with enough detail to reproduce it: the .torrent or magnet link, the engine settings, and the alerts you observed. Networking code keeps its parse and build logic in pure, testable functions. Most fixes can therefore land as a unit test plus a regression test at the engine integration level.
Contributing¶
Contributions are welcome. Check the GitHub repository for open issues. Test additions and protocol fixes are especially valuable. The component-by-component map for contributors is PORTING_STATUS.md. The API reference is the place to confirm a signature before you build against it.
License and credits¶
KiteTorrent is a derivative work of libtorrent by Arvid Norberg and contributors, distributed under the BSD-3-Clause license. The original copyright notices are kept per file wherever code was translated directly. The Kotlin code and the architecture around it use the same BSD-3-Clause terms.
This is an independent reimplementation. It is not affiliated with or endorsed by the libtorrent project.
Acknowledgements¶
- libtorrent by Arvid Norberg and contributors: the reference implementation, pinned to RC_2_0 (2.0.12)
- The BEP authors: the BitTorrent Enhancement Proposals that define the protocol and its extensions
- The FIPS and RFC 8032 test vectors, and the BEP golden bytes, that let this project check itself against published results