Clears all four open Dependabot alerts against rustls 0.23.45 in tools/arti (ECH rejection leaking buffered application data, unauthenticated ServerHello key-exchange poisoning, session Debug formatting exposing master secrets and resumption PSKs, and unbounded Brotli certificate-decompression memory before authentication). The lock also re-wires tempfile and the strum-dependent crate to the syn 3 and getrandom 0.3 versions cargo kept when pruning. All nine native libraries are rebuilt from this one source and lock: four Android ABIs, desktop Linux x64/arm64, Windows x64, and both macOS flavors (the macOS dylibs came out of the amber-macos VM, rustup and the pinned 1.98.1 toolchain installed there).
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-ndkand the NDK revision inbuild.sh - desktop:
cargo install cargo-zigbuildand zig (pip install ziglangis 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
- Bump
arti-clientandtor-rtcompatinCargo.toml(andversion, which tracks the Arti release: arti 2.7.0 = crates 0.47.0). cargo update -p arti-client -p tor-rtcompatto refreshCargo.lock.- 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.