Skip to content

Architecture

github-actions[bot] edited this page Aug 27, 2026 · 1 revision

Architecture

TypeBit is one no_std crate with a single dependency direction: the engine asks Host for every OS primitive, and nothing reaches back.

Layering

┌──────────────────────────────────────────────────────────┐
│  your app (UI, Kotlin/Swift/C#/Go, embedded RTOS)         │
└───────────────────────────┬──────────────────────────────┘
                            │ tick() / take_events() / add_*()
┌───────────────────────────▼──────────────────────────────┐
│  typebit::Engine                                          │
│   ├─ TorrentSession × N   (per-torrent state machine)     │
│   ├─ Dht                  (Kademlia: table, KRPC, lookups)│
│   ├─ DiskCache            (write-back + read-through LRU) │
│   ├─ UtpManager           (BEP-29 transport)              │
│   ├─ PortMapManager       (NAT-PMP / UPnP IGD)            │
│   ├─ LsdScheduler         (BEP-14 multicast announce)     │
│   └─ VerifyPool           (multi-core piece hashing)      │
└───────────────────────────┬──────────────────────────────┘
                            │ every primitive, non-blocking
┌───────────────────────────▼──────────────────────────────┐
│  typebit::Host  ◄── YOU IMPLEMENT THIS (or StdHost)      │
│   sockets · files · clock · RNG · DNS · HTTP              │
└──────────────────────────────────────────────────────────┘

The engine never touches the OS. Host supplies ~18 primitives; the engine drives hundreds of peers from one thread, so the methods must not block (tcp_recv/udp_recv return Err(Error::WouldBlock) when idle).

Module map

src/
  platform.rs   the Host trait + NetAddr/ConnId/DiskId — the only seam
  engine.rs     Engine: owns sessions, DHT, cache, uTP, portmap, LSD, pool
  session.rs    TorrentSession: one torrent's state machine (peers, pieces,
                trackers, web seeds, metadata fetch, choke passes)
  wire.rs       peer-wire codec (BEP-6/9/10/11), bounded frames
  metainfo.rs   v1/v2/hybrid torrent parsing + piece layout/verification
  dht.rs        Kademlia routing table + KRPC + lookups + peer store
  tracker.rs    HTTP + UDP (BEP-15) tracker clients
  trackerlist.rs built-in tracker list + community-list parsing
  lsd.rs        BEP-14 local peer discovery
  pex.rs        BEP-11 peer exchange
  utp.rs        BEP-29 uTP with LEDBAT
  leech.rs      anti-leech: reciprocity scoring, corrupt accountability,
                reputation store, bans. Depends ONLY on platform.
  ratelimit.rs  token-bucket rate limiting
  scheduler.rs  utility-driven piece scheduling
  picker.rs     stateless piece/block selection over scheduler utilities
  piece.rs      per-piece block tracker (in-flight/partial/have)
  disk_cache.rs coalesced write-back cache + read-through LRU
  verify.rs     pure verify_piece + optional worker pool
  receipt.rs    Ed25519 provable-download receipts
  socks.rs      SOCKS5 (RFC 1928/1929) client + proxied HTTP
  portmap.rs    NAT-PMP (RFC 6886) + UPnP IGD
  state.rs      session persistence codecs (binary + JSON)
  monitoring.rs swarm recoverability estimation
  links.rs      unified link parser (magnet/ed2k/thunder/qqdl/flashget/ipfs/kad/http)
  magnet.rs     magnet URI (BEP-9/53)
  bencode.rs    dependency-free bencode codec with depth/size limits
  crypto/       SHA-1/256/512, Ed25519, ChaCha20, MD4, base58/32/64, CSPRNG
  host_std.rs   [std] OS-backed Host implementation
  ffi.rs        [ffi] extern "C" bridge

The tick pipeline

Every Engine::tick() does, in order:

  1. Timers — flush the disk cache (bounded per tick), refresh DHT buckets, re-bootstrap if the table is tiny, re-announce on cadence.
  2. TCP pump — advance non-blocking connects (tcp_connect_done), complete SOCKS5 handshakes, feed peer sockets, drain outgoing buffers through the global + per-session token buckets.
  3. UDP pump — one datagram budget per tick: route DHT KRPC, UDP tracker responses, LSD announces, NAT-PMP/SSDP, uTP packets.
  4. Sessions — announce to trackers (HTTP sync / UDP async), drive metadata fetches, run choke/unchoke passes, issue piece requests, drain web-seed block fetches.
  5. Verify — hand assembled pieces to the pool (or verify inline), drain results, complete or punish pieces.
  6. Events — append to the events queue for take_events().

Everything is bounded per tick (datagram budget, cache flush budget, request pipeline), so one slow peer or a hostile LAN can never freeze the loop.

Key design decisions

  • One code path, two execution strategies — piece verification is a pure function; VerifyPool (threads under std) and inline (no_std) share it, so results are bit-identical.
  • Token buckets at the choke points — the global rate is enforced where bytes actually flow (peer send loop, request issuance), not in a separate accounting pass.
  • UDP is optional, never fatal — if udp_open fails, start() still succeeds; DHT/UDP-trackers degrade and the engine emits EngineEvent::Error { code: 0 }.
  • Anti-leech is behavior-first — client fingerprinting is a soft signal; bans and disconnects come only from measured misbehavior. leech depends only on platform, so it is unit-testable in isolation.
  • The picker is stateless — everything it needs is passed in (scheduler utilities, availability, peer bitfield, priorities, endgame flag), so scheduling logic is trivially testable.

Clone this wiki locally