rsend reproducible builds ========================= How rsend stays reproducible and what F-Droid needs to build it. Pinning ------- Every tool and source version lives in ./versions: the rsync source, Go, JDK, Android NDK, SDK platform, build-tools, command-line tools, Gradle, the Android Gradle Plugin, and Kotlin. Directly downloaded archives have repository-owned checksums. The SDK platform revision and android.jar bytes are checked after sdkmanager installs them. Temurin's glibc and musl archives have separate checksums and cache directories. The Gradle wrapper records its distribution checksum too. The gradle files and go.mod repeat some pins because gradle cannot source a shell file. Gradle's verification-metadata.xml locks every resolved Maven and plugin artifact by SHA-256. ci/test.sh fails if direct pins or packaged notices drift. After a deliberate dependency change, regenerate the checksum manifest while running every Gradle path that resolves artifacts, then review its diff: gradle --write-verification-metadata sha256 \ :app:testReleaseUnitTest :app:lintRelease :app:assembleRelease Determinism ----------- - rsh (Go): built with the NDK clang, -trimpath, -ldflags "-s -w -buildid=", CGO_CFLAGS=-ffile-prefix-map to drop build paths, and 16 KB ELF alignment. - rsync (C): cross-compiled with -ffile-prefix-map and stripped; optional dependencies are disabled and zlib and popt come from rsync's bundled copies, so the binary needs nothing outside Bionic. Stack protection, fortified libc calls, RELRO, immediate binding, and a non-executable stack are explicit. Configure's host-dependent nobody group is pinned explicitly. Linker flags give it 16 KB ELF alignment too. - APK: no code shrinking (minifyEnabled false), the Google dependency-metadata block is dropped (dependenciesInfo off), and native libs use legacy packaging. Verify locally from a clean commit. The script extracts that commit twice at different absolute paths, builds both complete unsigned APKs, and compares them byte for byte: make verify-repro A local gitignored keystore.properties is harmless: git archive cannot copy it into either clean build tree. F-Droid ------- The build recipe lives in fdroiddata, not in this repo. F-Droid supplies the NDK named by the recipe's ndk field and exports ANDROID_NDK_HOME. It pins Go through the go srclib (the official Go source) and builds it with make.bash, and installs make and g++ for rsync. The native lib*.so are then built in the recipe's build step, which runs after F-Droid's source scanner and calls rsync/build.sh and rsh/build.sh; Gradle then assembles the APK. The release build is unsigned unless a local keystore.properties is present, so F-Droid signs the published APK with its own key. Each release recipe must set the go srclib ref to the exact GO_VERSION in ./versions. In particular, the published 0.1.3 recipe's go1.25.0 ref must not be reused for a release built with a newer Go patch version. All-files access ---------------- rsend requests MANAGE_EXTERNAL_STORAGE. It is required: backup targets such as OEM call-recording directories and app-specific media folders live outside the scoped-media buckets, and rsync needs real filesystem paths rather than SAF content URIs. At runtime the app makes no network access other than SSH connections to hosts the user configures. Android backup and device-transfer rules exclude all app data, including the encrypted identity, host pins, paths, and logs.