1
0
forked from GRIN/grim

Build 53: Windows + Android support with a per-platform Nym sidecar

Ship the bundled nym-socks5-client on Windows and Android, not just Linux:
- sidecar.rs resolves the binary per platform — nym-socks5-client.exe on
  Windows; on Android the sidecar rides in the APK jniLibs as
  libnym_socks5_client.so and is launched from the native-library dir (the
  one exec-allowed path), located via NATIVE_LIBS_DIR.
- Restore a vendored, statically-linked OpenSSL. Upstream Grim got this from
  arti's static feature; dropping arti for Nym took it with it, which broke
  Android/cross builds (no system OpenSSL for the target) and left desktop
  dynamically linked to libssl. Inert on Windows/macOS (SChannel/Security.fw).
- android.sh bundles the sidecar into jniLibs per ABI; scripts/nym-android.sh
  cross-builds it. Onboarding copy: Tor -> the Nym mixnet.

Verified end to end on an x86_64 emulator: the sidecar extracts, launches,
initialises, and opens the mixnet SOCKS5 proxy on 127.0.0.1:1080.
This commit is contained in:
2ro
2026-06-13 19:57:36 -04:00
parent 0f46145f46
commit 329067e1c2
6 changed files with 88 additions and 3 deletions
Generated
+11
View File
@@ -3265,6 +3265,7 @@ dependencies = [
"nostr-relay-pool",
"nostr-sdk",
"num-bigint 0.4.6",
"openssl",
"parking_lot 0.12.5",
"pin-project",
"qrcode",
@@ -6118,6 +6119,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-src"
version = "300.6.1+3.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.111"
@@ -6126,6 +6136,7 @@ checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
+7
View File
@@ -87,6 +87,13 @@ bytes = "1.11.0"
hyper-socks2 = "0.9.1"
hyper-proxy2 = "0.1.0"
hyper-tls = "0.6.0"
## native-tls (via hyper-tls) uses OpenSSL on Linux/Android. Upstream Grim got a
## vendored, statically-linked OpenSSL for free through arti's `static` feature;
## dropping arti for Nym took that with it, breaking Android/cross builds (no
## system OpenSSL for the target) and leaving desktop dynamically linked to
## libssl. Restore the vendored build so every target is self-contained. Inert on
## Windows/macOS, which use SChannel / Security.framework instead of OpenSSL.
openssl = { version = "0.10", features = ["vendored"] }
async-std = "1.13.2"
uuid = { version = "0.8.2", features = ["v4"] }
num-bigint = "0.4.6"
+16
View File
@@ -48,6 +48,22 @@ function build_lib() {
sed -i -e 's/"cdylib","rlib"]/"rlib"]/g' Cargo.toml
rm -f Cargo.toml-e
# Bundle the Nym SOCKS5 sidecar beside libgrim.so. Named lib*.so so Android
# ships it in the APK's jniLibs and extracts it to the native-library dir —
# the only exec-allowed location for a child process (manifest needs
# extractNativeLibs=true). Built from the Nym workspace; see scripts/nym-android.sh.
[[ $1 == "v7" ]] && nym_target=armv7-linux-androideabi
[[ $1 == "v8" ]] && nym_target=aarch64-linux-android
[[ $1 == "x86" ]] && nym_target=x86_64-linux-android
nym_bin="${NYM_DIR:-../nym/target}/${nym_target}/release/nym-socks5-client"
if [ -f "${nym_bin}" ]; then
cp "${nym_bin}" "android/app/src/main/jniLibs/${arch}/libnym_socks5_client.so"
echo "bundled Nym sidecar: jniLibs/${arch}/libnym_socks5_client.so"
else
echo "WARN: Nym sidecar missing at ${nym_bin} — APK will have NO mixnet sidecar"
success=0
fi
}
### Build application
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# Cross-compile the bundled Nym SOCKS5 sidecar (nym-socks5-client) for Android.
# scripts/android.sh copies the result into the APK's jniLibs as
# libnym_socks5_client.so so Goblin can launch the mixnet client on-device.
#
# Usage: NYM_SRC=../nym scripts/nym-android.sh [v7|v8|x86|all]
# NYM_SRC path to the Nym workspace checkout (default: ../nym)
# Requires: ANDROID_NDK_HOME, rustup android targets, cargo-ndk.
#
# Note: the sidecar is patched to use preconfigured webpki roots on Android
# (common/http-api-client/src/registry.rs) — the default rustls platform
# verifier needs the app's JNI context, which a standalone process lacks.
set -e
NYM_SRC="${NYM_SRC:-../nym}"
WHICH="${1:-all}"
build() {
local abi="$1"
echo ">> building nym-socks5-client for ${abi}"
( cd "${NYM_SRC}" && cargo ndk -t "${abi}" build --release -p nym-socks5-client )
}
case "${WHICH}" in
v7) build armeabi-v7a ;;
v8) build arm64-v8a ;;
x86) build x86_64 ;;
all) build arm64-v8a; build x86_64; build armeabi-v7a ;;
*) echo "usage: $0 [v7|v8|x86|all]"; exit 1 ;;
esac
echo "done — sidecars in ${NYM_SRC}/target/<triple>/release/nym-socks5-client"
+2 -2
View File
@@ -194,8 +194,8 @@ impl OnboardingContent {
(
"Send like a message",
"Pay a @username or npub and it arrives as an end-to-end \
encrypted message over nostr and Tor — no one in between can \
see the amount or who's involved.",
encrypted message over nostr and the Nym mixnet — no one in \
between can see the amount or who's involved.",
),
(
"Yours alone",
+21 -1
View File
@@ -31,7 +31,16 @@ use log::{error, info, warn};
use super::{SOCKS5_HOST, SOCKS5_PORT};
/// Bundled SOCKS5 client binary name.
/// Bundled SOCKS5 client binary name. Windows release archives ship the `.exe`;
/// `Command`/`current_exe().parent().join(..)` need the suffix to find it. On
/// Android the sidecar is shipped inside the APK's `jniLibs` as a `lib*.so` (the
/// only files extracted to the exec-allowed native-library dir) — same trick
/// upstream Grim used for Tor's webtunnel binary.
#[cfg(target_os = "windows")]
const BIN_NAME: &str = "nym-socks5-client.exe";
#[cfg(target_os = "android")]
const BIN_NAME: &str = "libnym_socks5_client.so";
#[cfg(not(any(target_os = "windows", target_os = "android")))]
const BIN_NAME: &str = "nym-socks5-client";
/// Per-app client id; namespaces the config/keys under the Nym data root.
@@ -84,6 +93,17 @@ fn binary_path() -> PathBuf {
return PathBuf::from(p);
}
}
// Android: `current_exe()` is the zygote/app_process, not us — the sidecar
// rides in the APK's jniLibs and is extracted to the native-library dir
// (the one exec-allowed location). MainActivity exports it as
// `NATIVE_LIBS_DIR` (see android/.../MainActivity.java).
#[cfg(target_os = "android")]
if let Ok(dir) = std::env::var("NATIVE_LIBS_DIR") {
let p = PathBuf::from(dir).join(BIN_NAME);
if p.exists() {
return p;
}
}
if let Ok(exe) = std::env::current_exe() {
if let Some(dir) = exe.parent() {
let sibling = dir.join(BIN_NAME);