Skip to content

Building from Source

pq-cybarg edited this page May 29, 2026 · 1 revision

Building from Source

This page walks through a full rebuild of Tetherand from a fresh clone, including every native library.

Toolchain prerequisites

Tool Version Used for
Android NDK r26 or newer Cross-compiling Rust + C libs to arm64-android
Android SDK API 36 platform apksigner, zipalign, adb
Rust 1.85 or newer Workspace at relay/
aarch64-linux-android target rustup target add aarch64-linux-android Cross-compile output
Go 1.22 or 1.23 (not 1.26 yet) Conjure / Snowflake upstream binaries
cmake 3.20+ librtlsdr / libhackrf / libusb
Java OpenJDK 21 Gradle wrapper expects 21+

Set NDK_HOME (or ANDROID_NDK_HOME) to the NDK root before any native build:

export NDK_HOME=$HOME/Library/Android/sdk/ndk/26.3.11579264

(macOS path shown; Linux path is similar under ~/Android/Sdk/ndk.)

Quick path — debug APK with all native libs

make native-all   # cross-compiles wg, tor, nym, pt-bridge, librtlsdr stack
make apk          # repackages the APK + emits hash sidecars
make install      # adb install + pre-grants VPN consent
make smoke-device # UiAutomator walk

The full bundle APK lands in bin/tetherand.apk at around 59 MB. Hash sidecars (tetherand.apk.sha256 and tetherand.apk.sha3-256) and a master bin/SHASUMS.txt are emitted by make hashes, which make apk runs automatically.

Individual native libraries

If you only need one:

make native-wg       # libtetherand_wg.so (WireGuard via BoringTun)
make native-tor      # libtetherand_tor.so (Arti)
make native-nym      # libtetherand_nym.so (NymVPN JNI surface)
make native-pt       # libtetherand_pt.so (obfs4/meek/webtunnel) + libconjure_client.so
make native-rtlsdr   # librtlsdr.so + libhackrf.so + libusb1.0.so

Each script lives under scripts/build-*-android.sh. They all apply --remap-path-prefix so that build-time absolute paths from your workstation don't end up in the shipped .rodata panic strings.

Signed release APK

For a production-style release:

make release-signed

This runs:

  1. gradle assembleRelease to produce the unsigned APK.
  2. zipalign -p 4 for page-aligned native libraries.
  3. apksigner sign with SHA256-RSA2048 against the Android v2 signing scheme.

The script gates the signing step against an out-of-repo allow-list of approved certificate DNs (~/.tetherand-signing-allow). The stock CN=Android Debug, O=Android, C=US debug subject is always allowed without an entry. To use a production keystore, generate one with keytool -genkeypair -alias tetherand-prod -keystore tetherand-prod.jks -keyalg RSA -keysize 4096 -validity 36500 -dname "CN=Tetherand, O=pq-cybarg, C=US" and then `echo "CN=Tetherand, O=pq-cybarg, C=US"

~/.tetherand-signing-allow`.

Bundle combinations

scripts/bundle-combinations.sh generates every subset of the six native-library groups (wg, tor, nym, pt, pts, sdr) as its own zip with paired SHA-256 + SHA3-256 sidecars under dist/bundles/. Details at Bundle Combinations.

Known build snags

Symptom Cause Fix
ld.lld: error: unable to find library -lsqlite3 during Tor build Arti's tor-dirmgr uses rusqlite and the NDK sysroot has no libsqlite3 Already pinned in relay/tor/Cargo.toml via rusqlite = { features = ["bundled"] }; if you're patching it, keep the bundled feature.
nym-noise fails with a type-inference error Upstream nym-noise 1.20.4 has a closure parameter that needs annotation under rustc 1.83+ TETHERAND_NYM_SDK=0 make native-nym (default; ships the JNI surface only). The with-sdk feature is opt-in.
ld.lld: error: unable to find library -lusb-1.0 during rtlsdr build rtlsdr's CMakeLists hardcodes -lusb-1.0 and ignores LIBUSB_LIBRARIES The script in scripts/build-rtlsdr-android.sh already passes -L$WORK/libusb/build via CMAKE_SHARED_LINKER_FLAGS.
Snowflake build fails with link: github.com/wlynxg/anet: invalid reference to net.zoneCache Upstream wlynxg/anet uses a Go internal symbol that moved in Go 1.26 Downgrade Go to 1.22 or 1.23 for the snowflake build; or wait for upstream to fix. v0.1 skips snowflake.

Test suite

make test       # all Rust workspace unit tests
cd android && ./gradlew :app:testDebugUnitTest   # Kotlin unit tests

The Rust workspace ships unit tests for the codec, the bridge parser, the obfs4 cert decoder, the perplexity rule, the prompt- injection regex, and the provenance checker. The Kotlin unit tests cover the geohash6 implementation.

Integration tests against the real Tor network live behind #[ignore] — run explicitly:

cd relay && cargo test -p tetherand-tor --test live_probe -- --ignored

The probe dials check.torproject.org:443 and an .onion service. Requires internet egress to the Tor network from the test runner.

Clone this wiki locally