Mobile Maia — F-Droid packaging volunteer requested

Hi F-Droid community,

I’m the developer of Mobile Maia, an AGPL-3.0-only offline chess application for Android. It provides human-like Maia opponents, game analysis, PGN support, premoves, clock controls, and experimental Chessnut Go support.

Project details

• Source: GitHub - Dash1971/maia-chess-android: An open-source, offline-first Android chess app for playing against Maia-3, with post-game Maia and Stockfish analysis and PGN export. · GitHub

• Latest stable release: Release Mobile Maia 2.1.0 · Dash1971/maia-chess-android · GitHub

• Package ID: com.dash1971.maia_chess

• Version: 2.1.0 (versionCode 73)

• Release-source commit: 7ad1264f2df1a483a9fc083d7cd885568681df1b

• Proposed fdroiddata recipe: maia-chess-android/fdroid/com.dash1971.maia_chess.yml at main · Dash1971/maia-chess-android · GitHub

• Model provenance: maia-chess-android/MODEL_PROVENANCE.md at main · Dash1971/maia-chess-android · GitHub

Mobile Maia remains fully offline and requests no Internet permission. The stable APK supports ARMv7, ARM64, and x86_64.

F-Droid preparation completed

• The proposed recipe includes the current 2.1.0 release and tag-based automatic updates.

• Local F-Droid lint, source-scanner, and APK-scanner checks passed.

• The developer-signed reproducible-build path is configured with Binaries and AllowedAPKSigningKeys.

• Two clean unsigned builds from separate worktrees and package caches were byte-for-byte identical.

• Two clean signed builds reproduced all 2,500 APK payload entries after Android signature normalization.

• The bundled Maia-3 ONNX model is AGPL-3.0 licensed. Its source, conversion procedure, immutable hashes, and reproducibility are documented.

• The recipe replaces the Git LFS model pointer from the immutable release commit and verifies its SHA-256 before building.

The public APK is 555,026,185 bytes because it contains the approximately 316 MB Maia model. Its SHA-256 is:

26b99c95a5118752316249c3efa12256bf36c646c90a1a5412b3b4dc4a7eae3c

GitLab’s account-verification requirements prevent me from opening the fdroiddata merge request myself. Would an established contributor be willing to:

1. Review or copy the proposed recipe to metadata/com.dash1971.maia_chess.yml;

2. Run the official fdroiddata checks and build pipeline;

3. Open a New App merge request with the reproducible-builds label; and

4. Reference this forum topic in the merge request?

I’m available to answer questions and make any required upstream changes. Thank you.

Hi Dash1971,

Happy to help - I can review your recipe, run the official lint/source/APK-scanner checks, and open the New App MR with the reproducible-builds label, referencing this thread.

Funny coincidence: I’ve been finishing up my own offline chess app (MoveAPiece, with physical DGT Pegasus board support) for F-Droid submission too. Given we’re both building similar apps at the same time, might be worth supporting each other going forward as well.

I actually went ahead and test-built your recipe against F-Droid’s real build pipeline already, just to make sure it holds up before committing to anything. Model verification, the prebuild steps, and the scanner all pass cleanly. One real blocker though: v2.1.0’s from-source build doesn’t reproduce byte-for-byte against your published GitHub release APK. Looks like PUB_CACHE being set to a $(pwd)-relative path means the Stockfish plugins bake in different absolute paths depending on where each CI checks out - pinning PUB_CACHE to a fixed path (e.g. /tmp/pub-cache) in your release script might fix it outright. Happy to dig into the details together if useful.

Update: I dug further and the PUB_CACHE theory turned out to be wrong.

Root cause: android/app/build.gradle.kts sets ndkVersion = flutter.ndkVersion instead of an exact pinned string, so whichever NDK
package a given build host resolves (and has cached) gets used as-is.

Fix: pin ndkVersion to an exact value (e.g. "28.2.13676358", Flutter
3.47.1’s own resolved default) instead of flutter.ndkVersion.

Would be happy to send you a PR with this fix, then follow up with the
fdroiddata MR once it’s merged.

Thank you egdels. Would be great to work with you on this. Will review your feedback and get back to you.

Thanks very much for investigating this and opening PR #10—and for offering to handle the fdroiddata MR. I really appreciate the help.

One detail I’d like to understand before merging: Mobile Maia pins Flutter 3.47.1, and that revision already defines flutter.ndkVersion as "28.2.13676358". I’d therefore expect the two assignments to behave identically when using the same Flutter revision. Could something else in the build environment explain the difference?

Could you share the build command or pipeline link and the before/after comparison output, ideally including the Flutter revision and effective NDK version/path? Whatever logs you already have would be a useful starting point.

Since v2.1.0 is already released and tagged at 7ad1264f, merging this into main won’t change that release. Once we understand what made the rebuild match, we can work out whether an fdroiddata recipe adjustment is enough or whether I should publish v2.1.1.

Thanks again—happy to work through this together, and I’m interested to check out your MoveAPiece submission too.

Good question - let’s take the technical back-and-forth to GitHub so we’re
not filling up this thread.

Hi @egdels — an update following our GitHub investigation: Mobile Maia v2.1.1 has now been released:

The official unsigned APK was built on Linux and independently reproduced byte-for-byte on a second Linux machine.

Source commit: 8b039e6af365aa7dbeb206b74530e241ff673a62

Unsigned APK SHA-256: 3cf8f4dd30ad89cdb6feec051df90de8e6ee51738065b08fe52a03a587120b29

Published signed APK SHA-256: 22e6317a9504f5db3d5d080d5fcd258b1bff47e86d203781e21702e46526647a

During independent verification I also found and fixed nondeterministic ordering in Flutter’s resolution-aware asset manifest, including stale Flutter tool-cache invalidation.

