tools/arti-build/README.md
This directory contains the build scripts and source files for compiling the custom Arti (Tor in Rust) library for Android.
bitchat-android uses a custom-built Arti library instead of Guardian Project's outdated arti-mobile-ex AAR. This provides:
The pre-built .so files are committed to the repo, so you don't need to build unless you want to:
tools/arti-build/
├── README.md # This file
├── build-arti.sh # Main build script (clones Arti, builds .so files)
├── ARTI_VERSION # Pinned Arti version tag (e.g., arti-v1.7.0)
├── Cargo.toml # Rust package configuration
├── src/
│ └── lib.rs # JNI wrapper (Rust -> Kotlin/Java bridge)
└── .arti-source/ # [GITIGNORED] Cloned official Arti repo
app/src/main/jniLibs/ # [COMMITTED] Pre-built native libraries
├── arm64-v8a/
│ └── libarti_android.so (~5.3MB)
└── x86_64/
└── libarti_android.so (~6.2MB)
Rust toolchain with Android targets:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add aarch64-linux-android x86_64-linux-android
cargo-ndk for Android cross-compilation:
cargo install cargo-ndk
Android NDK 25+ (for 16KB page size support):
# Via Android Studio SDK Manager, or:
$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager "ndk;27.0.12077973"
# Set environment variable
export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/27.0.12077973"
cd tools/arti-build
# Build both architectures (arm64 + x86_64 for emulator)
./build-arti.sh
# Build ARM64 only (smaller, for production releases)
./build-arti.sh --release
# Clean rebuild (re-clone Arti source)
./build-arti.sh --clean
The script will:
ARTI_VERSIONcargo ndk.so files to app/src/main/jniLibs/After building, verify the libraries:
# Check file sizes
ls -lh ../../app/src/main/jniLibs/*/libarti_android.so
# Verify JNI symbols are exported
nm -gU ../../app/src/main/jniLibs/arm64-v8a/libarti_android.so | grep Java_org_torproject
# Verify 16KB page alignment
readelf -l ../../app/src/main/jniLibs/arm64-v8a/libarti_android.so | grep LOAD
# Look for: Align 0x4000 (16KB)
Check available versions:
git ls-remote --tags https://gitlab.torproject.org/tpo/core/arti.git | grep arti-v
Update the version file:
echo "arti-v1.8.0" > ARTI_VERSION
Rebuild from scratch:
./build-arti.sh --clean
Test the build:
cd ../..
./gradlew clean assembleDebug
./gradlew installDebug
# Enable Tor in app and verify it works
Commit the new libraries:
git add app/src/main/jniLibs/ tools/arti-build/ARTI_VERSION
git commit -m "chore: update Arti to v1.8.0"
The src/lib.rs file implements a JNI bridge between Kotlin and Rust:
Kotlin (ArtiNative.kt)
↓ JNI
Rust (lib.rs)
↓
Arti Client (TorClient)
↓
SOCKS5 Proxy (localhost:9060)
Exported JNI Functions:
getVersion() - Returns Arti version stringsetLogCallback(callback) - Registers log listener for bootstrap progressinitialize(dataDir) - Creates Tokio runtime and TorClientstartSocksProxy(port) - Starts SOCKS5 proxy on specified portstop() - Stops SOCKS proxy (TorClient is reused)Key Design Decisions:
TorClient persists across stop/start cycles (fixes Nov 2024 toggle bug)GlobalRef callbackEdit Cargo.toml to customize Arti features:
[dependencies]
arti-client = {
path = "../crates/arti-client",
default-features = false,
features = [
"tokio", # Required: async runtime
"rustls", # Required: pure Rust TLS (no OpenSSL)
"compression", # Optional: directory compression
"bridge-client", # Optional: Tor bridge support
"onion-service-client", # Optional: .onion site support
"static-sqlite" # Required: bundled SQLite
]
}
| Configuration | arm64-v8a | x86_64 | Total | APK Size |
|---|---|---|---|---|
| Guardian Project AAR | - | - | ~140 MB | ~150 MB |
| Custom (both arch) | 5.3 MB | 6.2 MB | 11.5 MB | ~15 MB |
| Custom (ARM-only) | 5.3 MB | - | 5.3 MB | ~10 MB |
28x size reduction vs Guardian Project implementation.
cargo install cargo-ndk
export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/27.0.12077973"
rustup target add aarch64-linux-android x86_64-linux-android
Check available versions:
git ls-remote --tags https://gitlab.torproject.org/tpo/core/arti.git | grep arti-v | tail -10
--release flagstrip = true in Cargo.toml [profile.release]