- app 1.20.0, appCode 465 - RELEASE_NOTES_ID points at the published v1.20.0 note - Flatpak metainfo release entry - version examples in BUILDING.md, RELEASE_OPS.md and geode/README.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Flatpak packaging for Amethyst Desktop
App ID: com.vitorpamplona.amethyst.Amethyst. Earlier bundles used two
other IDs:
- Up to v1.17.0:
com.vitorpamplona.amethyst.Desktop. Flathub forbids IDs that end in generic terms like.desktop,.appor.linux("it's fine to repeat the application name in such cases", hence.Amethyst). - v1.18.0:
com.vitorpamplona.amethyst, the Android package name. Nostr app stores (NIP-82) key an app's listing on its native ID: Zapstore lists the Android app undercom.vitorpamplona.amethyst, and Orchard, a Flatpak store, would list the Flatpak under its app ID. Both listings are signed by the same key, so one shared ID would make each store's publish overwrite the other's listing.
BUILDING.md § Uninstall + state paths explains how users move their data to
the new ID.
Release CI (.github/workflows/create-release.yml, linux-portable leg)
builds a single-file bundle from this directory on every tag and attaches it to
the GitHub Release as amethyst-desktop-<version>-linux-<x64|arm64>.flatpak.
Amethyst is not on Flathub. The manifest here repackages a prebuilt tree,
which Flathub does not accept; flathub/ has the source build. See
Flathub below.
Files
com.vitorpamplona.amethyst.Amethyst.yml: Flatpak manifest for the GitHub Release bundle. It packages the prebuilt jpackage tree from./gradlew :desktopApp:createReleaseDistributable, which bundles its own trimmed JRE (hence no openjdk extension). Build that tree on the JetBrains Runtime (see Sandbox notes), or the bundle can't draw on Wayland.com.vitorpamplona.amethyst.Amethyst.metainfo.xml: AppStream metadata. It keeps the release history: add an entry when tagging (RELEASE_OPS.md§ Pre-tag checklist). CI injects a bare entry for the version it builds if one is missing.com.vitorpamplona.amethyst.Amethyst.desktop: XDG desktop entry.icons/512/com.vitorpamplona.amethyst.Amethyst.png: 512x512 icon (copy ofdesktopApp/src/jvmMain/resources/icon.png).
Validate after editing:
appstreamcli validate --pedantic com.vitorpamplona.amethyst.Amethyst.metainfo.xml
desktop-file-validate com.vitorpamplona.amethyst.Amethyst.desktop
Both pass. --pedantic prints one hint, cid-contains-uppercase-letter: the
.Amethyst component is capitalised, which Flathub's ID rules allow. Flathub's
own linter (flatpak-builder-lint from the org.flatpak.Builder app) passed
on the manifest under the previous ID and has not been re-run since the rename;
on the built repo it reported only the missing screenshot:
flatpak install --user flathub org.flatpak.Builder
flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest com.vitorpamplona.amethyst.Amethyst.yml
flatpak run --command=flatpak-builder-lint org.flatpak.Builder repo <path-to>/repo
Local build
# 1. Build the app tree the manifest packages, on the JetBrains Runtime 21 (jbrsdk)
JAVA_HOME=/path/to/jbrsdk-21 ./gradlew :desktopApp:createReleaseDistributable
# 2. Tooling + Flathub remote (one-time)
sudo apt-get install -y flatpak flatpak-builder # or distro equivalent
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
# 3. Build + install locally
cd desktopApp/packaging/flatpak
flatpak-builder --user --install --install-deps-from=flathub --force-clean \
build-dir com.vitorpamplona.amethyst.Amethyst.yml
flatpak run com.vitorpamplona.amethyst.Amethyst
To produce the distributable single-file bundle instead (what CI ships):
flatpak-builder --user --install-deps-from=flathub --force-clean \
--repo=repo build-dir com.vitorpamplona.amethyst.Amethyst.yml
flatpak build-bundle repo amethyst.flatpak com.vitorpamplona.amethyst.Amethyst \
--runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo
Installing the bundle: flatpak install --user ./amethyst.flatpak. The
--runtime-repo baked in above lets flatpak fetch the freedesktop runtime
from Flathub automatically on the user's machine.
Runtime
org.freedesktop.Platform 26.08. Each freedesktop runtime gets about two
years of support (a new one ships every August), so bump runtime-version
yearly. CI reads the version from the manifest when it installs the runtime.
Codecs: since 25.08 the runtime declares org.freedesktop.Platform.codecs-extra
(the successor of ffmpeg-full: H.264/H.265 and friends) and flatpak installs
it with the runtime, so the manifest declares no codec extension. Don't set
GST_PLUGIN_SYSTEM_PATH in finish-args: the runtime's own value already
lists the codecs-extra plugin directory, and overriding it hides those plugins.
Sandbox notes
--socket=wayland+--socket=fallback-x11: the app draws natively on Wayland and gets X11 only on X11 sessions. This needs the bundled runtime to be the JetBrains Runtime (its AWT has a Wayland toolkit,WLToolkit; stock OpenJDK's AWT is X11-only), which release CI builds the Linux legs on. Skiko's window surface needs X11, so on Wayland the app draws through Swing with Skiko's CPU renderer (desktopApp/.../platform/WaylandDisplay.kt,AppWindow.kt).flatpak run --env=AMETHYST_X11=1 --socket=x11 com.vitorpamplona.amethyst.Amethystputs it back on XWayland.- File access is limited to Downloads and Pictures. AWT's
FileDialogcalls the GTK file chooser directly rather than the file chooser portal, so it can only browse what the sandbox can see. --talk-name=org.freedesktop.secrets:java-keyringstores the account keys through the Secret Service API.--talk-name=org.freedesktop.Notifications:NucleusNotificationDispatchersends desktop notifications over D-Bus throughnucleus.notification-linux(AWT's tray is only its fallback).--persist=.amethystand--persist=.java: the app writes accounts, drafts, scheduled posts and the local relay under~/.amethyst, and Java Preferences (relay lists, search history) under~/.java. Without home access, writes there are lost when the sandbox exits;--persistkeeps them under~/.var/app/com.vitorpamplona.amethyst.Amethyst/. Tor state and the image cache already followXDG_DATA_HOME/XDG_CACHE_HOME.- Scheduled posts only publish while the app is open.
OsSchedulerregisters a systemd user timer (or crontab entry) to publish while the app is closed, and neither can be reached from the sandbox. - The "now playing" reader (
MprisNowPlayingReader) queries other players over the session bus withdbus-send. The sandbox grants noorg.mpris.MediaPlayer2.*access, so it finds nothing in the Flatpak.
Flathub
Amethyst has not been submitted yet. flathub/ holds a manifest
that builds from source as Flathub requires, and the tools that keep it current.
Status against Flathub's
requirements:
-
Build from source: done.
flathub/com.vitorpamplona.amethyst.Amethyst.ymlcompiles the release tag offline with:desktopApp:createReleaseDistributable. Its pinned sources are the tag's tarball, the JetBrains Runtime SDK 21 (it runs Gradle and jpackage, so the bundled runtime keeps JBR's Wayland toolkit), the Gradle distribution, andgradle-sources.json: every Maven file the build resolves, for x86_64 and aarch64.flathub-offline.init.gradlereplaces every repository with that local copy. Flathub already ships a prebuilt JBR and Skiko's natives incom.prof18.feedflow, a Compose Desktop app built the same way.Checked on aarch64 with
flatpak-builder(network-less build, installs,flatpak runreaches the login screen, bundled runtime haslibawt_wlawt.so); x86_64's dependency list was captured but not built throughflatpak-builder.flatpak-builder-lintpasses the manifest and the metainfo. On the build it reports onlyappstream-external-screenshot-urlandappstream-remote-icon-not-mirrored: Debian's AppStream 1.0.5 writes media paths relative tomedia_baseurl, while the linter expects the fullhttps://dl.flathub.org/mediaURLs Flathub's own builder writes. Flathub's builder (org.flatpak.Builder) could not run nested in Docker, so that was not checked end to end.Every release:
flathub/tools/regenerate-gradle-sources.sh vX.Y.Z(about 35 minutes, both arches in Docker) rewritesgradle-sources.jsonfrom a real online build and prints the tarball's sha256 for the manifest. Flathub's update bot can't regenerate that list, so each release is a manual PR to the Flathub app repo.Some Maven dependencies ship prebuilt Linux binaries inside their jars (JNA, secp256k1, SQLite, the media player, notifications, Skiko, and a
torexecutable in kmp-tor). FeedFlow's acceptance covers the Skiko kind; a reviewer may still ask about thetorexecutable. -
Generative AI policy: not met. Flathub manifests "must not contain AI-generated or AI-assisted content", AI tools "must not open or automate Flathub submission pull requests, or generate their commit messages, descriptions, review comments, or replies", and all other AI-generated code, documentation or packaging must be disclosed. The
flathub/files were written with Claude Code (the manifest says so), so the submission must disclose it and reviewers may decline; a person opens the submission PR and answers the review. -
Screenshot: done. The metainfo lists
docs/screenshots/desktop-home.png, the 1.19.0 Linux build's home feed, from a commit-pinned URL. It landed afterv1.19.0, so submit with the next tag: thev1.19.0metainfo has none. -
Domain verification.
com.vitorpamplona.amethyst.Amethystis verified by serving the token Flathub provides athttps://vitorpamplona.com/.well-known/org.flathub.VerifiedApps.txt. -
Latest runtime: done. 26.08 is the newest freedesktop runtime.
Once accepted, Flathub creates flathub/com.vitorpamplona.amethyst.Amethyst;
copy the manifest, the init script and gradle-sources.json there, and each
update is a PR to it.
License metadata
The metainfo declares
MIT AND LGPL-2.1-or-later AND BSD-2-Clause AND Apache-2.0 AND GPL-2.0-only WITH Classpath-exception-2.0:
the expression from rpmLicenseType in desktopApp/build.gradle.kts plus the
bundled OpenJDK runtime. The manifest installs the repository LICENSE to
/app/share/licenses/com.vitorpamplona.amethyst.Amethyst/. The JRE's own notices ship
inside the jpackage tree under lib/runtime/legal/.