Keyring launchers, Firefox default browser, rootless Docker

- keyring.flatpaks / keyring.programs: desktop entries shadowing an app's
  own that run it on the outer bus (one kwalletd6 for both sessions), with
  the wallet's D-Bus names as `flatpak run --talk-name` options (no Flatpak
  overrides), --password-store=kwallet6 for Electron, and login callback
  schemes as default + recommended handlers.
- firefox.defaultBrowser: the launcher as default for http, https and
  text/html.
- docker: rootless dockerd as a user service, socket in the outer runtime
  dir, CLI with DOCKER_HOST for both sessions.
This commit is contained in:
Pierre Kisters committed 2026-09-29 01:13:47 +02:00
1 parent aa9ca183f1
commit b0131338de
10 files changed
+617 -14

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
# Rootless Docker
`docker.*`, module `docker`. Options:
[README, Options](../README.md#options).
## Problem
SteamOS has no Docker, and Home Manager can't install the usual root
daemon. Rootless Docker's default socket is in `$XDG_RUNTIME_DIR`, which
differs between the Frame's [two sessions](../README.md#two-sessions), and
the nested desktop can't reach the user service manager.
## What you get
- `dockerd` in rootless mode as the systemd user service `docker.service`
of the Steam session's user manager, started at login and on switch
(never restarted by a switch, which would stop running containers).
- The `docker` CLI on `PATH`, whose default `DOCKER_HOST` is the socket in
the outer runtime dir (`docker.host`,
`unix:///run/user/1000/docker.sock`): it works in both sessions. A
`DOCKER_HOST` you set yourself wins.
SteamOS already has what rootless Docker needs: `newuidmap`/`newgidmap`
with their capabilities, `/etc/subuid` and `/etc/subgid` entries for the
user, user namespaces and cgroup v2 delegation.
## Configuration
```nix
steamFrame.docker.enable = true;
```
Then `docker run --rm hello-world`. `docker.package` replaces the Docker
package (daemon and CLI).
## Caveats
- Rootless limits apply: no ports below 1024 without extra setup,
`--network host` is the rootless network namespace, containers can't
gain real root.
- Images, containers and volumes are app data in `~/.local/share/docker`,
see [App data you create](../README.md#app-data-you-create) for how to
remove them.
- Disabling the module removes the unit but doesn't stop a running daemon:
run `systemctl --user stop docker` first (or add `docker.service` to
`session.services.stop` for that switch).
- Another `docker` package in `home.packages` collides with the wrapped
CLI.
## How it works
The user unit runs `dockerd-rootless` (RootlessKit) with `PATH=/usr/bin`,
for SteamOS's `newuidmap`/`newgidmap`, and `Delegate=yes`. The unit is
added to `session.services.start`, so each switch starts it if it isn't
running. The CLI is the package's `docker` wrapped with a default
`DOCKER_HOST`; nothing is written outside the store (no `daemon.json`, no
Docker context).
+8 -2
View File
@@ -7,8 +7,10 @@
In the Steam session gamescope never shows fullscreen windows, so Firefox
looks frozen when a page goes fullscreen; sites send AV1, which the Frame
decodes in software; and the two sessions can't see each other's Firefox,
so a second instance stops at the locked profile.
decodes in software; the two sessions can't see each other's Firefox,
so a second instance stops at the locked profile; and without a default
browser the portal opens links with the first installed `https` handler
(e.g. Chromium), in both sessions.
## What you get
@@ -26,6 +28,9 @@ ID), so default-browser associations keep working.
- **`desktopProfile`** (`"desktop"`): in the nested desktop the launcher
uses this separate profile (a normal Firefox profile with its own browser
data, created on first use). `null`: the default profile in both sessions.
- **`defaultBrowser`** (off): the launcher becomes the default for `http`,
`https` and `text/html` (Home Manager's `xdg.mimeApps`, which then owns
`~/.config/mimeapps.list`).
`prefs` and the fixes are *default* values, not user values: `about:config`
can still change them per profile, and removing one leaves nothing behind.
@@ -37,6 +42,7 @@ Changes take effect at the next start of Firefox.
steamFrame.firefox = {
enable = true;
disableAv1 = true;
defaultBrowser = true;
prefs."browser.startup.page" = 3; # restore the previous session
};
```
+128
View File
@@ -0,0 +1,128 @@
# Keyring launchers
`keyring.flatpaks.<app ID>`, `keyring.programs.<desktop ID>`, module
`keyring`. Options: [README, Options](../README.md#options).
## Problem
Apps that keep logins or passwords in the KDE wallet lose them between the
Frame's [two sessions](../README.md#two-sessions):
- **A second wallet:** the nested desktop has its own D-Bus. An app started
there starts a second `kwalletd6` on that bus; what it stores there is
invisible to the same app in the Steam session, which talks to the running
`kwalletd6` on the outer bus.
- **Electron in the Steam session:** Electron picks its keyring from
`XDG_CURRENT_DESKTOP`. In the Steam session that is `gamescope`, which it
doesn't know, so it falls back to `basic` (a local, unencrypted store) and
the login made in the desktop (stored in the wallet) is gone.
- **Flatpak permissions:** many Flatpaks may not talk to the wallet at all
(Element), or only to `org.kde.kwalletd6` while their KF6 wallet client
reads through the Secret Service `org.freedesktop.secrets` (KRDC: "Password
not found").
- **Login callbacks:** SSO logins come back through a URL scheme
(`io.element.desktop://`, `claude://`) opened by the portal. Unless the app
is the default and a recommended handler, the portal opens an app chooser,
which isn't shown in VR.
## What you get
For each listed app, a desktop entry in `~/.local/share/applications` with
the app's own desktop ID, so it replaces the Flatpak's or package's entry in
the KDE menu and the "+" menu. It starts the app
- on the outer bus (`session.busEnv`): one `kwalletd6` for both sessions;
- for Flatpaks, with `--talk-name=org.kde.kwalletd6` and
`--talk-name=org.freedesktop.secrets` as `flatpak run` options (not a
Flatpak override: they apply only to launches from this entry and are
gone with it);
- with `electron = true`, with `--password-store=kwallet6`;
- as the default and recommended handler of its `schemeHandlers`
(`xdg.mimeApps`).
Logins then survive switching between the desktop and VR windows. Changes
take effect at the next start of the app.
## Configuration
The Flatpak isn't in the Nix store, so its entry can't be read at build
time: give the fields you want in menus (`name` is required; `icon`
defaults to the app ID, as Flatpaks export it). A Flatpak (installing it is
up to you):
```nix
steamFrame.keyring.flatpaks = {
"im.riot.Riot" = {
name = "Element";
electron = true;
categories = [ "Network" "InstantMessaging" ];
schemeHandlers = [ "element" "io.element.desktop" ]; # SSO callback
};
"org.kde.krdc" = {
name = "KRDC";
fieldCode = "%u";
categories = [ "Qt" "KDE" "Network" "RemoteAccess" ];
mimeTypes = [ "x-scheme-handler/vnc" "x-scheme-handler/rdp" ]; # listed, not made default
};
};
```
A program from a Nix package: use the package's own desktop ID (without
`.desktop`), so the entry replaces the package's:
```nix
{ pkgs, ... }: {
home.packages = [ pkgs.claude-desktop ];
steamFrame.keyring.programs."com.anthropic.Claude" = {
name = "Claude";
executable = "${pkgs.claude-desktop}/bin/claude-desktop";
icon = "claude-desktop";
electron = true;
startupWMClass = "com.anthropic.Claude";
schemeHandlers = [ "claude" ]; # login callback
settings.StartupNotify = "true";
actions.NewChat = {
name = "New Chat";
args = [ ''"claude://claude.ai/new?surface=chat&source=desktop_action"'' ];
};
};
}
```
`args`, `actions.<name>.args` and `settings` are written into the entry as
given (desktop entry syntax: quote yourself). Each app's `command` (read
only) is its command line without arguments, to start it from a terminal
the same way. The fields each entry takes are in the
[options](../README.md#options).
## Caveats
- The wallet must be KDE's (`kwalletd6`, and `ksecretd` for the Secret
Service), as SteamOS ships it.
- Only launches from the entry get the fixes: a Flatpak started with plain
`flatpak run`, or a program from `~/.nix-profile/bin`, doesn't.
- `schemeHandlers` turns on Home Manager's `xdg.mimeApps`, which then owns
`~/.config/mimeapps.list`: set other defaults there too.
- A Flatpak's own entry may have fields not given here (translations,
`Keywords`, actions); add what you need through `settings` and `actions`.
## How it works
For `"im.riot.Riot" = { name = "Element"; electron = true; ... }` the entry's
command line is
```sh
env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
flatpak run --talk-name=org.kde.kwalletd6 --talk-name=org.freedesktop.secrets \
im.riot.Riot --password-store=kwallet6 %U
```
`flatpak run` connects the sandbox's D-Bus proxy to the bus in its own
environment, so the prefix (not `--env=`) moves the app to the outer bus.
`--talk-name` adds to the Flatpak's permissions for this launch only. The
entry also sets `X-Flatpak=<app ID>`. For `programs` the prefix runs
`executable` directly.
`schemeHandlers` go into `xdg.mimeApps.defaultApplications` and
`associations.added` (Added Associations, what the portal treats as
recommended), and into the entry's `MimeType=` with `mimeTypes`.
+3 -2
View File
@@ -23,8 +23,9 @@ running") and skips `reloadSystemd`.
and applies `session.services.start` / `stop` / `restart`, which other
modules fill (you can add your own units).
**Configuration:** a launcher for an app that must use the single wallet on
the outer bus (see [Two sessions](../README.md#two-sessions)):
**Configuration:** apps that keep secrets in the wallet get launchers from
[keyring](keyring.md). Another launcher that must reach the outer session
(its bus, services or wallet) uses the prefix:
```nix
{ config, ... }: {