feat(beefcake,pebble): publish personal Android releases to F-Droid #1298

Open
lytedev wants to merge 4 commits from propose/element-x-personal-releases into main
Owner

See the commit messages. Already deployed to beefcake and pebble from this branch, so a deploy from a branch that lacks it would remove the android-release runner and the ntfy grants.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkqyhgPYauDRFrGNqncpC

See the commit messages. Already deployed to beefcake and pebble from this branch, so a deploy from a branch that lacks it would remove the android-release runner and the ntfy grants. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FEkqyhgPYauDRFrGNqncpC
lytedev/element-x-android's personal-release workflow builds the Rust
SDK fork in release mode and the app, then publishes the signed APK to
an F-Droid repository served by files.lyte.dev at /fdroid/repo. This
adds the runner it runs on, labelled android-release.

A separate runner, rather than the existing nixos-host one, because a
release takes an hour or more and the CI runner is deliberately single
and serialized: a release would hold every NixOS PR behind it. The
agent runners are for coding agents and are not given write access to
files.lyte.dev.

Its HOME and workdirs are on /persist (82G free) rather than the
shared runner tmpfs or the 27G root, since one release needs ~20G of
scratch, and keeping Gradle and Cargo downloads in HOME saves several
GB of downloads per release. The runner may write only to
/storage/files.lyte.dev/fdroid, through the fdroid-publishers group
on a setgid 2775 directory, so files a previous boot's DynamicUser uid
published stay replaceable.

The three runner definitions share their hostPackages list through
runnerHostPackages instead of each repeating it.

Tested by evaluation only: the unit gets DynamicUser, HOME on
/persist/cache/android-release-runner, ReadWritePaths for that, the
nix cache and the fdroid directory, SupplementaryGroups
fdroid-publishers, and the runner settings carry the /persist
workdir_parent and a 4h timeout. Not deployed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkqyhgPYauDRFrGNqncpC
feat(pebble): add an ntfy topic for personal app updates
Some checks failed
/ check-format (push) Successful in 11s
/ build (push) Has been cancelled
38e1779d23
app-updates carries notifications from the personal app release
workflows on git.lyte.dev: a release published to the F-Droid
repository at files.lyte.dev/fdroid, a release that failed, and a new
upstream release an app fork is behind. The alerts account publishes
it, like the other topics.

Kept apart from infra-alerts so app churn never mutes a pager, as
with hardware-deals.

Tested by evaluation only; not deployed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkqyhgPYauDRFrGNqncpC
chore(secrets): back up the personal release signing keys
All checks were successful
/ check-format (push) Successful in 12s
/ build (push) Successful in 10m48s
2607993178
The PKCS12 keystores and passwords that sign Element X lytedev's APKs
and the index of the F-Droid repository at
files.lyte.dev/fdroid/repo. The release workflow reads them from
lytedev/element-x-android's Forgejo Actions secrets; this file is the
copy that survives losing Forgejo.

Android only installs an update signed with the same key as the
installed app, and F-Droid clients pin the repository key, so losing
either key means uninstalling the app or re-adding the repository.

Encrypted to daniel's key alone through the generic secrets/<file>
creation rule: no host needs them.

Tested: decrypting the app keystore's base64 gives a file identical
to the generated keystore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkqyhgPYauDRFrGNqncpC
feat(pebble): accept UnifiedPush notifications on ntfy
All checks were successful
/ check-format (push) Successful in 11s
/ build (push) Successful in 10m10s
31bc554b4f
Element X from the personal F-Droid repository has no Firebase, so it
gets push notifications only through UnifiedPush, with the ntfy app on
the phone as the distributor. The homeserver pushes through ntfy's
Matrix gateway without credentials, which deny-all refuses, so the
anonymous user gets write-only access to the up* topics the ntfy app
creates, as https://docs.ntfy.sh/config/#example-unifiedpush
recommends. Anyone can therefore publish into a topic whose random name
they know; nobody but alerts can read one.

The alternative, granting each topic by name, would need a provisioning
step every time an app registers on the phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkqyhgPYauDRFrGNqncpC
All checks were successful
/ check-format (push) Successful in 11s
Required
Details
/ build (push) Successful in 10m10s
Required
Details
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin propose/element-x-personal-releases:propose/element-x-personal-releases
git switch propose/element-x-personal-releases
Sign in to join this conversation.
No reviewers
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
lytedev/nix!1298
No description provided.