Split out of #335, whose Flathub half turned out to rest on a premise that does not hold. #335 reads "Flathub needs its own manifest submitted to flathub/flathub", which suggests wrapping what we already build. Flathub does not allow that, and the real shape of the work is a from-source build of the entire native stack, offline, inside the sandbox. Filing it properly rather than half-doing it in a packaging PR.
The upstream prerequisite — the AppStream MetaInfo file, which Flathub will not accept from a packager — landed in #340. Everything below is what remains.
The constraint that decides the design
From Flathub's requirements, verbatim:
All source available submissions must be built entirely from source code. This requirement applies to the main application component defined in the manifest, as well as any runtime dependencies included in the manifest.
There is no network access during the build process so all dependencies used by the application must be supplied by specifying them in the manifest as sources with publicly accessible URLs so that they can be downloaded before the build starts.
So the lazy route — unpack Openscreen-Linux-*.deb into /app — is out. Older Electron apps on Flathub do exactly that and maintainers used to wave it through on Discourse; the current documented rule is what a reviewer applies now, and betting a submission on a stale forum reply costs a rejected PR and a redo.
Two consequences worth stating plainly, because they are the reason this is not a one-afternoon task:
- Everything currently produced by a different CI workflow, or downloaded prebuilt, has to be rebuilt inside the manifest. That is most of the native stack.
- There is no local iteration on this.
flatpak-builder needs Linux; every attempt is a CI round trip on a build measured in tens of minutes.
nix/package.nix is not a shortcut, in case it looks like one: it builds the renderer and wraps system Electron, and compiles none of the native code — no compositor, no PipeWire helper, no STT. A Flatpak built to that standard would reach Flathub unable to record, which is worse than not shipping.
What has to be built from source
| Module |
Today |
In the manifest |
| Renderer + main process |
vite build |
Same, with npm deps vendored offline |
| Electron |
npm prebuilt download |
org.electronjs.Electron2.BaseApp (confirm the branch matches the chosen runtime) |
| npm dependencies |
npm ci |
flatpak-node-generator over package-lock.json |
Rust compositor (crates/compositor, crates/compositor-view-napi) |
scripts/build-linux-compositor-addon.mjs |
flatpak-cargo-generator over Cargo.lock |
PipeWire capture helper (electron/native/pipewire-capture) |
scripts/build-linux-pipewire-helper.mjs |
C module; the freedesktop SDK carries the PipeWire headers |
| ffmpeg |
prebuilt shared libs fetched by scripts/fetch-ffmpeg.mjs |
built from source, then patchelf --rename-dynamic-symbols to osff_* — the rename is not cosmetic, it is what stops the addon binding to Chromium's bundled ffmpeg, and that clash exists inside a Flatpak too |
whisper STT (electron/native/whisper-stt) |
prebuilt binaries staged from build-whisper-stt.yml artifacts by scripts/stage-whisper-stt.sh |
CMake module, Vulkan backend, needs a shader compiler in the SDK |
The STT model is not a build input — electron/stt/modelManager.ts downloads it into userData on first use, and runtime downloads are fine.
App ID and verification
com.getopenscreen.OpenScreen, already the component ID of the MetaInfo file added in #340. Not the Electron appId com.etiennelescot.openscreen: Flathub requires the ID to map to a domain the project controls, getopenscreen.com is that domain, and changing the Electron appId would move the userData directory of every existing install.
The ID differing from Electron's has one consequence to handle in the manifest: the desktop file, MetaInfo file and icon must all be renamed to the app ID, the <launchable> rewritten to match, and StartupWMClass=Openscreen kept as-is — Electron sets WM_CLASS from the product name, so that line is what associates the window with the icon.
Sandbox surface to get right
Capture is the one that decides whether this is worth shipping. Linux capture already goes exclusively through xdg-desktop-portal (X11 included — the helper has no other path), which is the sandbox-native route, so this should be the easy part. It still has to be proven on a real desktop rather than assumed.
Roughly: --socket=wayland, --socket=fallback-x11, --share=ipc, --device=dri for the Vulkan compositor, --socket=pulseaudio, --share=network for the LLM providers and the model download, --talk-name=org.freedesktop.secrets for safeStorage (without it isEncryptionAvailable() is false and saving an API key throws), and filesystem access for exports.
x86_64 only — flathub.json with only-arches, matching what build.yml produces.
Checklist
Worth deciding before starting
Whether it is worth it at all, honestly. Flathub is the main Linux discovery surface and that argument from #335 stands. Against it: this adds a fifth Linux packaging path to maintain, and its build is the only one that cannot reuse anything build.yml already does. A first submission that ships without STT — dropping the heaviest module — is a legitimate smaller target, if a degraded one, and would be much faster to land.
Split out of #335, whose Flathub half turned out to rest on a premise that does not hold. #335 reads "Flathub needs its own manifest submitted to
flathub/flathub", which suggests wrapping what we already build. Flathub does not allow that, and the real shape of the work is a from-source build of the entire native stack, offline, inside the sandbox. Filing it properly rather than half-doing it in a packaging PR.The upstream prerequisite — the AppStream MetaInfo file, which Flathub will not accept from a packager — landed in #340. Everything below is what remains.
The constraint that decides the design
From Flathub's requirements, verbatim:
So the lazy route — unpack
Openscreen-Linux-*.debinto/app— is out. Older Electron apps on Flathub do exactly that and maintainers used to wave it through on Discourse; the current documented rule is what a reviewer applies now, and betting a submission on a stale forum reply costs a rejected PR and a redo.Two consequences worth stating plainly, because they are the reason this is not a one-afternoon task:
flatpak-builderneeds Linux; every attempt is a CI round trip on a build measured in tens of minutes.nix/package.nixis not a shortcut, in case it looks like one: it builds the renderer and wraps system Electron, and compiles none of the native code — no compositor, no PipeWire helper, no STT. A Flatpak built to that standard would reach Flathub unable to record, which is worse than not shipping.What has to be built from source
vite buildorg.electronjs.Electron2.BaseApp(confirm the branch matches the chosen runtime)npm ciflatpak-node-generatoroverpackage-lock.jsoncrates/compositor,crates/compositor-view-napi)scripts/build-linux-compositor-addon.mjsflatpak-cargo-generatoroverCargo.lockelectron/native/pipewire-capture)scripts/build-linux-pipewire-helper.mjsscripts/fetch-ffmpeg.mjspatchelf --rename-dynamic-symbolstoosff_*— the rename is not cosmetic, it is what stops the addon binding to Chromium's bundled ffmpeg, and that clash exists inside a Flatpak tooelectron/native/whisper-stt)build-whisper-stt.ymlartifacts byscripts/stage-whisper-stt.shThe STT model is not a build input —
electron/stt/modelManager.tsdownloads it into userData on first use, and runtime downloads are fine.App ID and verification
com.getopenscreen.OpenScreen, already the component ID of the MetaInfo file added in #340. Not the ElectronappIdcom.etiennelescot.openscreen: Flathub requires the ID to map to a domain the project controls,getopenscreen.comis that domain, and changing the ElectronappIdwould move the userData directory of every existing install.The ID differing from Electron's has one consequence to handle in the manifest: the desktop file, MetaInfo file and icon must all be renamed to the app ID, the
<launchable>rewritten to match, andStartupWMClass=Openscreenkept as-is — Electron setsWM_CLASSfrom the product name, so that line is what associates the window with the icon.Sandbox surface to get right
Capture is the one that decides whether this is worth shipping. Linux capture already goes exclusively through
xdg-desktop-portal(X11 included — the helper has no other path), which is the sandbox-native route, so this should be the easy part. It still has to be proven on a real desktop rather than assumed.Roughly:
--socket=wayland,--socket=fallback-x11,--share=ipc,--device=drifor the Vulkan compositor,--socket=pulseaudio,--share=networkfor the LLM providers and the model download,--talk-name=org.freedesktop.secretsforsafeStorage(without itisEncryptionAvailable()is false and saving an API key throws), and filesystem access for exports.x86_64 only —
flathub.jsonwithonly-arches, matching whatbuild.ymlproduces.Checklist
osff_*rename reproduced<launchable>flathub.jsonwithonly-arches: [x86_64]flatpak-builderplusflatpak-builder-lint— the only place any of this can be verified/.well-known/org.flathub.VerifiedApps.txtfrom getopenscreen.com to claim the IDnew-prbase branch offlathub/flathub, titledAdd com.getopenscreen.OpenScreenWorth deciding before starting
Whether it is worth it at all, honestly. Flathub is the main Linux discovery surface and that argument from #335 stands. Against it: this adds a fifth Linux packaging path to maintain, and its build is the only one that cannot reuse anything
build.ymlalready does. A first submission that ships without STT — dropping the heaviest module — is a legitimate smaller target, if a degraded one, and would be much faster to land.