me-web? 0.15.1: the F-Droid repository is signed with the app's own key

- The Arasaka F-Droid repository, packages.arasaka.software/fdroid/repo, is signed with the same key as the app, so
  the fingerprint F-Droid or Obtainium checks is the one already on the download page for the APK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9uhaCTSycTh1r8At679Pz
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-31 09:24:50 +01:00
1 parent b38f50ee5e
commit 618a6782cd
8 files changed
+26 -27

No files matched your search

+8 -8
View File
@@ -5,12 +5,12 @@ here=$(pwd)
root=${PACKAGES_ROOT:-/var/www/packages.arasaka.software} root=${PACKAGES_ROOT:-/var/www/packages.arasaka.software}
keep=${FDROID_KEEP:-3} keep=${FDROID_KEEP:-3}
url=https://packages.arasaka.software/fdroid/repo url=https://packages.arasaka.software/fdroid/repo
fingerprint=42C47659B699E0B1A8BCF6F9CE2C4972972265B592BE32B5725B8F87C6BD0374 fingerprint=5F52CFF4F52F4E0B6DA6CD044BFCEAF78D1D42DB24F2470602DB7F19CBFDF846
id=software.arasaka.meweb id=software.arasaka.meweb
apk=$here/releases/$version/MeWeb-$version-android.apk apk=$here/releases/$version/MeWeb-$version-android.apk
test -f "$apk" test -f "$apk"
if [ -z "${FDROID_KEYSTORE_B64:-}" ] || [ -z "${FDROID_KEYSTORE_PASS:-}" ]; then if [ -z "${MEWEB_KEYSTORE_B64:-}" ] || [ -z "${MEWEB_KEYSTORE_PASS:-}" ]; then
echo "::error::the organisation secrets FDROID_KEYSTORE_B64 and FDROID_KEYSTORE_PASS are not set" echo "::error::the repository secrets MEWEB_KEYSTORE_B64 and MEWEB_KEYSTORE_PASS are not set"
exit 1 exit 1
fi fi
major=$(echo "$version" | cut -d. -f1) major=$(echo "$version" | cut -d. -f1)
@@ -37,11 +37,11 @@ flock -w 2700 9
keys=$(mktemp -d) keys=$(mktemp -d)
trap 'rm -rf "$keys"' EXIT trap 'rm -rf "$keys"' EXIT
(umask 077 && printf '%s' "$FDROID_KEYSTORE_B64" | base64 -d > "$keys/repo.p12") (umask 077 && printf '%s' "$MEWEB_KEYSTORE_B64" | base64 -d > "$keys/meweb.keystore")
FDROID_KEYSTORE=$keys/repo.p12 MEWEB_KEYSTORE=$keys/meweb.keystore
export FDROID_KEYSTORE FDROID_KEYSTORE_PASS export MEWEB_KEYSTORE MEWEB_KEYSTORE_PASS
unset FDROID_KEYSTORE_B64 unset MEWEB_KEYSTORE_B64
held=$(keytool -J-Duser.language=en -list -v -keystore "$FDROID_KEYSTORE" -storepass:env FDROID_KEYSTORE_PASS -alias arasaka | sed -n 's/^.*SHA256: *//p' | tr -d ':') held=$(keytool -J-Duser.language=en -list -v -keystore "$MEWEB_KEYSTORE" -storepass:env MEWEB_KEYSTORE_PASS -alias meweb | sed -n 's/^.*SHA256: *//p' | tr -d ':')
test "$held" = "$fingerprint" test "$held" = "$fingerprint"
state=$root/.fdroid state=$root/.fdroid
+2 -2
View File
@@ -58,6 +58,6 @@ jobs:
- name: The APK in the F-Droid repository on packages.arasaka.software - name: The APK in the F-Droid repository on packages.arasaka.software
if: ${{ github.ref_type == 'tag' }} if: ${{ github.ref_type == 'tag' }}
env: env:
FDROID_KEYSTORE_B64: ${{ secrets.FDROID_KEYSTORE_B64 }} MEWEB_KEYSTORE_B64: ${{ secrets.MEWEB_KEYSTORE_B64 }}
FDROID_KEYSTORE_PASS: ${{ secrets.FDROID_KEYSTORE_PASS }} MEWEB_KEYSTORE_PASS: ${{ secrets.MEWEB_KEYSTORE_PASS }}
run: .gitea/scripts/fdroid.sh "$VERSION" run: .gitea/scripts/fdroid.sh "$VERSION"
+5 -4
View File
@@ -177,13 +177,14 @@ The Android release key: alias `meweb`, certificate SHA-256
release is published, so the repository can lag the release page but never lead it. `.gitea/scripts/fdroid.sh` release is published, so the repository can lag the release page but never lead it. `.gitea/scripts/fdroid.sh`
installs fdroidserver into `tools/fdroid` (cached like the JDK) with uv from `setup.json` and the hash-locked installs fdroidserver into `tools/fdroid` (cached like the JDK) with uv from `setup.json` and the hash-locked
`packaging/fdroid/requirements.txt`, because the box has no `ensurepip` and the apt package would bring 242 others. `packaging/fdroid/requirements.txt`, because the box has no `ensurepip` and the apt package would bring 242 others.
It refuses any repository key but `42C47659…BD0374` (org secrets `FDROID_KEYSTORE_B64` and `FDROID_KEYSTORE_PASS`; It signs the repository with the APK's own key (`MEWEB_KEYSTORE_B64` and `MEWEB_KEYSTORE_PASS`, alias `meweb`), so
the only other copy is the owner's Nextcloud, `Backups/arasaka-fdroid-repo-key/`), builds a new snapshot in the F-Droid fingerprint is the certificate's `5F52CFF4…FDF846` and there is no second key to lose; it refuses any
other. It builds a new snapshot in
`/var/www/packages.arasaka.software/.fdroid/` from the live one (the APKs as hard links, so `rm` then `cp` as above; `/var/www/packages.arasaka.software/.fdroid/` from the live one (the APKs as hard links, so `rm` then `cp` as above;
the metadata, `apkcache.json` and the previous `index-v2.json`, which is where fdroidserver reads each APK's first the metadata, `apkcache.json` and the previous `index-v2.json`, which is where fdroidserver reads each APK's first
date), adds this version, keeps the newest three, runs `fdroid update`, deletes `repo/status/` (it names the date), adds this version, keeps the newest three, runs `fdroid update`, deletes `repo/status/` (it names the
build machine's paths and `/etc/issue.net`), swaps the `fdroid/repo` link and checks the live signature. Never build machine's paths and `/etc/issue.net`), swaps the `fdroid/repo` link and checks the live signature. The key
regenerate the key: everyone who added the repository trusts only its fingerprint. `packaging/fdroid/` holds the was already never to be regenerated; now everyone who added the repository trusts only its fingerprint too. `packaging/fdroid/` holds the
repository's settings, its icon (the Arasaka emblem) and the app's listing in English and Italian; the download repository's settings, its icon (the Arasaka emblem) and the app's listing in English and Italian; the download
page links to it and to Obtainium (`Endpoints.FDroid`, source `FDroidRepo`). page links to it and to Obtainium (`Endpoints.FDroid`, source `FDroidRepo`).
- **The server cannot ping**: `NoNewPrivileges` drops `ping`'s file capability and `ping_group_range` is closed, so - **The server cannot ping**: `NoNewPrivileges` drops `ping`'s file capability and `ping_group_range` is closed, so
+1 -1
View File
@@ -1,6 +1,6 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>0.15.0</Version> <Version>0.15.1</Version>
<Product>me-web?</Product> <Product>me-web?</Product>
<Company>Arasaka di Hvozdikov Yevheniy</Company> <Company>Arasaka di Hvozdikov Yevheniy</Company>
<Copyright>© 2026 Arasaka di Hvozdikov Yevheniy</Copyright> <Copyright>© 2026 Arasaka di Hvozdikov Yevheniy</Copyright>
+2 -3
View File
@@ -55,9 +55,8 @@ is `packaging/arasaka-repository.sh`; it never calls the package manager,
leaves files you edited alone, and removes the repository only when no Arasaka package is left (purge or erase). leaves files you edited alone, and removes the repository only when no Arasaka package is left (purge or erase).
On Android, the same signed APK is also in an F-Droid repository, so F-Droid, a store like it, or Obtainium can bring On Android, the same signed APK is also in an F-Droid repository, so F-Droid, a store like it, or Obtainium can bring
the updates: add `https://packages.arasaka.software/fdroid/repo`, whose key has the SHA-256 the updates: add `https://packages.arasaka.software/fdroid/repo`, which is signed with the same key as the APK. The
`42:C4:76:59:B6:99:E0:B1:A8:BC:F6:F9:CE:2C:49:72:97:22:65:B5:92:BE:32:B5:72:5B:8F:87:C6:BD:03:74`. The download page download page links to both. The app is not in F-Droid's own catalogue, which builds every app from source on F-Droid's servers.
links to both. The app is not in F-Droid's own catalogue, which builds every app from source on F-Droid's servers.
## Privacy ## Privacy
+4 -4
View File
@@ -5,7 +5,7 @@ repo_description: >-
Each app here is the same signed file as on its release page. Each app here is the same signed file as on its release page.
repo_icon: icon.png repo_icon: icon.png
archive_older: 0 archive_older: 0
repo_keyalias: arasaka repo_keyalias: meweb
keystore: {env: FDROID_KEYSTORE} keystore: {env: MEWEB_KEYSTORE}
keystorepass: {env: FDROID_KEYSTORE_PASS} keystorepass: {env: MEWEB_KEYSTORE_PASS}
keypass: {env: FDROID_KEYSTORE_PASS} keypass: {env: MEWEB_KEYSTORE_PASS}
+3 -4
View File
@@ -11,7 +11,6 @@ public static class DownloadPage
const string Fingerprint = "5F:52:CF:F4:F5:2F:4E:0B:6D:A6:CD:04:4B:FC:EA:F7:8D:1D:42:DB:24:F2:47:06:02:DB:7F:19:CB:FD:F8:46"; const string Fingerprint = "5F:52:CF:F4:F5:2F:4E:0B:6D:A6:CD:04:4B:FC:EA:F7:8D:1D:42:DB:24:F2:47:06:02:DB:7F:19:CB:FD:F8:46";
const string ObtainiumApp = $$"""{"id":"software.arasaka.meweb","url":"{{Endpoints.FDroid.Repository}}","author":"Arasaka","name":"me-web?","overrideSource":"FDroidRepo","additionalSettings":"{\"appIdOrName\":\"software.arasaka.meweb\"}"}"""; const string ObtainiumApp = $$"""{"id":"software.arasaka.meweb","url":"{{Endpoints.FDroid.Repository}}","author":"Arasaka","name":"me-web?","overrideSource":"FDroidRepo","additionalSettings":"{\"appIdOrName\":\"software.arasaka.meweb\"}"}""";
static readonly string Obtainium = Endpoints.FDroid.Obtainium + Uri.EscapeDataString(ObtainiumApp); static readonly string Obtainium = Endpoints.FDroid.Obtainium + Uri.EscapeDataString(ObtainiumApp);
static readonly string RepositoryKey = string.Join(":", Enumerable.Range(0, Endpoints.FDroid.Fingerprint.Length / 2).Select(i => Endpoints.FDroid.Fingerprint.Substring(i * 2, 2)));
static readonly Dictionary<string, Dictionary<string, string>> Words = new() static readonly Dictionary<string, Dictionary<string, string>> Words = new()
{ {
@@ -27,7 +26,7 @@ public static class DownloadPage
["AddFDroid"] = "Add to F-Droid", ["AddFDroid"] = "Add to F-Droid",
["AddObtainium"] = "Add to Obtainium", ["AddObtainium"] = "Add to Obtainium",
["RepoAddress"] = "The repository's address is", ["RepoAddress"] = "The repository's address is",
["RepoKey"] = "and the SHA-256 of its key is", ["RepoKey"] = "and it is signed with the same key as the app.",
["Windows"] = "Windows (64-bit)", ["Windows"] = "Windows (64-bit)",
["WindowsNote"] = "The file isn't signed with a paid certificate, so Windows may say it protected your PC: choose “More info”, then “Run anyway”.", ["WindowsNote"] = "The file isn't signed with a paid certificate, so Windows may say it protected your PC: choose “More info”, then “Run anyway”.",
["Linux"] = "Linux", ["Linux"] = "Linux",
@@ -55,7 +54,7 @@ public static class DownloadPage
["AddFDroid"] = "Aggiungere a F-Droid", ["AddFDroid"] = "Aggiungere a F-Droid",
["AddObtainium"] = "Aggiungere a Obtainium", ["AddObtainium"] = "Aggiungere a Obtainium",
["RepoAddress"] = "L'indirizzo del repository è", ["RepoAddress"] = "L'indirizzo del repository è",
["RepoKey"] = "e lo SHA-256 della sua chiave è", ["RepoKey"] = "ed è firmato con la stessa chiave dell'app.",
["Windows"] = "Windows (64 bit)", ["Windows"] = "Windows (64 bit)",
["WindowsNote"] = "Il file non è firmato con un certificato a pagamento, quindi Windows può dire di aver protetto il PC: scelga “Ulteriori informazioni”, poi “Esegui comunque”.", ["WindowsNote"] = "Il file non è firmato con un certificato a pagamento, quindi Windows può dire di aver protetto il PC: scelga “Ulteriori informazioni”, poi “Esegui comunque”.",
["Linux"] = "Linux", ["Linux"] = "Linux",
@@ -91,7 +90,7 @@ public static class DownloadPage
html.Append($"<p class=\"soft\">{E(string.Format(culture, words["Latest"], version, release.PublishedAt.ToString("d MMMM yyyy", culture)))}</p>"); html.Append($"<p class=\"soft\">{E(string.Format(culture, words["Latest"], version, release.PublishedAt.ToString("d MMMM yyyy", culture)))}</p>");
Block(html, release, words["Android"], [("android", "APK")], $"{E(words["AndroidNote"])} <code>{Fingerprint}</code>.", Block(html, release, words["Android"], [("android", "APK")], $"{E(words["AndroidNote"])} <code>{Fingerprint}</code>.",
$"<p>{E(words["Updates"])}</p><p><a href=\"{E(Endpoints.FDroid.Add)}\">{E(words["AddFDroid"])}</a> · <a href=\"{E(Obtainium)}\">{E(words["AddObtainium"])}</a></p>" + $"<p>{E(words["Updates"])}</p><p><a href=\"{E(Endpoints.FDroid.Add)}\">{E(words["AddFDroid"])}</a> · <a href=\"{E(Obtainium)}\">{E(words["AddObtainium"])}</a></p>" +
$"<p class=\"soft\">{E(words["RepoAddress"])} <code>{Endpoints.FDroid.Repository}</code> {E(words["RepoKey"])} <code>{RepositoryKey}</code>.</p>"); $"<p class=\"soft\">{E(words["RepoAddress"])} <code>{Endpoints.FDroid.Repository}</code> {E(words["RepoKey"])}</p>");
Block(html, release, words["Windows"], [("windows", ".exe")], E(words["WindowsNote"])); Block(html, release, words["Windows"], [("windows", ".exe")], E(words["WindowsNote"]));
var deb = ReleaseFeed.Asset(release, "linux-deb")?.Name; var deb = ReleaseFeed.Asset(release, "linux-deb")?.Name;
var rpm = ReleaseFeed.Asset(release, "linux-rpm")?.Name; var rpm = ReleaseFeed.Asset(release, "linux-rpm")?.Name;
+1 -1
View File
@@ -78,7 +78,7 @@ public static class Endpoints
public static class FDroid public static class FDroid
{ {
public const string Repository = "https://packages.arasaka.software/fdroid/repo"; public const string Repository = "https://packages.arasaka.software/fdroid/repo";
public const string Fingerprint = "42C47659B699E0B1A8BCF6F9CE2C4972972265B592BE32B5725B8F87C6BD0374"; public const string Fingerprint = "5F52CFF4F52F4E0B6DA6CD044BFCEAF78D1D42DB24F2470602DB7F19CBFDF846";
public const string Add = $"{Repository}?fingerprint={Fingerprint}"; public const string Add = $"{Repository}?fingerprint={Fingerprint}";
public const string Obtainium = "obtainium://app/"; public const string Obtainium = "obtainium://app/";
} }