Files
PalladiumWallet/docker
davide d0037747b5 chore(android): add persistent release-signing keystore generator
An Android APK's signature identifies the app to the OS; a new build must
carry the same one as the last install or Android refuses it as an update.
generate-keystore.sh creates the keystore once, interactively, and refuses
to run again once one exists to avoid ever changing it by accident. The
resulting file is git-ignored — it's a secret that must live outside the
repo, backed up separately.
2026-07-02 21:53:56 +02:00
..

PalladiumWallet — Reproducible Builds via Docker

This folder builds all three distribution targets — Windows, Linux, Android — inside Docker containers. The entire toolchain (.NET 10 SDK, JDK, Android SDK, android workload) lives in the container images, pinned in the Dockerfiles, so:

  • anyone can produce the official binaries with a single command, on any Linux machine, without installing any SDK on the host;
  • the toolchain never drifts: every build uses exactly the same SDK versions, regardless of what is installed (or updated) on the host;
  • the build environment is auditable: the Dockerfiles in this folder are the complete, reviewable definition of how release binaries are made — which matters for a wallet, where users must be able to trust that the shipped binary comes from the published source.

Scope note: this pins the build environment. Bit-for-bit identical output across machines is a stronger property that .NET does not guarantee by default (embedded timestamps, signing); if two builds of the same commit differ, they differ only in those metadata, not in code.


Prerequisites

A Linux host (native, WSL2, or a VM) with Docker Engine installed and running. Nothing else — no .NET SDK, no JDK, no Android SDK.

# Check that Docker works:
docker info

If Docker is missing, install it from https://docs.docker.com/engine/install/ (or sudo apt install docker.io on Debian/Ubuntu). If docker info fails with a permission error, add yourself to the docker group and start a new shell:

sudo usermod -aG docker $USER

Disk space: ~2 GB for the desktop image, ~7 GB for the Android image (SDK + emulator-less toolchain). First-run time: the images are built automatically on first use — a few minutes for desktop, 1020 minutes for Android (large downloads). Subsequent builds reuse the cached images and take well under a minute (desktop) / a few minutes (Android).


Quick start

# From the repository root (or from the docker/ folder — both work):
./docker/build.sh

Running without arguments shows an interactive menu — pick a single target or all. Non-interactive usage:

./docker/build.sh [TARGET] [--rebuild]

Targets:
  windows   Win x64 single-file executable (native libs embedded)
  linux     Linux x64 single-file binary (runs as-is, nothing to install)
  android   Android APK (debug-signed)
  all       All three targets

Options:
  --rebuild   Force rebuild of the Docker images (needed after editing a Dockerfile)

Examples:

./docker/build.sh all               # build everything
./docker/build.sh windows           # Windows only
./docker/build.sh android --rebuild # Android, rebuilding the image first

Output — and how to use each artifact

All artifacts land in dist/ at the repository root. The version number is read automatically from <Version> in src/App/PalladiumWallet.App.csproj.

Target Path
Windows dist/windows/PalladiumWallet-{ver}-win-x64.exe
Linux dist/linux/PalladiumWallet-{ver}-linux-x64
Android dist/android/PalladiumWallet-{ver}.apk

Windows — a single self-contained .exe (runtime and native libraries embedded). Copy it to any 64-bit Windows 10/11 machine and double-click. The first launch takes a few extra seconds (it unpacks native libraries to a per-user cache); later launches are normal. SmartScreen may warn because the binary is not code-signed — choose "Run anyway".

Linux — a single self-contained binary, already executable. Copy and run:

./PalladiumWallet-{ver}-linux-x64

Works on any desktop distro with glibc, X11/Wayland and fontconfig (i.e. effectively all of them); no .NET or other packages to install. If you transfer it through a channel that strips permissions (e.g. a web download), restore the execute bit with chmod +x.

Android — a debug-signed APK for sideloading: transfer it to the phone and open it (enable "install from unknown sources" if prompted), or install via adb install dist/android/PalladiumWallet-*.apk. Supports Android 6.0+ (API 23), arm64 phones and x86_64 emulators.

Signature caveat: debug-signed APKs built on different machines carry different keys. If a previous build is already installed, Android refuses the update (INSTALL_FAILED_UPDATE_INCOMPATIBLE) — uninstall the old app first. Uninstalling deletes the app's data: back up the wallet seed before doing this. Release signing with a stable key is not set up yet.


How it works

Docker images

Image Dockerfile Used for Size
plm-build-desktop Dockerfile.desktop windows + linux ~1.5 GB
plm-build-android Dockerfile.android android ~5 GB

Images are built automatically the first time a target needs them and reused afterwards. Use --rebuild only after modifying a Dockerfile.

Source isolation

The repository is mounted read-only inside the container; the build works on a copy at /tmp/build. Your working tree is never touched — no stray bin//obj/ directories, and a dirty working tree doesn't leak into the build beyond the files it contains. Artifacts are written back through a bind mount to dist/ and chown'd to your user.

NuGet cache

A Docker named volume plm-nuget-cache holds downloaded NuGet packages across builds. To reclaim the space or force a clean re-download:

docker volume rm plm-nuget-cache

Troubleshooting

  • Docker daemon is not running — start it (sudo systemctl start docker; on WSL2, start Docker Desktop or the docker service).
  • Android image build fails downloading commandlinetools — Google rotates the build number in the URL. Update the URL in Dockerfile.android to the current one from https://developer.android.com/studio#command-tools and rerun with --rebuild.
  • error XA5207: Could not find android.jar for API level N — the .NET android workload moved to a newer API level. Bump ANDROID_SDK_PLATFORM (and ANDROID_SDK_BUILD_TOOLS) in Dockerfile.android to the level the error names, then --rebuild.
  • APK won't install over an existing app — signature mismatch between debug keys; see the signature caveat above.
  • Everything is broken / start from scratchdocker system prune -a && docker volume rm plm-nuget-cache, then rerun the script (images and packages are re-downloaded).

Linux AppImage (future)

The Linux target currently produces a single-file self-contained binary. Once a pupnet.conf is added to the repository, the build_linux function in build.sh can be extended to call PupNet Deploy inside the same plm-build-desktop image to also produce an AppImage with desktop integration (icon, menu entry).