aboutsummaryrefslogtreecommitdiff
path: root/REPRODUCIBLE-BUILDS
diff options
context:
space:
mode:
Diffstat (limited to 'REPRODUCIBLE-BUILDS')
-rw-r--r--REPRODUCIBLE-BUILDS79
1 files changed, 79 insertions, 0 deletions
diff --git a/REPRODUCIBLE-BUILDS b/REPRODUCIBLE-BUILDS
new file mode 100644
index 0000000..7cfcde9
--- /dev/null
+++ b/REPRODUCIBLE-BUILDS
@@ -0,0 +1,79 @@
+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. 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.
+
+The packages sdkmanager installs are the exception. The NDK and build-tools are
+pinned by revision and verified against the checksums Google publishes in the
+SDK repository manifest, not against a checksum this repository owns; only the
+platform's android.jar is checked here byte for byte. A republished revision
+would therefore change the output without any pin in this tree changing. Adding
+repository-owned hashes would mean bypassing sdkmanager and fetching the
+multi-gigabyte archives directly, which is not worth the exchange.
+
+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.