Skip to content

libtorrent integration and Fennel binding ​

This document describes the libtorrent integration used by Space today. It is the authoritative reference for the libtorrent Fennel module exposed by src/lua_libtorrent.cpp.

overview ​

Space binds libtorrent-rasterbar into Lua/Fennel through package.preload["libtorrent"]. The binding is implemented in C++ and exposed as hyphen-case APIs.

Design goals:

  • expose a practical, script-friendly control surface for torrent/session operations
  • keep data model explicit (SessionHandle, AddTorrentParamsHandle, SessionParamsHandle)
  • support both offline and online integration tests
  • fail loudly when a capability is unavailable

build and availability ​

The module is conditionally compiled:

  • enabled build: real libtorrent implementation is exported
  • disabled build: module still exists with available = false and all API calls throw a clear error

Runtime fields:

  • libtorrent.available -> boolean
  • libtorrent.missing-reason -> string or nil

Missing dependency message:

  • libtorrent unavailable (install libtorrent-rasterbar-dev and rebuild)

naming conventions ​

All exposed API names are hyphen-case. Examples:

  • parse-magnet-uri
  • wait-for-alert
  • save-state-flags

No snake_case aliases are intentionally provided except explicit compatibility helpers we expose (for example parse-magnet-uri-dict).

object model ​

SessionHandle ​

Created by:

  • (libtorrent.Session opts)
  • (libtorrent.session [opts])

Lifecycle:

  • drop
  • is-closed

Primary responsibilities:

  • add/remove/find torrents
  • session/torrent controls
  • DHT operations
  • alert polling
  • status and completion waiting

AddTorrentParamsHandle ​

Created by parsing/loading helpers, for example:

  • parse-magnet-uri-params
  • load-torrent-file-params
  • load-torrent-buffer-params
  • load-torrent-parsed
  • read-resume-data / read-resume-data-params

Primary responsibilities:

  • mutable add-torrent parameters
  • serialization to resume/torrent buffers
  • table inspection with to-table

SessionParamsHandle ​

Created by:

  • session-params
  • read-session-params
  • ses:session-state

Primary responsibilities:

  • session bootstrap settings/ext-state
  • session params serialization roundtrip

top-level module api ​

factory and utility ​

  • Session(opts)
  • session([opts])
  • session-params([opts])
  • version()

torrent metadata and magnet ​

  • create-torrent(opts)
  • parse-magnet-uri(uri)
  • parse-magnet-uri-dict(uri)
  • make-magnet-uri(torrent-path)
  • make-magnet-uri-from-params(params-handle)
  • load-torrent-file(path)
  • load-torrent-buffer(buffer)

params/serialization helpers ​

  • parse-magnet-uri-params(uri)
  • load-torrent-file-params(path)
  • load-torrent-buffer-params(buffer)
  • load-torrent-parsed(parsed-table)
  • read-resume-data(buffer)
  • read-resume-data-params(buffer)
  • write-resume-data(params-handle)
  • write-resume-data-buf(params-handle)
  • write-torrent-file(params-handle)
  • write-torrent-file-buf(params-handle)
  • read-session-params(buffer[, flags])
  • write-session-params(session-params-handle[, flags])
  • write-session-params-buf(session-params-handle[, flags])

bencode helpers ​

  • bencode(value)
  • bdecode(buffer)

settings/metrics presets ​

  • default-settings()
  • high-performance-seed()
  • min-memory-usage()
  • session-stats-metrics()
  • find-metric-idx(name)

ed25519 / mutable item signing helpers ​

  • ed25519-create-seed()
  • ed25519-create-keypair(seed-hex)
  • sign-mutable-item(opts)
  • verify-mutable-item(opts)

operation/error helpers ​

  • operation-name(op-code)
  • libtorrent-category-name()
  • http-category-name()
  • socks-category-name()
  • upnp-category-name()
  • i2p-category-name()
  • system-category-name()
  • generic-category-name()
  • bdecode-category-name()

constants tables ​

  • torrent-flags
  • storage-modes
  • save-state-flags
  • dht-announce-flags

SessionHandle api summary ​

add/remove/find ​

  • add-torrent-file(opts)
  • add-info-hash(opts)
  • add-magnet-uri(uri[, opts])
  • add-torrent-params(opts)
  • get-torrents()
  • find-torrent(info-hash-hex)
  • remove-torrent(handle-id[, opts])

per-torrent control ​

  • pause-torrent(handle-id)
  • resume-torrent(handle-id)
  • set-torrent-download-limit(handle-id, value) / torrent-download-limit(handle-id)
  • set-torrent-upload-limit(handle-id, value) / torrent-upload-limit(handle-id)
  • set-torrent-max-connections(handle-id, value) / torrent-max-connections(handle-id)
  • set-torrent-max-uploads(handle-id, value) / torrent-max-uploads(handle-id)
  • set-torrent-piece-priorities(handle-id, priorities) / torrent-piece-priorities(handle-id)
  • set-torrent-file-priorities(handle-id, priorities) / torrent-file-priorities(handle-id)
  • force-reannounce(handle-id)
  • force-dht-announce(handle-id)
  • status(handle-id)
  • torrent-info(handle-id)
  • make-magnet-uri(handle-id)
  • wait-for-complete(handle-id[, opts])

