Files
Amber/tools/arti
greenart7c3 d8e4caad67 Fix Android Studio's inspection findings and turn off its spell check
Android Studio's batch inspections reported about 800 problems besides
spelling, which the project profile now leaves off (it mostly flagged
the translations).

Real bugs among them: the desktop's strings files had empty strings
where Android has translations (German, Spanish, Brazilian Portuguese,
Chinese), showing blank text; and IS_FDROID_BUILD, which the F-Droid
workflow flips with sed, was read as a constant the IDE wanted to
simplify away, so it is now read through BuildFlavorChecker.isFdroidBuild().
Account loads in coroutines (relay, intent and UI paths) and logout no
longer block a thread with runBlocking; the blocking ContentProvider
path and the desktop lock say why they do.

Also remove what was dead (the drawable IconRow overload, PermissionCard,
ConfirmationToastMsg, the desktop OkHttpWebSocket.Builder and
DeleteAfterType.screenCode, the QR drawer's zero corner radius math and
the strings only they used), move to the Duration overloads, give
subject-less when blocks their subject, drop redundant opt-ins, suspend
modifiers, qualifiers and SAM constructors, fix broken KDoc links, take
the ktlint and KSP plugin versions from the catalog, strip editor
metadata from the SVGs, align the Markdown tables and fix real grammar
and spelling in comments, docs and three English strings.
2026-10-26 17:28:06 -03:00
..

Arti (built-in Tor)

Amber's built-in Tor runs Arti (Tor in Rust) in-process through a small JNI wrapper, src/lib.rs. The wrapper starts a Tor client, puts a SOCKS5 proxy on 127.0.0.1 in front of it and reports bootstrap progress; Kotlin's TorManager drives it through com.greenart7c3.nostrsigner.tor.ArtiNative.

The wrapper is adapted from Amethyst's tools/arti-build (https://github.com/vitorpamplona/amethyst, © 2025 Vitor Pamplona, MIT License).

Prebuilt libraries

The libraries are built once with build.sh and committed; CI does not build Rust.

App Path Built with
Android (free, benchmark) app/src/tor/jniLibs/{arm64-v8a,armeabi-v7a,x86_64,x86}/libamber_arti.so ./build.sh android (cargo-ndk, NDK pinned in build.sh)
Desktop Linux desktop/appResources/linux-{x64,arm64}/libamber_arti.so (glibc ≥ 2.28) ./build.sh desktop (cargo-zigbuild)
Desktop Windows desktop/appResources/windows-x64/amber_arti.dll ./build.sh desktop (cargo-zigbuild)
Desktop macOS desktop/appResources/macos-{x64,arm64}/libamber_arti.dylib ./build.sh macos, on a Mac

The Android ABIs must match splits.abi in app/build.gradle.kts. On the desktop, Compose ships the current OS's directory as app resources (appResourcesRootDir), and ArtiNative loads the library from compose.application.resources.dir.

Rebuilding

Rebuild after changing src/lib.rs, Cargo.toml or Cargo.lock, and commit the regenerated libraries together with the source change. Every library has to come from the same source, so rebuild all of them.

Prerequisites: rustup (the compiler is pinned in rust-toolchain.toml; the script adds the targets), plus

  • android: cargo install cargo-ndk and the NDK revision in build.sh
  • desktop: cargo install cargo-zigbuild and zig (pip install ziglang is enough)
  • macos: a Mac with the Xcode command line tools (the link needs Apple's frameworks, which zig cannot provide)
./build.sh android desktop   # on Linux
./build.sh macos             # on a Mac, from a copy of this repository

The script checks that every library exports the JNI entry points and prints their SHA-256 hashes.

Reproducibility

Rebuilding from a fresh checkout (any path: build.sh remaps host paths out of the binaries) gives byte-identical libraries with the pinned toolchain and Cargo.lock. The APK packages libamber_arti.so unstripped (packaging.jniLibs.keepDebugSymbols in app/build.gradle.kts), so the copy in a release is the committed file itself:

unzip -p amber-arm64-v8a-vX.Y.Z.apk lib/arm64-v8a/libamber_arti.so | sha256sum
sha256sum app/src/tor/jniLibs/arm64-v8a/libamber_arti.so

AGP still strips the app's other native libraries with the NDK pinned in app/build.gradle.kts (ndkVersion), which the reproducibility Dockerfile installs too.

Patched dependency: saturating-time

Cargo.toml replaces saturating-time 0.5.0 (used by tor-netdoc) with the copy in patches/saturating-time. Upstream's find_limit halves its step down to 1ns and only stops on None, but Windows' SystemTime ticks in 100ns, so a 1-99ns checked_sub returns the same time instead of None and the loop never ends: Arti then hangs at 15% ("fetching a consensus") with one core pinned the first time it parses a consensus. The patch treats "no progress" like None (see the "Amber patch" comment). Drop it once a fixed release is out.

Updating Arti

  1. Bump arti-client and tor-rtcompat in Cargo.toml (and version, which tracks the Arti release: arti 2.7.0 = crates 0.47.0).
  2. cargo update -p arti-client -p tor-rtcompat to refresh Cargo.lock.
  3. Rebuild every library as above.

JNI surface

Kotlin (ArtiNative) Purpose
initialize(dataDir) Create the Tor client (state + cache under dataDir) and start the directory download in the background
isBootstrapped() / bootstrapProgressPermille() Readiness and download progress, polled by TorManager
startSocksProxy(port, username, password) / stopSocksProxy() Bind / release the SOCKS5 listener; the client stays alive. The listener only accepts RFC 1929 username/password auth with the given per-listener secret, which TorSocksAuthenticator hands to Java's SOCKS client
destroy() Drop the client so the next initialize starts over (used by restart)
setLogCallback(cb) / getVersion() Logging into Amber's log and version string

The JNI symbol names are derived from the Kotlin package and class (Java_com_greenart7c3_nostrsigner_tor_ArtiNative_*), so the Android and desktop copies of ArtiNative must keep that package and name.