From app-gtm-release
Distribute apps through alternative channels beyond mainstream stores. Android: F-Droid, GitHub Releases (Obtainium-compatible), IzzyOnDroid, direct APK. Linux desktop: Flathub (Phase 2). Covers reproducible builds, fdroiddata metadata, AppStream metadata for Flathub, flatpak-builder manifests, and PR workflow to flathub/flathub. Use this skill when the user asks about F-Droid publishing, open source app distribution, APK distribution outside Play Store, Obtainium, reproducible builds, FOSS app stores, fdroiddata, IzzyOnDroid, self-hosted repos, sideloading, distributing without Google Play, Flathub, flatpak, flatpak-builder, AppStream, 'publish to Flathub', or 'Linux desktop FOSS distribution'.
How this skill is triggered — by the user, by Claude, or both
Slash command
/app-gtm-release:alt-distributionThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- TODO: framework-agnostic split for Android-focused sections (F-Droid, Obtainium, IzzyOnDroid) — Phase 3+ when MAUI/KMP need Android alt-distribution. The Flathub section (Phase 2) is already framework-agnostic since Flatpak builds anything that compiles on Linux. -->
Not every app belongs on the mainstream stores. FOSS projects, privacy-focused apps, and developers who want to avoid commission/review processes have several established distribution channels.
This skill covers two domains:
Is your app fully open source (FOSS)?
├── Yes → F-Droid is your primary target
│ ├── Pure FOSS (no proprietary deps)? → Official F-Droid repo
│ └── Has some non-free deps? → IzzyOnDroid repo
│
└── No (or mixed)
├── Want direct-to-user distribution? → GitHub Releases + Obtainium
├── Want a store without Google account? → Uptodown, Aurora Store (read-only)
└── Want full control? → Self-hosted F-Droid repo
| Channel | Review | Cost | Requirements | Audience |
|---|---|---|---|---|
| F-Droid (official) | Yes (build verification) | Free | FOSS license, reproducible builds, no proprietary deps | FOSS/privacy community |
| IzzyOnDroid | Lighter review | Free | Open source, can have some non-free deps | Broader FOSS community |
| GitHub Releases | None | Free | GitHub repo, APK artifact | Developers, Obtainium users |
| Uptodown | Editorial review | Free | APK upload | Global, no geo-restrictions |
| Self-hosted repo | None | Hosting cost | F-Droid server setup | Your users only |
Read references/fdroid.md for the complete submission process.
F-Droid builds your app from source on their infrastructure. This means:
Flutter apps face unique challenges on F-Droid:
| Challenge | Solution |
|---|---|
| Flutter SDK not in F-Droid buildserver | Use flutter build type in fdroiddata metadata |
| Google Play Services deps | Replace with FOSS alternatives (e.g., unifiedpush instead of FCM) |
| Firebase dependencies | Remove or replace with self-hosted alternatives (Supabase, Appwrite) |
| Proprietary fonts | Use bundled open fonts or system fonts |
| Dart obfuscation | Not needed — F-Droid builds are open source |
| AAB format | F-Droid uses APK, not AAB |
| Proprietary | FOSS Alternative | Package |
|---|---|---|
| Firebase Auth | Supabase Auth, Appwrite | supabase_flutter, appwrite |
| Firebase Crashlytics | Sentry (self-hosted) | sentry_flutter (with self-hosted Sentry) |
| Firebase Analytics | Plausible, Matomo | Custom HTTP integration |
| Google Maps | OpenStreetMap | flutter_map + latlong2 |
| FCM Push | UnifiedPush, ntfy | unifiedpush |
| Google Sign-In | OAuth2 (generic) | flutter_appauth |
| Play Billing | None needed (FOSS = free) | — |
If you want both F-Droid and Google Play, use flavors to strip proprietary deps:
// android/app/build.gradle
android {
flavorDimensions "store"
productFlavors {
fdroid {
dimension "store"
applicationIdSuffix ".fdroid"
}
playstore {
dimension "store"
// Google Play Services included
}
}
}
// lib/main_fdroid.dart — no Firebase, no Google Play Services
void main() => bootstrap(
store: Store.fdroid,
analytics: NoOpAnalytics(), // No tracking
crashReporter: SelfHostedSentry(), // Self-hosted only
);
// lib/main_playstore.dart — full Google ecosystem
void main() => bootstrap(
store: Store.playstore,
analytics: FirebaseAnalytics(),
crashReporter: SentryCrashReporter(),
);
The simplest distribution: build APK, attach to GitHub release. Users install via Obtainium, which auto-updates from your releases.
Build a signed APK (not AAB — AAB is Google Play only):
flutter build apk --release
Create a GitHub Release:
# Tag and release
git tag -a v1.0.0 -m "Release 1.0.0"
git push origin v1.0.0
# Attach APK to release
gh release create v1.0.0 \
build/app/outputs/flutter-apk/app-release.apk \
--title "v1.0.0" \
--notes "Release notes here"
Users add to Obtainium:
https://github.com/youruser/yourappAdd to your existing pipeline (Codemagic or GitHub Actions):
# .github/workflows/release.yml
name: Release APK
on:
push:
tags: ['v*.*.*']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- uses: subosito/flutter-action@v2
with:
flutter-version-file: pubspec.yaml
cache: true
- run: flutter pub get
- name: Decode keystore
run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > android/app/upload-keystore.jks
- name: Build signed APK
run: flutter build apk --release
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: build/app/outputs/flutter-apk/app-release.apk
generate_release_notes: true
Now every git tag automatically produces a signed APK on GitHub Releases.
A popular F-Droid-compatible repository with lighter requirements than official F-Droid.
Users who have IzzyOnDroid repo added in their F-Droid client (Droid-ify, Neo Store) will see your app.
For organizations that want a private app store for internal distribution.
# Install fdroidserver
pip install fdroidserver
# Initialize repo
fdroid init
# Add your APK
cp your-app.apk repo/
fdroid update
# Serve via any web server (nginx, S3, GitHub Pages)
Users add your repo URL in their F-Droid client to access apps.
If your startup was incubated or accelerated through a DojoCodingLabs hackathon or the DojoOS Launchpad, you can distribute through the DojoCodingLabs Startups Marketplace — a curated F-Droid-compatible repository.
You publish on your own repo. The marketplace fetches from there.
Publish a signed release APK on your GitHub repo:
flutter build apk --release
git tag -a v1.0.0 -m "Release 1.0.0"
git push origin v1.0.0
gh release create v1.0.0 build/app/outputs/flutter-apk/app-release.apk
Open an issue on DojoCodingLabs/startups-android-marketplace with your repo URL
A maintainer adds your app to the marketplace index — you never touch the marketplace repo
Future updates are automatic — publish a new release on your repo, the marketplace picks it up
See the full submission guide for details.
Starting September 2026, Google's Android Developer Verification Program may restrict sideloading on stock Android. The DojoCodingLabs marketplace continues working on custom ROMs (GrapheneOS, CalyxOS, LineageOS) without restrictions. See the impact assessment.
Flathub is the de facto Linux desktop app store. Unlike Snap (centralized, Canonical-controlled, requires snapd), Flatpak (the underlying tech) is decentralized and bundle-portable. Most major Linux distros ship with Flathub support out of the box (Fedora, Ubuntu via PPA, Linux Mint, Pop!_OS, Endless OS, elementary OS).
Pair, don't pick:
| Property | Flathub | Snap Store |
|---|---|---|
| Distros pre-shipped with support | Fedora, Mint, Pop!_OS, Endless, elementary, EndeavorOS, Manjaro | Ubuntu, Ubuntu Core |
| Sandboxing | Bubblewrap + portals | AppArmor + seccomp |
| Update model | Decentralized (anyone can host a remote) | Centralized (snapcraft.io only) |
| Submission gate | Manual review by Flathub maintainers (PR-based) | Automated review (strict) or 1-2 weeks (classic) |
| Confinement override | "permissions" in manifest | "classic" confinement (manual approval) |
| Format | OCI container with Flatpak runtime | SquashFS image with snapd metadata |
For maximum Linux desktop reach: ship to both Flathub AND Snap Store. Different audiences, different distros.
A Flatpak app is defined by a manifest in YAML or JSON. Filename convention: com.example.MyApp.yml (reverse-DNS).
# com.example.MyApp.yml — minimal Flutter desktop example
app-id: com.example.MyApp
runtime: org.freedesktop.Platform
runtime-version: '24.08' # Flatpak runtime; check https://docs.flatpak.org/en/latest/available-runtimes.html
sdk: org.freedesktop.Sdk
command: myapp
finish-args:
- --share=network # outbound network
- --socket=wayland # Wayland display
- --socket=fallback-x11 # X11 fallback
- --socket=pulseaudio # audio
- --device=dri # GPU
- --filesystem=home # home dir access
- --talk-name=org.freedesktop.Notifications # desktop notifications via D-Bus
modules:
- name: myapp
buildsystem: simple
build-commands:
- install -Dm755 myapp -t /app/bin/
- install -Dm644 myapp.desktop -t /app/share/applications/
- install -Dm644 com.example.MyApp.appdata.xml -t /app/share/metainfo/
- install -Dm644 icon-256.png /app/share/icons/hicolor/256x256/apps/com.example.MyApp.png
sources:
- type: archive
url: https://github.com/example/myapp/releases/download/v1.0.0/myapp-linux-x86_64.tar.gz
sha256: <sha256 of the release archive>
Key fields:
| Field | Purpose |
|---|---|
app-id | Reverse-DNS unique identifier (matches Flathub repo name) |
runtime | Base runtime (Freedesktop, GNOME, KDE) |
runtime-version | Pinned version (24.08 = Sep 2024 release) |
sdk | SDK matching the runtime |
command | Executable name |
finish-args | Sandbox permissions (analogous to snap plugs) |
modules | Build steps + sources |
Flathub requires an AppStream .appdata.xml (or .metainfo.xml) file describing the app for the store listing. This is shared with KDE Discover, GNOME Software, and other Linux app browsers.
com.example.MyApp.appdata.xml:
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>com.example.MyApp</id>
<name>My App</name>
<summary>Short tagline, ≤ 80 chars</summary>
<description>
<p>First paragraph. Markdown not supported; use plain text or HTML-like inline.</p>
<p>Second paragraph if needed.</p>
</description>
<metadata_license>CC0-1.0</metadata_license>
<project_license>BSL-1.1</project_license>
<developer id="com.example">
<name>Example Inc.</name>
</developer>
<url type="homepage">https://example.com</url>
<url type="bugtracker">https://github.com/example/myapp/issues</url>
<url type="help">https://example.com/help</url>
<url type="vcs-browser">https://github.com/example/myapp</url>
<launchable type="desktop-id">com.example.MyApp.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://example.com/screenshots/main.png</image>
<caption>Main view</caption>
</screenshot>
</screenshots>
<releases>
<release version="1.0.0" date="2026-04-25">
<description>
<p>Initial Flathub release.</p>
</description>
</release>
</releases>
<content_rating type="oars-1.1" />
<categories>
<category>Office</category>
<category>Productivity</category>
</categories>
<provides>
<binary>myapp</binary>
</provides>
</component>
Validate with appstreamcli:
appstreamcli validate com.example.MyApp.appdata.xml
Standard freedesktop.org .desktop file:
[Desktop Entry]
Name=My App
Comment=Tagline
Exec=myapp
Icon=com.example.MyApp
Terminal=false
Type=Application
Categories=Office;Productivity;
StartupWMClass=myapp
Filename matches app-id: com.example.MyApp.desktop.
Install flatpak + flatpak-builder:
sudo apt install flatpak flatpak-builder
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08
Build:
flatpak-builder --user --install --force-clean build-dir com.example.MyApp.yml
flatpak run com.example.MyApp
Output: app installed locally for testing.
For a .flatpak bundle (single-file install):
flatpak-builder --repo=repo --force-clean build-dir com.example.MyApp.yml
flatpak build-bundle repo myapp.flatpak com.example.MyApp
Flathub does NOT have a dashboard. Submission is via GitHub PR:
com.example.MyApp/Add com.example.MyAppFlathub gives you a dedicated repo flathub/com.example.MyApp (separate from the submissions repo) once your initial PR merges. Updates flow through that repo:
com.example.MyApp.yml in flathub/com.example.MyAppfinish-args declares what your app can access. Like snap plugs, but more granular. Common args:
| finish-arg | What it grants |
|---|---|
--share=network | Outbound network |
--share=ipc | X11 IPC (most apps need) |
--socket=wayland | Wayland display |
--socket=fallback-x11 | X11 fallback |
--socket=pulseaudio | Audio |
--device=dri | GPU access |
--device=all | All devices (avoid; use specific) |
--filesystem=home | Read/write home dir |
--filesystem=xdg-documents | XDG Documents only |
--filesystem=host | Full filesystem (avoid; reviewers will reject) |
--talk-name=org.freedesktop.Notifications | D-Bus notifications |
--system-talk-name=org.freedesktop.UPower | System D-Bus access |
Reviewers reject overly broad args. Default minimal; add only what you need.
| Concern | Flathub | Snap Store |
|---|---|---|
| Build system | flatpak-builder + manifest | snapcraft + snapcraft.yaml |
| Submission | GitHub PR (1-4 week review) | snapcraft upload (automated review minutes; classic 1-2 weeks) |
| Sandbox | finish-args (granular) | plugs (named capabilities) |
| Default audiences | Fedora, Pop!_OS, Mint users | Ubuntu users |
| Auto-update | Yes (via flatpak update) | Yes (snapd refresh) |
| Centralized hosting | Yes (flathub.org) | Yes (snapcraft.io) |
| Self-hosted alternative | Yes (any HTTP server can host a Flatpak repo) | No (snapd only trusts snapcraft.io) |
For maximum Linux desktop reach, ship to both.
When the user has a Linux desktop app:
/app-gtm-release:ship-snap (Phase 1) for Snap Storesnap/local/... is similar to Flatpak's input)In Phase 3+, /ship-everywhere may add Flathub as a parallel child to ship-snap when Linux desktop is detected.
Choose based on your goals:
| Goal | Primary | Secondary |
|---|---|---|
| DojoCodingLabs startup | DojoCodingLabs Marketplace | GitHub Releases |
| Maximum FOSS reach | F-Droid | IzzyOnDroid + GitHub Releases |
| Privacy-first, some proprietary deps | IzzyOnDroid | GitHub Releases |
| Developer audience | GitHub Releases | F-Droid |
| Global reach, no geo-restrictions | Uptodown | GitHub Releases |
| Enterprise internal | Self-hosted F-Droid | — |
| Maximum reach (FOSS + mainstream) | F-Droid + Google Play (flavors) | GitHub Releases |
| Goal | Primary | Secondary |
|---|---|---|
| Maximum reach across distros | Flathub + Snap Store | AppImage on GitHub Releases |
| Ubuntu-only audience | Snap Store | Flathub |
| Fedora / non-Ubuntu audience | Flathub | AppImage |
| FOSS purist with hand-curation | Flathub + AppImage | Self-hosted Flatpak repo |
| Single-file portable | AppImage on GitHub Releases | — |
Alternative distribution fits into the existing gates:
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub dojocodinglabs/app-gtm-release-toolkit --plugin app-gtm-release