session control/settings ​

  • apply-settings(settings)
  • get-settings()
  • pause() / resume() / is-paused()
  • session-status()
  • session-state([flags])

alerts ​

  • set-alert-mask(mask)
  • set-alert-queue-size-limit(limit)
  • wait-for-alert(timeout-ms)
  • pop-alerts()
  • post-session-stats()
  • post-dht-stats()
  • post-torrent-updates()

DHT operations ​

  • start-dht() / stop-dht() / is-dht-running()
  • add-dht-node({:host ... :port ...})
  • add-dht-router({:host ... :port ...})
  • dht-get-peers(info-hash-hex)
  • dht-announce({:info-hash ... :port ... :flags ...})
  • dht-live-nodes(node-id-hex)
  • dht-sample-infohashes({:host ... :port ... :target ...})
  • dht-get-item(target-hex)
  • dht-put-item(item-table) -> target hex
  • dht-get-mutable-item({:public-key ... [:salt ...]})
  • dht-put-mutable-item({:public-key ... :secret-key ... [:salt ...] :seq ... :item ...})
  • dht-direct-request({:host ... :port ... :request ... [:userdata ...]})

alert table enrichment ​

All alerts include base fields:

  • type, message, category, alert-type, timestamp-ms

The binding enriches several alert families with additional keys (when present), including:

  • add/session/state alerts
  • DHT stats alerts
  • immutable/mutable DHT item alerts
  • DHT put alerts
  • DHT get-peers/live-nodes/sample-infohashes replies
  • DHT direct response alerts

Callers should treat optional fields as optional and type-check before use. dht-direct-response-alert may include userdata when :userdata was set on dht-direct-request.

testing strategy ​

Tests are implemented in Fennel modules under assets/lua/tests/.

Primary suites:

  • tests.test-libtorrent-offline:main (fast enough for regular runs, no network requirement)
  • tests.test-libtorrent-online:main (real network roundtrip; intentionally slow)
  • tests.test-libtorrent:main (combined; includes online only when SPACE_LIBTORRENT_INCLUDE_ONLINE=1)

Typical commands:

  • SKIP_KEYRING_TESTS=1 XDG_DATA_HOME=/tmp/space/tests/xdg-data SPACE_DISABLE_AUDIO=1 SPACE_ASSETS_PATH=$(pwd)/assets ./build/space -m tests.test-libtorrent-offline:main
  • SKIP_KEYRING_TESTS=1 XDG_DATA_HOME=/tmp/space/tests/xdg-data SPACE_DISABLE_AUDIO=1 SPACE_ASSETS_PATH=$(pwd)/assets ./build/space -m tests.test-libtorrent-online:main
  • SKIP_KEYRING_TESTS=1 XDG_DATA_HOME=/tmp/space/tests/xdg-data SPACE_LIBTORRENT_INCLUDE_ONLINE=1 SPACE_DISABLE_AUDIO=1 SPACE_ASSETS_PATH=$(pwd)/assets ./build/space -m tests.test-libtorrent:main

known limitations ​

  • verify-mutable-item is not available in this environment's libtorrent binary (symbol declared in headers but not exported by the installed package). The binding throws a clear runtime error.
  • add-dht-router is ABI-dependent and throws a clear runtime error when unavailable in the active libtorrent ABI.

torrent cli tools ​

Space includes two Fennel CLI tools built on this binding:

  • tools.torrent-download
  • tools.torrent-upload

tools.torrent-download ​

Purpose:

  • download from magnet URI, v1 info-hash, or .torrent file
  • continue seeding until interrupted
  • resume across restarts

Run:

  • SPACE_DISABLE_AUDIO=1 SPACE_ASSETS_PATH=$(pwd)/assets ./build/space -m tools.torrent-download:main -- <magnet|info-hash|torrent-path>

Persistence:

  • ~/.local/share/space/libtorrent/<info-hash>/ (or $XDG_DATA_HOME/space/libtorrent/<info-hash>/)
  • includes torrent state metadata and downloaded data path

tools.torrent-upload ​

Purpose:

  • publish a file/directory as a torrent
  • print magnet URI + info-hash
  • seed until interrupted
  • reuse previous publish metadata when source is unchanged

Run:

  • SPACE_DISABLE_AUDIO=1 SPACE_ASSETS_PATH=$(pwd)/assets ./build/space -m tools.torrent-upload:main -- <source-path>

Persistence:

  • ~/.local/share/space/libtorrent/uploads/<source-id>/ (or $XDG_DATA_HOME/space/libtorrent/uploads/<source-id>/)
  • contains published.torrent and state.json

Current source-change detection:

  • metadata-based fingerprint over path/type/size/mtime/permissions
  • this avoids full content re-hash on every start, but can miss edge cases where file bytes change without metadata change

bencode container wrappers ​

Lua tables are ambiguous between list and dictionary semantics for edge cases (especially empty containers). The module exposes explicit wrappers for unambiguous bencoding:

  • libtorrent.list(table) -> force list encoding (supports empty list)
  • libtorrent.dict(table) -> force dictionary encoding

Use these wrappers when a protocol payload requires exact container shape.

source of truth ​

For exact function names and behavior, use:

  • src/lua_libtorrent.cpp
  • assets/lua/tests/test-libtorrent-offline.fnl
  • assets/lua/tests/test-libtorrent-online.fnl

See also ​