The updated fdroiddata recipe is here:

Would you be willing to rerun the F-Droid build against v2.1.1 and, if it reproduces successfully, open the New App merge request? Thanks again for identifying the original macOS/Linux discrepancy.

Hi @Dash1971 - update from rebuilding against v2.1.1.

1. A few changes to the recipe you posted.

  • Categories: Games → Board Game
  • wget -O → curl -sL -o
  • Dropped the scanignore entry for assets/models/maia3-79m.onnx.
    With it present, the build fails before it
    even gets to compiling.

2. Pipeline run: Pipeline #2844222783 · egdels / Data · GitLab

The fdroid build job is green but SUSS scanner flags six classes under
com/google/android/play/core/... we have to exclude in APK.

3. Of three F-Droid CI runs on this same commit, one failed with a digest
mismatch specifically on libmultistockfish_sf16.so (armeabi-v7a) — that
build came out at 537.6MB vs. 555.0MB for the two that matched. I haven’t
diffed the actual .so files, but multistockfish_sf16’s
src/CMakeLists.txt does

file(DOWNLOAD https://tests.stockfishchess.org/api/nn/nn-5af11540bbfe.nnue ${CMAKE_BINARY_DIR}/nn-5af11540bbfe.nnue)

at native-build time, with no EXPECTED_HASH. If that download is ever
partial, retried, or served differently, it’d land in exactly that
library and can explain an intermittent mismatch unrelated to anything in
your repo.

In MoveAPiece (also bundles Stockfish) NNUE net files are committed into the repo
(one xz-compressed, one plain), decompressed/copied into the build’s NNUE
cache and SHA-256-verified before every build.

Once points 1 and 2 are fully sorted, I’ll do one more full pipeline run
and, assuming it’s clean end to end, open the New App MR. Point 3 is
worth keeping an eye on for future rebuilds.

Thanks for running this. The point 1 recipe changes make sense.

On point 2, could you clarify whether you expect to resolve the six Play Core scanner findings entirely in fdroiddata, or whether an upstream APK change is required?

I confirmed that these are Flutter embedding references associated with its unused deferred-component support; Mobile Maia does not include the Play Core implementation and requests no Internet permission. Since the pipeline scans the published developer-signed APK through Binaries, it appears an upstream change may be necessary unless F-Droid has an accepted exclusion mechanism.

If an upstream change is required, could you share the exact six scanner lines and the approach F-Droid prefers—such as enabling R8 to remove the unused Flutter classes? I can then prepare and test the corresponding upstream fix.

On point 3, I agree that the unchecked NNUE download should be hardened rather than merely monitored. Could you retain or share the mismatching APK/library hashes if available? That would let us confirm whether the NNUE download caused the smaller libmultistockfish_sf16.so before preparing a deterministic SHA-256-verified fix.

Once we know the required scanner solution, I can address both issues together and publish a maintenance release if changing the upstream APK is necessary.

Hi Dash,

On 2) I think this needs to be fixed upstream. In
check apk (#16468323513) · Jobs · egdels / Data · GitLab, lines 2266 to
2271:

ERROR Found com/google/android/play/core/tasks/OnFailureListener in tmp/binaries/com.dash1971.maia_chess_74.binary.apk
ERROR Found com/google/android/play/core/splitinstall/SplitInstallSessionState in tmp/binaries/com.dash1971.maia_chess_74.binary.apk
ERROR Found com/google/android/play/core/splitinstall/SplitInstallStateUpdatedListener in tmp/binaries/com.dash1971.maia_chess_74.binary.apk
ERROR Found com/google/android/play/core/splitcompat/SplitCompatApplication in tmp/binaries/com.dash1971.maia_chess_74.binary.apk
ERROR Found com/google/android/play/core/splitinstall/SplitInstallManager in tmp/binaries/com.dash1971.maia_chess_74.binary.apk
ERROR Found com/google/android/play/core/tasks/OnSuccessListener in tmp/binaries/com.dash1971.maia_chess_74.binary.apk

I can’t say what F-Droid prefers. Presumably F-Droid doesn’t care about
the specific solution, as long as the classes don’t end up in the APK.

isMinifyEnabled = true will probably already fix it.

On 3) Here’s the failing run:
fdroid build (#16467974581) · Jobs · egdels / Data · GitLab. If you can’t
download the artifact there, I can provide it via GitHub instead. A
re-run afterwards,
fdroid build (#16468102278) · Jobs · egdels / Data · GitLab, succeeded.

Hi @egdels — a follow-up to the issues you identified: Mobile Maia v2.1.2 has now been released:

Both upstream issues have been addressed:

• Android R8 release minification removes all six unused Flutter Play Core references. The F-Droid APK scanner now reports zero findings.

• The Stockfish 16 NNUE network is downloaded once, verified against its pinned SHA-256, and the build fails closed if it is incomplete or changed.

The release-source commit is:

ebe11cf1c2e973fa50eb99391181c29d2f545937

I also tested the updated metadata through the current fdroidserver buildserver environment. Metadata parsing, lint, update checking, source/APK scanning, the full build, allowed-signer verification, and developer-binary comparison all passed.

GitHub Actions, an independent clean Linux build, and the F-Droid buildserver environment produced the same unsigned APK byte-for-byte:

• Size: 552150548 bytes

• SHA-256: 84bf2ca6fa698d3e707303cbf992af06554a435046611e48a5d694b445dcbb8b

Published signed APK:

• SHA-256: c1ab7fbf12fb0154ea01035ff2e8471f3839103960a6b5f1c585fe73c059603b

The updated recipe is here:

Would you be willing to use the v2.1.2 metadata for the New App merge request? Thanks again for identifying both issues and helping to harden the build.

Kindly take this to github.

Well noted. Thank you.