# OneKeePass Flatpak manifest. # # Everything is built from source with no network available to any build command: # the Rust app, the proxy sidecar, and the ClojureScript frontend. Every dependency # arrives through a declared source, so what is shipped can be derived from what is # published. # # The four generated source lists must be regenerated whenever the matching lockfile # changes (see linux/README.md): # cargo-sources.json src-tauri/Cargo.lock # proxy-cargo-sources.json onekeepass-proxy/Cargo.lock # node-sources.json yarn.lock # maven-sources.json src-cljs/shadow-cljs.edn # # This lives at the repo root rather than in linux/ because --sandbox — the mode # Flathub's buildbot uses — refuses any source outside the manifest's own directory. # The Flathub manifest satisfies that by pulling the repo as one git source; here the # manifest has to sit above the trees it references instead. # # Build: flatpak run org.flatpak.Builder --force-clean --user --install build-dir \ # com.onekeepass.OneKeePass.yml # Run: flatpak run com.onekeepass.OneKeePass # # Prove it builds offline the way Flathub's buildbot does — download first, then build # with the network and the download cache both taken away: # flatpak run org.flatpak.Builder --download-only build-dir \ # com.onekeepass.OneKeePass.yml # flatpak run org.flatpak.Builder --force-clean --sandbox --disable-download \ # --user --install build-dir com.onekeepass.OneKeePass.yml id: com.onekeepass.OneKeePass ## Verified on the Fedora VM 2026-08-29: ## - org.gnome.Platform/Sdk 49 and 50 are both published; 50 chosen because GNOME ## runtimes get roughly a year of support (48 released 2025-03, EOL 2026-03), so 49 ## is close to EOL and would force an immediate bump. ## - `pkg-config --exists webkit2gtk-4.1` succeeds in org.gnome.Sdk//50, which is the ## API version Tauri v2 targets. ## - The Sdk's extension point declares `version = 25.08`, so the rust-stable branch ## is 25.08 (Rust 1.98.0) — the newest published. No version is named below because ## flatpak-builder resolves it from that extension point. ## Do not copy the runtime version from the Tauri docs; they still show 47. runtime: org.gnome.Platform runtime-version: '50' sdk: org.gnome.Sdk ## Pinned explicitly rather than the unversioned org.freedesktop.Sdk.Extension.openjdk: ## that alias tracks whatever JDK the SDK considers default and can shift between runtime ## releases. ## openjdk21 + node20 match the Mac toolchain (JDK 21.0.6, Node 20.20.2), so the ## Flatpak-built bundle corresponds to what `just build-cljs-bundle` produces there. sdk-extensions: - org.freedesktop.Sdk.Extension.rust-stable - org.freedesktop.Sdk.Extension.openjdk21 - org.freedesktop.Sdk.Extension.node20 command: OneKeePass # The compiled ClojureScript is handed from the frontend module to the Rust module # through /app (see onekeepass-cljs below); strip it from the finished app. cleanup: - /share/onekeepass-build finish-args: # Display - --socket=wayland - --socket=fallback-x11 - --share=ipc - --device=dri # Remote storage (SFTP/WebDAV) and favicon fetch - --share=network ## No Secret Service access on purpose. key_secure/linux.rs keeps each open ## database's encryption key in the freedesktop Secret Service where one is ## reachable, and in a process-local store where it is not — a fallback that ## predates the Flatpak and is what the sandbox uses. The keys are scoped to an ## open database (delete_key runs on close), so nothing is lost by holding them ## in the process. ## ## org.freedesktop.portal.Secret is the portal answer, but it is not a swap: it ## hands an app one secret, not a keyed store, so it would be used to encrypt a ## local store of the per-database keys. Worth doing, tracked separately. ## No --system-talk-name=org.freedesktop.login1, so lock-on-suspend is off here. ## power_monitor.rs takes a logind delay inhibitor and listens for PrepareForSleep, so ## an open database is encrypted before the memory image can reach a hibernation file. ## That needs the SYSTEM bus, which the sandbox does not have by default, and no portal ## replaces it: the Inhibit portal can block a suspend but never says one is starting. ## ## The permission is the one thing here that needs a flatpak-builder-lint exception, and ## the request for it sat unreviewed while the submission could not build at all. It is ## dropped so the submission can proceed; the app degrades gracefully, logging ## "lock-on-suspend: no system D-Bus, sleep locking disabled". Every other Linux package ## keeps the feature. Add the finish-arg back once the exception is granted. ## Connection::system() at power_monitor.rs:190. ## No grant for the app's own data directory on purpose. Under Flatpak the app puts ## preferences, logs, backups and wordlists under XDG_DATA_HOME, which already ## resolves to ~/.var/app//data and needs no permission. Other Linux packages ## keep using ~/.onekeepass, which is why app_home_dir() branches on is_flatpak() ## rather than changing for everyone. An earlier revision asked for ## --persist=.onekeepass here; the XDG path removed the need for it. # No --filesystem grant on purpose. Database files come through the XDG document # portal; opening, saving and the recent-databases list all work through it, because # the document id stays stable across restarts. ## Verified 2026-08-30. The recents list displays the raw /run/user//doc/... ## path rather than the user's own ~/Documents/... path — cosmetic, tracked ## separately. build-options: append-path: /usr/lib/sdk/rust-stable/bin env: # botan is vendored via onekeepass-core ## Matches the flags used by the just build-linux-* recipes BOTAN_CONFIGURE_EXTRA_CXXFLAGS: '-fPIC' ## No build-args here on purpose. flatpak-builder gives a build no network unless it is ## asked for, so the absence of --share=network is what makes this Flathub-legal — and ## what forces every dependency to be a declared source. ## Split into four modules on purpose. flatpak-builder caches per module, and a change ## anywhere in a module re-runs all of it — with everything in one module, editing an ## `install` line cost a full 21-minute cargo rebuild. The expensive Rust and ## ClojureScript builds are now isolated from the cheap file-install steps. modules: ## --------------------------------------------------------------- sidecar - name: onekeepass-proxy buildsystem: simple build-options: env: CARGO_HOME: /run/build/onekeepass-proxy/cargo sources: - type: dir path: onekeepass-proxy dest: onekeepass-proxy skip: - target - binaries # Vendored crates plus the cargo config that redirects crates.io to them. # Generated from onekeepass-proxy/Cargo.lock. - linux/proxy-cargo-sources.json build-commands: - cd onekeepass-proxy && cargo build --release --locked ## proxy_full_path() looks for current_exe().parent()/onekeepass-proxy - install -Dm755 onekeepass-proxy/target/release/onekeepass-proxy /app/bin/onekeepass-proxy ## -------------------------------------------------------------- frontend # Build the ClojureScript frontend from published source. ## Compiled here rather than shipping a bundle built on the developer's machine. The ## frontend is a Closure :advanced build — nobody can audit that against the published ## source, and for a password manager the shipped UI should be derivable from what is ## published. - name: onekeepass-cljs buildsystem: simple build-options: append-path: /usr/lib/sdk/openjdk21/bin:/usr/lib/sdk/node20/bin env: # Yarn requires a writable home and cache during the build. ## yarn keeps its cache and .yarnrc under $HOME; point it at the build dir so it ## is writable and self-contained. Note this does NOT decide where shadow-cljs ## looks for Maven artifacts — see the :local-repo command below. HOME: /run/build/onekeepass-cljs npm_config_cache: /run/build/onekeepass-cljs/npm-cache # flatpak-node-generator's node-headers source expects the node-gyp cache here XDG_CACHE_HOME: /run/build/onekeepass-cljs/flatpak-node/cache sources: - type: dir path: src-cljs dest: src-cljs skip: - .shadow-cljs - .clj-kondo - .lsp - .calva - type: dir path: resources dest: resources - type: file path: package.json - type: file path: yarn.lock # Yarn is not included in the Node SDK extension, so provide it as a pinned source. ## SDK extensions are mounted READ-ONLY, so `npm install -g yarn` fails with ENOENT ## trying to mkdir under /usr/lib/sdk/node20/lib/node_modules. Declared as a source ## instead: pinned by checksum, needs no global write, and needs no network. ## Version matches the Mac toolchain (yarn 1.22.22). - type: archive url: https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz sha256: c17d3797fb9a9115bf375e31bfd30058cac6bc9c3b8807a3d8cb2094794b51ca dest: yarn # Offline Yarn and Maven dependencies generated from their lock/configuration files. ## The npm tarballs, as a yarn offline mirror. Generated from yarn.lock. - linux/node-sources.json ## The Maven artifacts shadow-cljs resolves, as a populated local repository: ## the jars plus the poms Aether reads to rebuild the dependency graph. ## 58 jars and 113 poms. Generated by linux/flatpak-maven-generator.py. - linux/maven-sources.json build-commands: ## Invoke yarn's entry point directly through node — no PATH or shim needed. ## Point it at the mirror node-sources.json populated, then install from it alone. - node yarn/bin/yarn.js config --offline set yarn-offline-mirror $FLATPAK_BUILDER_BUILDDIR/flatpak-node/yarn-mirror - node yarn/bin/yarn.js install --offline --frozen-lockfile --non-interactive # Tell shadow-cljs where the vendored Maven repository is. This cannot be done # with HOME: shadow-cljs resolves through pomegranate/Aether, which takes the # default repository path from the JVM's user.home, and the JVM reads that from # the OS rather than the environment. Setting HOME alone leaves the resolver # looking somewhere else entirely and it would try to reach the network. # :local-repo is read from the config map, so prepending it to the map is enough. - 'sed -i "1s|^{|{:maven {:local-repo \"$FLATPAK_BUILDER_BUILDDIR/.m2/repository\"} |" src-cljs/shadow-cljs.edn' # The rm matters: if a previously built cljs-out arrived with the resources # source, a skipped or failed compile would otherwise ship it silently. ## Mirrors src-cljs/justfile's shadow-cljs-clean-release. That silent-ship ## failure is what D1 option B exists to prevent. - rm -rf resources/public/cljs-out - cd src-cljs && ../node_modules/.bin/shadow-cljs release app # cljs-out is a build INPUT to the onekeepass module (embedded by # generate_context!), not a runtime file, and each module gets a fresh build dir. # Hand it over through /app; the top-level cleanup: strips it from the final app. - mkdir -p /app/share/onekeepass-build - cp -r resources/public/cljs-out /app/share/onekeepass-build/cljs-out ## --------------------------------------------------------------- the app - name: onekeepass buildsystem: simple build-options: env: CARGO_HOME: /run/build/onekeepass/cargo # tauri.conf.json's frontendDist and externalBin are both given relative to # src-tauri, so the trees must stay siblings. sources: - type: dir path: src-tauri dest: src-tauri skip: - target - swift-lib # Editor/LSP caches. Both are gitignored, so they never reach the Flathub # build; they are skipped here only because a dir source copies the working # tree as it stands, ignore rules included or not. - .lsp - .clj-kondo - type: dir path: resources dest: resources # Vendored crates plus the cargo config that redirects crates.io — and the # onekeepass-core git dependency — to them. Generated from src-tauri/Cargo.lock. - linux/cargo-sources.json build-commands: # Take the ClojureScript built by the onekeepass-cljs module. Without this the # binary would embed whatever cljs-out the resources source happened to carry. - rm -rf resources/public/cljs-out - cp -r /app/share/onekeepass-build/cljs-out resources/public/cljs-out # tauri-build resolves externalBin during the app build, so the sidecar must # already sit at its target-triple path. Take it from /app, which the previous # module has already populated. The triple comes from rustc rather than being # written out, so the same command works on x86_64 and aarch64. - install -Dm755 /app/bin/onekeepass-proxy "onekeepass-proxy/binaries/onekeepass-proxy-$(rustc -vV | grep '^host:' | cut -d' ' -f2)" # Deliberately `cargo build`, not `cargo tauri build`: we want the binary, not # deb/rpm/AppImage bundles. The frontend (resources/public, including the cljs-out # copied in above) is embedded by tauri::generate_context! at compile time. - cd src-tauri && cargo build --release --locked # The cargo package is `onekeepass-frontend` with no [[bin]] override, so the # built binary carries that name. tauri.conf.json's mainBinaryName "OneKeePass" # is applied by `cargo tauri build` during bundling, which we skip — so rename # here, keeping `command:` and the .desktop Exec aligned. ## Also matches what the .deb ships (/usr/bin/OneKeePass). - install -Dm755 src-tauri/target/release/onekeepass-frontend /app/bin/OneKeePass ## ------------------------------------------------ resources and desktop files ## Cheap module: iterate here without touching the Rust builds above. - name: onekeepass-data buildsystem: simple sources: - type: dir path: linux dest: linux - type: dir path: resources dest: resources - type: file path: LICENSE build-commands: # Installs the Tauri resources, desktop entry, metainfo, icons and licence into # /app. The layout is Tauri's own and is easy to get subtly wrong, so it lives in # a script kept with the app rather than as install lines here. - linux/flatpak-install-data.sh