The APK also lands in an F-Droid repository on packages.arasaka.software, linked from the download page with Obtainium
Build / Desktop, server and tools (push) Successful in 1m12s

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 03:22:36 +01:00
1 parent b812e6a8d6
commit b38f50ee5e
21 files changed
+1431 -4

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
#!/bin/sh
set -eu
version=$1
here=$(pwd)
root=${PACKAGES_ROOT:-/var/www/packages.arasaka.software}
keep=${FDROID_KEEP:-3}
url=https://packages.arasaka.software/fdroid/repo
fingerprint=42C47659B699E0B1A8BCF6F9CE2C4972972265B592BE32B5725B8F87C6BD0374
id=software.arasaka.meweb
apk=$here/releases/$version/MeWeb-$version-android.apk
test -f "$apk"
if [ -z "${FDROID_KEYSTORE_B64:-}" ] || [ -z "${FDROID_KEYSTORE_PASS:-}" ]; then
echo "::error::the organisation secrets FDROID_KEYSTORE_B64 and FDROID_KEYSTORE_PASS are not set"
exit 1
fi
major=$(echo "$version" | cut -d. -f1)
minor=$(echo "$version" | cut -d. -f2)
patch=$(echo "$version" | cut -d. -f3)
code=$((major * 10000 + minor * 100 + patch))
tools/setup.sh --group fdroid
tools=$here/tools/fdroid/linux-x64
pinned=$(sed -n 's/^fdroidserver==\([^ ]*\).*/\1/p' packaging/fdroid/requirements.txt)
venv=$tools/fdroidserver-$pinned
if [ ! -x "$venv/bin/fdroid" ]; then
rm -rf "$venv"
UV_CACHE_DIR=$tools/cache UV_PYTHON_INSTALL_DIR=$tools/python "$tools/uv-x86_64-unknown-linux-gnu/uv" venv -q --python 3.12 "$venv"
UV_CACHE_DIR=$tools/cache "$tools/uv-x86_64-unknown-linux-gnu/uv" pip install -q --python "$venv/bin/python" --require-hashes -r packaging/fdroid/requirements.txt
fi
JAVA_HOME=$(ls -d "$here"/tools/jdk/linux-x64/*/ | head -n 1)
ANDROID_HOME=$here/tools/android-sdk
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME ANDROID_HOME PATH
exec 9>>"$root/.lock"
flock -w 2700 9
keys=$(mktemp -d)
trap 'rm -rf "$keys"' EXIT
(umask 077 && printf '%s' "$FDROID_KEYSTORE_B64" | base64 -d > "$keys/repo.p12")
FDROID_KEYSTORE=$keys/repo.p12
export FDROID_KEYSTORE FDROID_KEYSTORE_PASS
unset FDROID_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 ':')
test "$held" = "$fingerprint"
state=$root/.fdroid
snap=$state/$(date -u +%Y%m%d%H%M%S)-$version
mkdir -p "$snap/repo" "$snap/metadata" "$snap/tmp"
if [ -e "$root/fdroid/repo" ]; then
prior=$(readlink -f "$root/fdroid/repo")/..
for file in "$prior"/repo/*.apk; do
[ -e "$file" ] && ln "$file" "$snap/repo/"
done
cp -a "$prior/metadata/." "$snap/metadata/"
cp "$prior/repo/index-v2.json" "$snap/repo/"
if [ -f "$prior/tmp/apkcache.json" ]; then cp "$prior/tmp/apkcache.json" "$snap/tmp/"; fi
fi
cp packaging/fdroid/config.yml "$snap/config.yml"
chmod 600 "$snap/config.yml"
cp packaging/fdroid/icon.png "$snap/icon.png"
cp -r packaging/fdroid/metadata/. "$snap/metadata/"
rm -f "$snap/repo/${id}_$code.apk"
cp "$apk" "$snap/repo/${id}_$code.apk"
(cd "$snap/repo" && ls "$id"_*.apk | sed 's/.*_\([0-9]*\)\.apk$/\1/' | sort -n | head -n "-$keep" | while read -r old; do
rm -f "${id}_$old.apk"
done)
(cd "$snap" && "$venv/bin/fdroid" update)
rm -rf "$snap/repo/status"
chmod -R u=rwX,go=rX "$snap/repo"
mkdir -p "$root/fdroid"
ln -sfn "../.fdroid/$(basename "$snap")/repo" "$root/fdroid/repo.new"
mv -T -f "$root/fdroid/repo.new" "$root/fdroid/repo"
ls -1d "$state"/*/ | sort | head -n -3 | while read -r old; do
find "$old" -maxdepth 0 -mmin +1440 -exec rm -rf {} +
done
echo "published ${id}_$code.apk in $root/fdroid/repo"
if [ "$root" = /var/www/packages.arasaka.software ]; then
curl -fsS "$url/entry.jar" -o "$keys/entry.jar"
signed=$(keytool -J-Duser.language=en -printcert -jarfile "$keys/entry.jar" | sed -n 's/^.*SHA256: *//p' | head -n 1 | tr -d ':')
test "$signed" = "$fingerprint"
curl -fsS "$url/index-v2.json" | grep -Eq "\"versionCode\": ?$code\b"
curl -fsSI "$url/${id}_$code.apk" > /dev/null
echo "live at $url: $id $version ($code)"
fi
+1 -1
View File
@@ -8,7 +8,7 @@ fi
echo "VERSION=$version" >> "$GITHUB_ENV"
echo "COMMIT=$(echo "$GITHUB_SHA" | cut -c1-8)" >> "$GITHUB_ENV"
keep="$HOME/.cache/meweb"
for dir in tools/cache tools/jdk tools/packaging tools/android-sdk; do
for dir in tools/cache tools/jdk tools/packaging tools/android-sdk tools/fdroid; do
mkdir -p "$keep/$dir"
rm -rf "$dir"
ln -s "$keep/$dir" "$dir"
+7
View File
@@ -54,3 +54,10 @@ jobs:
env:
RELEASE_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: .gitea/scripts/release.sh "v$VERSION" releases/"$VERSION"/*
- name: The APK in the F-Droid repository on packages.arasaka.software
if: ${{ github.ref_type == 'tag' }}
env:
FDROID_KEYSTORE_B64: ${{ secrets.FDROID_KEYSTORE_B64 }}
FDROID_KEYSTORE_PASS: ${{ secrets.FDROID_KEYSTORE_PASS }}
run: .gitea/scripts/fdroid.sh "$VERSION"
+1
View File
@@ -17,6 +17,7 @@ appsettings.*.local.json
/tools/packaging/
/tools/android-sdk/
/tools/cache/
/tools/fdroid/
/tools/logs/
/.claude/settings.local.json
/artifacts/
+13
View File
@@ -173,6 +173,19 @@ The Android release key: alias `meweb`, certificate SHA-256
`arasaka_repository_known`, or installed copies are treated as edited by hand and left alone. The package owns
`/usr/share/arasaka-repository/meweb`, the marker that keeps the repository configured while any Arasaka package
is installed. The signing key's only copies are that secret and the owner's Nextcloud backup.
- **The APK reaches an F-Droid repository on packages.arasaka.software from the same job** (31 Oct 2026), after the
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
`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`;
the only other copy is the owner's Nextcloud, `Backups/arasaka-fdroid-repo-key/`), 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;
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
build machine's paths and `/etc/issue.net`), swaps the `fdroid/repo` link and checks the live signature. Never
regenerate the key: everyone who added the repository trusts only its fingerprint. `packaging/fdroid/` holds the
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`).
- **The server cannot ping**: `NoNewPrivileges` drops `ping`'s file capability and `ping_group_range` is closed, so
the trace back to a caller uses `tracepath` (UDP, unprivileged) and keeps only public hops outside `Probe:Refuse`,
so the box's own network never reaches an app.
+5
View File
@@ -54,6 +54,11 @@ import it first with `sudo rpm --import https://packages.arasaka.software/arasak
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).
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
`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
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
“me-web?” sends nothing anywhere unless a check needs it, and lists every host it contacts ("What this app contacts"
+11
View File
@@ -0,0 +1,11 @@
repo_url: https://packages.arasaka.software/fdroid/repo
repo_name: Arasaka
repo_description: >-
Apps published by Arasaka di Hvozdikov Yevheniy, Busto Arsizio, Italy, such as “me-web?”.
Each app here is the same signed file as on its release page.
repo_icon: icon.png
archive_older: 0
repo_keyalias: arasaka
keystore: {env: FDROID_KEYSTORE}
keystorepass: {env: FDROID_KEYSTORE_PASS}
keypass: {env: FDROID_KEYSTORE_PASS}
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

@@ -0,0 +1,10 @@
Categories:
- Connectivity
- Internet
License: AGPL-3.0-only
AuthorName: Arasaka di Hvozdikov Yevheniy
WebSite: https://meweb.arasaka.software
SourceCode: https://git.thepra.dev/Arasaka/MeWeb
IssueTracker: https://git.thepra.dev/Arasaka/MeWeb/issues
Changelog: https://git.thepra.dev/Arasaka/MeWeb/releases
AutoName: me-web?
@@ -0,0 +1 @@
“me-web?” checks your connection and your device and tells you, in plain words, what you could host at home, what stands in the way, and what would fix it. Free software, no telemetry, no account.
@@ -0,0 +1 @@
me-web?
@@ -0,0 +1 @@
Can you run your own server from here?
@@ -0,0 +1 @@
«me-web?» controlla la sua connessione e il suo dispositivo e le dice, in parole semplici, che cosa potrebbe ospitare a casa, che cosa lo impedisce e che cosa lo risolverebbe. Software libero, niente telemetria, nessun account.
@@ -0,0 +1 @@
me-web?
@@ -0,0 +1 @@
Può ospitare un server da qui?
File diff suppressed because it is too large. Load diff
+18 -3
View File
@@ -9,6 +9,9 @@ namespace MeWeb.Server;
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 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 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()
{
@@ -20,6 +23,11 @@ public static class DownloadPage
["None"] = "There is no release to download yet.",
["Android"] = "Android",
["AndroidNote"] = "Your phone will ask whether the browser may install apps. The app is signed; its certificate's SHA-256 is",
["Updates"] = "To get updates without coming back here, add the Arasaka repository to F-Droid (or a store like it), or add the app to Obtainium. Either way you get the same signed file.",
["AddFDroid"] = "Add to F-Droid",
["AddObtainium"] = "Add to Obtainium",
["RepoAddress"] = "The repository's address is",
["RepoKey"] = "and the SHA-256 of its key is",
["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”.",
["Linux"] = "Linux",
@@ -43,6 +51,11 @@ public static class DownloadPage
["None"] = "Non c'è ancora una versione da scaricare.",
["Android"] = "Android",
["AndroidNote"] = "Il telefono chiederà se il browser può installare app. L'app è firmata; lo SHA-256 del suo certificato è",
["Updates"] = "Per ricevere gli aggiornamenti senza tornare qui, aggiunga il repository Arasaka a F-Droid (o a uno store simile), oppure aggiunga l'app a Obtainium. In entrambi i casi riceve lo stesso file firmato.",
["AddFDroid"] = "Aggiungere a F-Droid",
["AddObtainium"] = "Aggiungere a Obtainium",
["RepoAddress"] = "L'indirizzo del repository è",
["RepoKey"] = "e lo SHA-256 della sua chiave è",
["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”.",
["Linux"] = "Linux",
@@ -76,7 +89,9 @@ public static class DownloadPage
{
var version = release.Tag.TrimStart('v');
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 class=\"soft\">{E(words["RepoAddress"])} <code>{Endpoints.FDroid.Repository}</code> {E(words["RepoKey"])} <code>{RepositoryKey}</code>.</p>");
Block(html, release, words["Windows"], [("windows", ".exe")], E(words["WindowsNote"]));
var deb = ReleaseFeed.Asset(release, "linux-deb")?.Name;
var rpm = ReleaseFeed.Asset(release, "linux-rpm")?.Name;
@@ -91,7 +106,7 @@ public static class DownloadPage
return html.ToString();
}
static void Block(StringBuilder html, GiteaRelease release, string title, (string Platform, string Label)[] files, string note)
static void Block(StringBuilder html, GiteaRelease release, string title, (string Platform, string Label)[] files, string note, string more = "")
{
html.Append($"<div class=\"card\"><h2>{E(title)}</h2>");
foreach (var (platform, label) in files)
@@ -100,7 +115,7 @@ public static class DownloadPage
if (asset is null) continue;
html.Append($"<a class=\"button\" href=\"{Endpoints.Downloads.GET_M_Platform(platform)}\">{E(label)} · {asset.Size / 1048576.0:0} MB</a>");
}
html.Append($"<p class=\"soft\">{note}</p></div>");
html.Append($"<p class=\"soft\">{note}</p>{more}</div>");
}
static string E(string text) => WebUtility.HtmlEncode(text ?? "");
+4
View File
@@ -21,6 +21,8 @@ public static class PrivacyPage
"To see how your router or your provider translates addresses for calls and games, the app sends one short message to stun.nextcloud.com, run by Nextcloud GmbH in Germany, which sees your public address as any server you contact does, and about twenty to stun.meweb.arasaka.software, run by the publisher, some of them two minutes apart to see how long an idle connection stays open.",
"## Updates on Linux",
"If you install the Linux package, it adds the Arasaka package repository to your system, and your package manager then checks packages.arasaka.software for updates, as it does with every repository it knows. That server keeps no access log.",
"## Updates on Android, if you choose them",
"If you add the Arasaka repository to F-Droid or a store like it, or add the app to Obtainium, that app checks packages.arasaka.software for updates. That server keeps no access log.",
"## Nothing else",
"No cookies, no analytics, no advertising, no crash reports. The app keeps your earlier checks on your device only, and you can forget them in the app.",
"## Your rights",
@@ -40,6 +42,8 @@ public static class PrivacyPage
"Per vedere come il router o l'operatore traducono gli indirizzi per chiamate e giochi, l'app invia un breve messaggio a stun.nextcloud.com, gestito da Nextcloud GmbH in Germania, che vede il suo indirizzo pubblico come qualsiasi server che lei contatta, e una ventina a stun.meweb.arasaka.software, gestito dall'editore, alcuni a due minuti di distanza per vedere quanto resta aperta una connessione inattiva.",
"## Gli aggiornamenti su Linux",
"Se installa il pacchetto per Linux, questo aggiunge al sistema il repository dei pacchetti Arasaka, e il gestore dei pacchetti controlla poi gli aggiornamenti su packages.arasaka.software, come fa con ogni repository che conosce. Quel server non tiene registri di accesso.",
"## Gli aggiornamenti su Android, se li sceglie",
"Se aggiunge il repository Arasaka a F-Droid o a uno store simile, oppure aggiunge l'app a Obtainium, quell'app controlla gli aggiornamenti su packages.arasaka.software. Quel server non tiene registri di accesso.",
"## Nient'altro",
"Niente cookie, niente statistiche, niente pubblicità, niente rapporti sugli arresti anomali. L'app conserva le verifiche precedenti solo sul suo dispositivo, e può dimenticarle dall'app.",
"## I suoi diritti",
+8
View File
@@ -74,4 +74,12 @@ public static class Endpoints
public const string Releases = $"{Origin}/{Repository}/releases";
public const string Source = $"{Origin}/{Repository}";
}
public static class FDroid
{
public const string Repository = "https://packages.arasaka.software/fdroid/repo";
public const string Fingerprint = "42C47659B699E0B1A8BCF6F9CE2C4972972265B592BE32B5725B8F87C6BD0374";
public const string Add = $"{Repository}?fingerprint={Fingerprint}";
public const string Obtainium = "obtainium://app/";
}
}
+12
View File
@@ -47,6 +47,18 @@
"kind": "tar",
"dest": "tools/jdk/osx-arm64",
"check": "tools/jdk/osx-arm64/jdk-21.0.12+8/Contents/Home/bin/java"
},
{
"name": "uv",
"group": "fdroid",
"version": "0.13.1",
"license": "MIT or Apache-2.0 (build tool, not distributed)",
"rid": "linux-x64",
"url": "https://github.com/astral-sh/uv/releases/download/0.13.1/uv-x86_64-unknown-linux-gnu.tar.gz",
"sha256": "eadd9d27c134ab5ac1ffd4473b97a741407f3ea238288f03121fc4deddf90220",
"kind": "tar",
"dest": "tools/fdroid/linux-x64",
"check": "tools/fdroid/linux-x64/uv-x86_64-unknown-linux-gnu/uv"
}
]
}
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
set -e
. "$(dirname "$0")/sdk.sh"
exec dotnet run tools/setup.cs -- "$@"