Tetherand

Bundle combinations

The native libraries that power Tetherand's hops are grouped into six independent bundles. Each release includes every possible combination of these bundles as its own zip file, with paired SHA-256 + SHA3-256 sidecars, so users can pick the slimmest bundle that gives them what they want.

The six groups

GroupContentsApproximate size
wglibtetherand_wg.so (WireGuard via BoringTun)~2.7 MB
torlibtetherand_tor.so (embedded Arti)~9.0 MB
nymlibtetherand_nym.so (Nym mixnet JNI surface)~600 KB
ptlibtetherand_pt.so (obfs4 + meek + webtunnel inline)~2.7 MB
ptslibconjure_client.so (and snowflake once upstream catches up)~16 MB
sdrlibrtlsdr.so + libhackrf.so + libusb1.0.so~1.0 MB

Six independent groups means 26 = 64 combinations including the empty one.

Naming

Each combination zip is named with its group codes sorted alphabetically and joined with +:

tetherand-libs-none.zip                       # empty (manifest only)
tetherand-libs-wg.zip                         # WG only
tetherand-libs-tor+wg.zip                     # WG + Tor
tetherand-libs-nym+pt+pts+sdr+tor+wg.zip      # everything

What's inside a zip

$ unzip -l tetherand-libs-tor+wg.zip
Archive:  tetherand-libs-tor+wg.zip
        Length      Date    Time    Name
        --------- ---------- -----   ----
              420  01-01-2026 00:00   MANIFEST.txt
          2677840  01-01-2026 00:00   lib/arm64-v8a/libtetherand_pt.so
          9472040  01-01-2026 00:00   lib/arm64-v8a/libtetherand_tor.so

The MANIFEST.txt lists which groups are present, the on-device target path (lib/arm64-v8a/<file>), and per-file SHA-256 hashes. Each zip also has its own .sha256 and .sha3-256 sidecar files covering the zip as a whole.

Picking the right bundle

Use the smallest bundle that contains every hop you intend to configure. As a rough guide:

The empty bundle (tetherand-libs-none.zip) is included so a verifier can prove that a given hash corresponds to an explicit "this contains nothing" state. Useful for build reproducibility checks where the verifier wants to confirm the bundle script's behaviour on edge cases.

Master index

dist/bundles/COMBOSUMS.txt lists every combination zip with both its SHA-256 and SHA3-256:

SHA256     <hex>  tetherand-libs-none.zip
SHA3-256   <hex>  tetherand-libs-none.zip
SHA256     <hex>  tetherand-libs-wg.zip
SHA3-256   <hex>  tetherand-libs-wg.zip
…

Use this for batch verification — awk + shasum -a 256 -c to walk the whole list.

Installing a custom bundle

To use a bundle other than the full one, take the empty APK from a custom build (make apk after deleting unwanted .so files from android/app/src/main/jniLibs/arm64-v8a/) and extract the chosen combination zip into the same lib/arm64-v8a/ directory:

unzip -o tetherand-libs-tor+wg.zip -d android/app/src/main/jniLibs/
make apk

This regenerates the APK with the desired native libraries and emits fresh hash sidecars covering the new contents.

Why so many combinations

Each native library has independent integrity properties: a compromised libtetherand_tor.so doesn't tell you anything about the state of libtetherand_nym.so. Publishing all combinations means a user who only wants Tor can verify against the wg+tor+pt combo and not be exposed to a hypothetical issue in Nym or SDR libraries they don't plan to use. The "all combinations" approach defends against trojaned-library substitution attacks under a different threat model than the unified APK does.