This document explains how Session's F-Droid distribution works, why a custom script is needed, and how to perform the F-Droid release manually if the script is unavailable or broken.
F-Droid is an open-source app store for Android. Unlike the Play Store, it operates on a repository model: a repo is a directory of APKs and a signed index file that F-Droid clients download to discover available apps and versions.
There are two ways an app can appear in F-Droid:
- Official F-Droid repo — F-Droid builds the APK itself from source, using its own signing key. The app developer has no control over the signing key or build timing.
- Self-hosted repo — The developer hosts their own F-Droid-compatible repository, signs the APKs and the repo index with their own keys, and F-Droid clients add it as a custom repo source.
Session uses a self-hosted repo at session-foundation/session-fdroid. This gives us control over the signing key and release timing, at the cost of needing to maintain the repo ourselves.
The standard fdroid command-line tools are designed around a workflow where F-Droid
builds the APK from source. Because we sign APKs ourselves (to keep the signing key
private) and provide pre-built APKs, the standard workflow does not apply cleanly.
Our script bridges that gap by:
- Building and signing the APK with Gradle, using our own keystore —
fdroid updateonly indexes and re-signs the repo index, it does not build or sign APKs itself. - Managing APK inventory — F-Droid clients expect old versions to remain available for users who cannot upgrade immediately. The script maintains a rolling window of the last four releases, removing anything older.
- Keeping secrets out of the repo —
config.yml(which contains keystore paths and passwords) and the generatedmetadata/<package-id>.yml(which embeds the current version code) are derived from committed template files (.tpl) at release time and deleted afterwards. Neither sensitive file is ever committed. - Automating the PR workflow — the
session-fdroidrepo uses a PR-based flow so that every release update is reviewed by a human before it reaches users. The script handles branch creation and PR opening automatically.
session-fdroid/
├── fdroid/
│ ├── repo/ # APKs served to F-Droid clients
│ │ └── session-<version>-<abi>.apk
│ ├── metadata/
│ │ └── <package-id>.yml.tpl # Template; .yml is generated at release time
│ ├── config.yml.tpl # Template; config.yml is generated at release time
│ └── index-v1.jar / index-v2.json # Signed repo index, regenerated by `fdroid update`
The .tpl files are committed to the repo. The generated config.yml and
metadata/<package-id>.yml are produced locally, used by fdroid update, then deleted —
they are never committed.
Our script automates the steps below, but understanding them means you can recover from a broken script and reason about what the tooling is doing.
The core tool is fdroid update from the
fdroidserver package. Its job is to:
- Scan the APKs in
repo/ - Read the app metadata from
metadata/<package-id>.yml - Produce a signed repository index (
index-v1.jarandindex-v2.json) that F-Droid clients download to learn what versions are available and where to get them
fdroid update does not build or sign APKs — it only indexes pre-built ones and signs
the repo index using the key configured in config.yml.
config.yml tells fdroid update how to sign the repo index and where to find the Android
SDK. The key fields are:
repo_keyalias: <alias>
keystore: /path/to/repo-signing-keystore.p12 # PKCS12 format
keystorepass: <keystore-password>
keypass: <key-password>
android_sdk_path: /path/to/android/sdkThis file contains secrets and is never committed. In session-fdroid it is generated
from config.yml.tpl at release time and deleted after fdroid update runs.
F-Droid uses a metadata file per app to describe the app to clients — its name, description,
links, and crucially the CurrentVersionCode, which tells F-Droid clients which version is
the latest so they know when to prompt for an upgrade.
The full metadata format is documented at https://f-droid.org/en/docs/Build_Metadata_Reference/
In session-fdroid, only the CurrentVersionCode field changes between releases. The rest
of the file is static and lives in metadata/<package-id>.yml.tpl. The .tpl is rendered
to .yml at release time with the new version code substituted in, used by fdroid update,
then deleted.
The fdroidserver workflow for adding a new version to a self-hosted repo is:
- Place the new signed APK(s) in the
repo/directory alongside existing ones. - Update
CurrentVersionCodeinmetadata/<package-id>.ymlto the new version code. - Run
fdroid updatefrom the repo root (whereconfig.ymllives). It will scan all APKs, merge in the metadata, and rewrite the signed index files. - Commit the updated index files and new APKs (but not
config.ymlor the renderedmetadata/<package-id>.yml).
When master of session-fdroid is updated, any F-Droid client that has added our repo
will pick up the new index on its next refresh and offer the update to users.