Publish your app for FlickOS
The other guides teach how FlickOS’s own packages are made. This one is for your app: a program you wrote and keep in your own public repository, which FlickOS’s users then find in Software, install with one click and keep up to date with their automatic updates.
FlickOS calls these community apps. They are ordinary .deb packages in
FlickOS’s apt repository, in a distribution of their own,
trixie-community. You never upload a package: you point FlickOS at a tagged
commit of your public source, FlickOS’s CI builds it and checks it, the
maintainer reads it, and only then is it signed and published.
- What you need: a program that runs on Debian 13 (trixie) and a public git repository (GitHub, Codeberg, GitLab…). The example here is Tally, a small GTK 3 counter in one Python file.
- What it costs: nothing. FlickOS takes no share of anything.
- Before this guide: Building a .deb teaches every file in
debian/. This guide only says what is different for a community app. - A style, not an app? It is published the same way. Step 12 says what is different.
Contents
Section titled “Contents”- What you get, and what you agree to
- Pick a name
- The rules
- The desktop file
- The metainfo file
- debian/
- Build and check it yourself
- Tag and open the pull request
- The review
- Updates
- The Made for FlickOS shelf
- A style instead of an app
- Flathub instead, or as well?
- Traps
- Cheat sheet
1. What you get, and what you agree to
Section titled “1. What you get, and what you agree to”You get:
- Your app in Software on every FlickOS computer, searchable, with your icon, description and screenshots, next to Debian’s and Flathub’s apps.
- Installs that need no runtime: your package uses the system’s Python, GTK and libraries, so it costs a few hundred kB, not the hundreds of MB a first Flatpak app brings.
- Updates that arrive by themselves: FlickOS’s automatic updates install new versions of community apps like Debian’s.
- A chance at the Made for FlickOS shelf on Software’s front page.
You agree to:
- Public source. Every version is built from a tagged commit in your public repository. Its license must let FlickOS redistribute the package.
- A review of every version. The maintainer reads your first version whole and every later one by what changed. It can take days.
- The rules in step 3, checked by a script on every build.
- Withdrawal if a version turns bad: FlickOS replaces it with an empty package on every computer that has it, through the automatic updates.
Paid apps will be possible later: you sell a key on your own checkout and Software shows the price with a Buy… button. The terms will be on this site before that opens.
2. Pick a name
Section titled “2. Pick a name”The package’s name is the name of your app in apt, in the repository’s list
and in the program you install (/usr/bin/NAME). It must:
- be a valid Debian package name: lowercase letters, digits,
+,-,.; - not be a package Debian or FlickOS already has. Check with
apt-cache policy NAMEon a FlickOS computer: it must print nothing; - not start with
flickos-orflick-: those are FlickOS’s own.
Tally’s name is tally.
3. The rules
Section titled “3. The rules”These are what CI checks on every build (repo/community/check.py in the
FlickOS repository). Each one exists for the same reason: installing your
app must not be able to run your code as root, overwrite another package’s
file or remove anything. apt installs packages as root, so a package that
breaks one of these could do anything to the computer before you ever start
your app.
| Rule | What it means | Why |
|---|---|---|
| No maintainer scripts | no preinst, postinst, prerm, postrm, config, triggers |
they run as root at install time |
| No fields that act on other packages | no Pre-Depends, Conflicts, Breaks, Replaces, Provides, Enhances |
they remove or replace someone else’s package |
| Dependencies from Debian or FlickOS | Depends, Recommends, Suggests name packages apt gets from Debian’s or FlickOS’s repository |
nothing reaches the computer that nobody reviewed |
| Its own name | not Debian’s or FlickOS’s, no FlickOS prefix | step 2 |
Architecture all or amd64 |
FlickOS runs on 64-bit PCs only | |
| Files only in its own paths | see the list below | no autostart, systemd unit, D-Bus service, polkit action, udev rule or sudoers file |
| Nothing setuid or setgid, no device files | they run as root, or reach hardware | |
| No link that leaves those paths | a link to /etc would let a file land anywhere |
|
| A metainfo file without errors | /usr/share/metainfo/ID.metainfo.xml, appstreamcli validate |
it is what makes the package an app in Software (step 5) |
| Its own names | desktop files, icons and the metainfo file are named after the package or its metainfo ID (NAME, NAME-…, ID, ID-…) |
it can’t pass for another app in the menu or the icon theme |
| Desktop files run your program | Exec and TryExec name one of your own programs; no DBusActivatable |
a menu entry can’t start something else |
| No claims on sensitive types | MimeType doesn’t name x-scheme-handler/http(s), mailto, file, inode/directory, text/html, scripts or programs, or a wildcard like image/* |
your app isn’t offered as the browser, mail program or file manager |
| lintian without errors | Debian’s own package checker |
And for your source, before anything is built:
| Rule | What it means | Why |
|---|---|---|
| Everything is in your repository | no git submodules | the reviewer reads what is built |
| Everything is readable | no binary files except pictures and fonts: no .so, .pyc, .jar or archives |
compiled code is built from source in CI |
| No links out of the tree | no symbolic link to /etc/… or ../.. |
|
| The build is offline | your debian/rules runs without network |
it can’t download code nobody reviewed |
Your own paths (NAME is your package’s name):
/usr/bin/NAME /usr/bin/NAME-* the programs you run/usr/share/NAME/ /usr/lib/NAME/ /usr/libexec/NAME//usr/share/applications/*.desktop menu entries/usr/share/metainfo/*.metainfo.xml what Software shows/usr/share/icons/hicolor/… icons/usr/share/man/man1/NAME.1.gz NAME-*.1.gz/usr/share/doc/NAME//etc/xdg/NAME/ /etc/xdg/flickos/NAME.conf settings defaults/usr/share/flickos/addons/NAME/ a FlickOS add-on (keys, a panel button)A Python app keeps its modules in /usr/share/NAME/, not in
/usr/lib/python3/dist-packages/. A compiled program goes to /usr/bin/NAME
or /usr/lib/NAME/.
Your app still runs as its user without a sandbox, like most Flathub apps that may read the home folder. The rules protect the install, and the review protects the rest.
4. The desktop file
Section titled “4. The desktop file”tally.desktop, installed to /usr/share/applications/, puts the app in the
menu. Software takes its icon and categories from here when the metainfo has
none:
[Desktop Entry]Type=ApplicationName=TallyComment=Count thingsExec=tallyIcon=accessories-calculatorCategories=Utility;Keywords=count;counter;clicker;- Icon: a name from the icon theme (
accessories-calculatoris in every theme FlickOS ships), or your own icon installed as/usr/share/icons/hicolor/scalable/apps/ID.svgwithIcon=ID. - Name it after your app:
NAME.desktoporID.desktop, andExecruns your own program (tally, or/usr/lib/NAME/…), notsh -c. - Categories: one of freedesktop’s main ones decides Software’s category:
AudioVideo,Development,Education,Game,Graphics,Network,Office,Science,System,Utility.
5. The metainfo file
Section titled “5. The metainfo file”The metainfo file is what Software shows: the name, the one-line summary, the description, screenshots, who made it, the license and the versions. Without one your package installs with apt but is not an app in Software, and CI refuses it.
io.github.example.Tally.metainfo.xml, installed to /usr/share/metainfo/:
<?xml version="1.0" encoding="UTF-8"?><component type="desktop-application"> <id>io.github.example.Tally</id> <metadata_license>CC0-1.0</metadata_license> <project_license>GPL-3.0-or-later</project_license> <name>Tally</name> <summary>Count things</summary> <developer id="io.github.example"><name>Example Developer</name></developer> <description> <p>Tally counts things: people at the door, laps, push-ups. Click the plus button or press Space to add one, and the count is still there the next time you open it.</p> </description> <launchable type="desktop-id">tally.desktop</launchable> <url type="homepage">https://github.com/example/tally</url> <screenshots> <screenshot type="default"> <caption>Counting</caption> <image>https://raw.githubusercontent.com/example/tally/v1.0/screenshot.png</image> </screenshot> </screenshots> <content_rating type="oars-1.1"/> <releases> <release version="1.0" date="2026-10-09"/> </releases></component>idis reverse DNS of something you control: your domain, orio.github.USER.Appfor a GitHub account. It never changes.launchablenames your desktop file: that is how Software’s Open button and the menu find the app.- Screenshots are full
https://addresses, best from a tag (so an update can’t change an old version’s pictures). Take them on FlickOS, at about 1000 × 680, in the dark style. releases: add a line for each version, newest first.
Check it:
appstreamcli validate --no-net io.github.example.Tally.metainfo.xmlLines starting with E: are errors and fail CI. W: and I: are advice.
6. debian/
Section titled “6. debian/”Exactly as in Building a .deb, with three differences:
debian/control names only dependencies from Debian or FlickOS, and none of the forbidden fields:
Source: tallySection: utilsPriority: optionalMaintainer: Example Developer <dev@example.org>Build-Depends: debhelper-compat (= 13)Standards-Version: 4.7.0Rules-Requires-Root: noHomepage: https://github.com/example/tally
Package: tallyArchitecture: allDepends: python3, python3-gi, gir1.2-gtk-3.0, ${misc:Depends}Description: count things with one click Tally counts things: people at the door, laps, push-ups. Click the plus button or press Space to add one; the count is kept between runs.One source, one package. The source must build exactly one .deb, named
like the app.
No maintainer scripts. Don’t write debian/postinst and friends. Some
debhelper steps write them for you (a systemd unit, an init script): you
can’t ship those files anyway, but look at what your build made:
dpkg-deb --ctrl-tarfile ../tally_1.0_all.deb | tar -tOnly ./control, ./md5sums and, with files in /etc, ./conffiles may be
there.
debian/install puts each file in its own path:
tally usr/bin/tally.desktop usr/share/applications/io.github.example.Tally.metainfo.xml usr/share/metainfo/7. Build and check it yourself
Section titled “7. Build and check it yourself”Build it, then run the same checks CI runs, on a FlickOS computer (its apt knows Debian’s and FlickOS’s repositories, which the dependency rule asks):
sudo apt install appstream lintian # what the checks use, besides dpkg and aptdpkg-buildpackage -us -uc -bgit clone https://github.com/Flick-OS/flickos.git ~/FlickOS~/FlickOS/repo/community/check.py ../tally_1.0_all.debtally pass tally_1.0_all.debA failure says which rule and which file:
tally FAIL tally_1.0_all.deb files: etc/xdg/autostart/tally.desktop is outside the package's own pathsThen install it and use it the way a user would:
sudo apt install ../tally_1.0_all.debtallysudo apt remove tally8. Tag and open the pull request
Section titled “8. Tag and open the pull request”Commit everything, tag the version, push both:
git tag v1.0git push origin main v1.0git rev-parse 'v1.0^{commit}' # the full commit, 40 charactersFork FlickOS and add one file,
repo/community/apps/NAME:
git = https://github.com/example/tally.gittag = v1.0commit = 76f57d5b15ff86fa83793b36f52dec1abae4ccb5version = 1.0commitis what the tag points at. The commit, not the tag, is what is built: if the tag moves after the review, CI refuses it.versionis the one indebian/changelog.
Open a pull request with that file alone. Say in its description what the app does and who it is for, and whether AI tools wrote part of it. That doesn’t count against it here: it tells the reviewer what to look for.
9. The review
Section titled “9. The review”On the pull request, CI (Community apps) clones your repository, checks that the tag points at the commit and that the source is readable, builds it without network in a clean Debian 13 container, runs the checks and writes the result on the run’s summary page:
## tallyChecks passed.
A new app: review all of it. debian/changelog | 5 +++++ … tally | 90 ++++++++++++A red run says why: a failed rule, a build error, a version that doesn’t match. Fix it, tag a new version, and change the file in the same pull request.
The page also lists things to look at: what your menu entries say, and
lines that reach the network, run commands, decode data, write files that
start programs, use sudo or read other programs’ files such as ~/.ssh or
a browser profile. That isn’t a failure. Many apps do some of it for good
reasons; say in the pull request what each one is for, and the review goes
faster.
Then the maintainer reads your source, all of it the first time, and tries
the package CI built. Questions come as comments on the pull request. When it
is merged, CI builds it once more on main, and the maintainer signs that
build into the repository. FlickOS computers find it in Software once they
have fetched the new package lists: within a day where automatic updates
are on, or when the user runs an update.
10. Updates
Section titled “10. Updates”A new version is the same steps with a new tag:
- Change the code, add a line to the metainfo’s
releases, anddch -i -D trixie "What changed."(never by hand). - Tag
v1.1, push it. - A pull request that changes
tag,commitandversionin your app’s file.
This time CI prints what changed since the published commit, and that is what the maintainer reads. Small, focused versions are reviewed faster. A version whose published commit isn’t one of its ancestors (rewritten history) is marked and reviewed whole.
Once published, FlickOS computers with automatic updates on install it by themselves.
11. The Made for FlickOS shelf
Section titled “11. The Made for FlickOS shelf”Software’s front page has a Made for FlickOS shelf. To be considered, open an issue on the FlickOS repository once your app is published. The maintainer checks that it:
- works on FlickOS’s desktop (labwc),
- follows the dark and the light style,
- has screenshots,
- runs well on a computer with 2 GB of memory,
- sends nothing anywhere without asking the user first.
The shelf is a list on FlickOS’s server, so it changes without a new version of anything.
12. A style instead of an app
Section titled “12. A style instead of an app”A style recolors the FlickOS desktop: the panel, the terminal, the launcher and the notifications, the GTK and icon themes it names, the title bars and a wallpaper (Make FlickOS yours shows how to make one). Published as a community package, it shows in Software → Styles, where people can Try it until they log out, or Use it. Everything above holds, with these differences. The example is Dusk: Dark with an orange accent and its own title bars.
Its name is the style’s. The package’s name is also the style’s id
(the folder name), so it must be lowercase letters, digits and - only, and
it can’t be dark, light or seven, which are FlickOS’s own.
Only a style’s files, in two folders named after the package:
/usr/share/flickos/styles/dusk/ style.ini, waybar-colors.css, foot.ini, fuzzel.ini, mako.conf, wallpaper.png or .jpg/usr/share/themes/dusk/labwc/ themerc and the title bar buttons (.svg, .png, .xbm): optionalLeave out any file your style doesn’t change: with Inherits=dark it
takes the rest from Dark, and keeps getting FlickOS’s updates to it.
flickos-layout export is a good start: the archive it writes holds your
style’s folder, folded into one. A GTK or icon theme can’t be in the
package. Name one Debian has, and put its package in Depends
(greybird-gtk-theme, papirus-icon-theme…).
Name the window theme in style.ini by the package’s name:
[Style]Name=DuskComment=Dark with an orange accentInherits=darkWaybar=waybar-colors.cssFuzzel=fuzzel.iniLabwcTheme=duskThe metainfo file is an add-on, not an app. It extends the layout chooser and names the style’s folder, which is how Software knows it’s a style:
<?xml version="1.0" encoding="UTF-8"?><component type="addon"> <id>io.github.example.styles.Dusk</id> <extends>flickos-layout.desktop</extends> <name>Dusk</name> <summary>Dark with an orange accent</summary> <description><p>FlickOS's Dark style with an orange accent and orange title bars.</p></description> <icon type="stock">preferences-desktop-theme</icon> <url type="homepage">https://github.com/example/dusk</url> <developer id="io.github.example"><name>Example Developer</name></developer> <metadata_license>CC0-1.0</metadata_license> <project_license>GPL-3.0-or-later</project_license> <screenshots> <screenshot type="default"> <image>https://raw.githubusercontent.com/example/dusk/v1.0/screenshot.png</image> </screenshot> </screenshots> <custom><value key="FlickOS::style">dusk</value></custom></component>There is no desktop file and no launchable. Add a screenshot of the
whole desktop with your style in use: it is what people choose by, and the
reviewer looks at it.
debian/ is as in step 6. Depends names flickos-layouts (>= 1.53)
and the Debian packages of any themes you name:
Package: duskArchitecture: allDepends: flickos-layouts (>= 1.53), ${misc:Depends}Description: orange style for FlickOS FlickOS's Dark style with an orange accent and orange title bars.styles/dusk/* usr/share/flickos/styles/dusk/theme/* usr/share/themes/dusk/labwc/io.github.example.styles.Dusk.metainfo.xml usr/share/metainfo/check must print nothing. check.py runs FlickOS’s own flickos-layout check on your style. A warning that you can ignore on your own desktop
fails here: people install your style without seeing it first. Most often
that’s contrast (text too faint on its background, step 4 of
Make FlickOS yours) or a theme that isn’t
installed. Check it before you open the pull request:
~/FlickOS/repo/community/check.py ../dusk_1.0_all.debdusk pass dusk_1.0_all.debA style that fails says why, with the file:
dusk FAIL dusk_1.0_all.deb style: usr/share/flickos/styles/dusk/waybar-colors.css: warning: selected_fg_color on selected_bg_color is 2.8:1, below 3:1: hard to read (HIG A11Y-6)Run it on a FlickOS computer that has your themes installed: CI installs
them from your Depends first.
13. Flathub instead, or as well?
Section titled “13. Flathub instead, or as well?”Software shows Flathub’s apps too, so an app on Flathub reaches FlickOS’s users either way. Flathub is worth it when your app has a history and wants every Linux distribution. Two of its rules (checked October 2026) matter for a new app:
- New apps are generally refused without “a meaningful history of development”, real-world use and tagged versions.
- AI: generated code in the app must be disclosed, the manifest must not be AI-written, AI tools must not open or write the submission, and reviewers may refuse an app for how much was generated.
The community repository has neither rule, and its packages need no runtime. Many apps can start here and go to Flathub later.
14. Traps
Section titled “14. Traps”- A moved tag. Never move or delete a tag that is in a pull request or published. Make a new one.
- Downloading during the build.
pip install,npm installorcurlindebian/rulesfails: the build has no network. Use Debian’s packages asBuild-Depends, or put the code in your repository. - Prebuilt files in the repository. A
.so,.pyc,.jaror a zip stops the build: everything that runs is built from source. Pictures and fonts are fine. Minified code isn’t refused, but the reviewer can’t read it and will ask for the source it came from. - A dependency from elsewhere. A package only your PPA or a vendor’s
repository has fails the dependency rule. Bundle it in
/usr/lib/NAME/, if its license allows, or ask for it in Debian. - Python modules in dist-packages. Put them in
/usr/share/NAME/and add that folder tosys.pathin your program. - Autostart.
/etc/xdg/autostart/is not your path. Offer a setting in your app that writes~/.config/autostart/NAME.desktopfor that user instead, with the user’s consent. - The screenshot from
main. An address on a branch changes under published versions. Use the tag’s. - A metainfo
idthat changes. It is how Software knows your app. Pick it once. - Building on a computer with other repositories. The dependency rule asks your apt. Check on a FlickOS computer or a clean trixie, as CI does.
15. Cheat sheet
Section titled “15. Cheat sheet”appstreamcli validate --no-net ID.metainfo.xml # E: lines must be gonedpkg-buildpackage -us -uc -b # one .deb, named like the appdpkg-deb --ctrl-tarfile ../NAME_VER_all.deb | tar -t # control, md5sums (conffiles)~/FlickOS/repo/community/check.py ../NAME_VER_all.deb # what CI checksgit tag vVER && git push origin main vVERgit rev-parse 'vVER^{commit}' # for the app's filerepo/community/apps/NAME:
git = https://…/NAME.gittag = vVERcommit = FULL-40-CHARACTER-COMMITversion = VER