dotfiles
Apps

Gemini: a web app installed by Chrome's policy

Edit on GitHub

modules/nixos/desktop/web-apps.nix. Upstream: https://gemini.google.com/app.

Why a web app

Google's Gemini desktop app exists for macOS (15/04/2026) and Windows (10/09/2026) only. Nothing for Linux in nixpkgs either: nix search nixpkgs gemini lists the gemini:// protocol browsers and the CLIs. The "Gemini desktop" repos on GitHub are an Electron shell around the same site, with their own login and one more third-party binary for no gain.

So the site, in a window of its own, from Chrome dev: the browser where the Google account is already signed in.

Why the policy, and not --app=

Measured on 04/11/2026:

LaunchWindow classLogin
google-chrome-unstable --app=https://gemini.google.com/appGoogle-chrome-unstable: the window joins the running Chromeshared
the same, with --user-data-dir=<new> --class=geminigeminia second login, a second profile
an installed web app, under XWaylandstill Google-chrome-unstableshared
an installed web app, native Waylandchrome-<id>-Defaultshared

A class of its own is what the rest of the desktop keys on: the bar's icon (looked up by the class among the .desktop entries), window rules, the vault. Only the installed app, under Wayland, has one without a second profile.

Only under native Wayland. The first three rows ran under XWayland, and there the installed app turned out no better: crx_<id> goes into WM_CLASS's instance, Hyprland keys on its class, and the class stays Google-chrome-unstable. Under Wayland the app's app_id is chrome-<id>-Default, the very name of the .desktop file Chrome writes, so the bar finds its icon. Chrome 158 fell back to XWayland despite the wrapper's --ozone-platform-hint=auto (set by NIXOS_OZONE_WL), so modules/home/packages.nix passes --ozone-platform=wayland through the package's commandLineArgs.

Chrome applies the policy only while it runs. The rebuild only writes the JSON; the app appeared about 20 s after Chrome started (Gemini's id: gdfaincndogidkdcdkhapmbffkckdkhn).

WebAppInstallForceList installs it declaratively into the signed-in profile, merged into the same managed policy JSON as the spellcheck keys (/etc/opt/chrome/policies/managed/extra.json, every channel). Chrome itself writes the launcher entry and the icon into ~/.local/share/applications and ~/.local/share/icons, which are state and already persisted. Removing the entry from the list uninstalls the app at Chrome's next policy refresh.