Files
nix-config/hardening/nixpaks/modules/gaming-policy.nix
T
ryan4yin | 二花 b726c28c14 feat(nixpak): sandbox Steam and Heroic in ~/Gaming (#520)
* feat(nixpak): sandbox Steam and Heroic in ~/Gaming

idols-ai stops preserving ~/.local/share/Steam, ~/.steam, ~/.config/heroic
and their game data, which must be moved into ~/Gaming before deploying.

Assisted-by: DeepSeek Harness (deepseek-flash)

* refactor(nixpak): replace steam-fix.nu with sandboxed protontricks

Assisted-by: DeepSeek Harness (claude-opus-5-5)

* fix(nixpak): hide host documents and GTK config from game sandboxes

Assisted-by: DeepSeek Harness (claude-opus-5-5)

* docs(ai): locate the Steam library under ~/Gaming

Assisted-by: DeepSeek Harness (claude-opus-5-5)

* fix(umu-game): confine writes to ~/Gaming and keep foreign entries

Assisted-by: DeepSeek Harness (claude-opus-5-5)

* fix(nixpak): keep Steam and Heroic in the application launcher

The wrappers only carried bin/, so neither launcher had a desktop entry
after replacing programs.steam.

Assisted-by: DeepSeek Harness (deepseek-flash)

* fix(nixpak): give the game sandboxes a fontconfig that sees CJK fonts

Steam rendered Chinese as tofu: the FHS layers build their own /etc/fonts,
whose config lists only DejaVu and the XDG font directory, so neither the
host fonts nor the FHS /usr/share/fonts were ever scanned. Point
FONTCONFIG_FILE at a small config that includes /usr/share/fonts, and ship
WenQuanYi Zen Hei and Noto Sans CJK in all three FHS layers.

Assisted-by: DeepSeek Harness (deepseek-flash)

* fix(nixpak): grant the game launchers a StatusNotifier tray icon

Steam and Heroic register a tray icon over D-Bus, and the game sandboxes
granted no StatusNotifierWatcher access, so the desktop shell had nothing
to show for them. Enable desktopIntegration.tray for all three launchers
and teach the policy suites to expect it.

Assisted-by: DeepSeek Harness (deepseek-flash)

* fix(nixpak): drop the duplicate host CJK font and tidy the font overrides

Steam reads the fonts inside its own FHS layer, so the system-wide wqy_zenhei
added for it no longer serves any sandbox. Give gaming-policy its own comment
for FONTCONFIG_FILE instead of leaving it under the compat-tool one, make the
desktop-base value a default, and note the SNI tray grant in the README table.

Assisted-by: DeepSeek Harness (deepseek-flash)
2026-11-06 02:18:33 -06:00

101 lines
3.6 KiB
Nix

# Shared policy for game launchers. Every launcher sees the same home: ~/Gaming
# is mounted over $HOME, so game installs (~/Gaming/Games, the sandbox's
# ~/Games where launchers put them by default), Proton builds, the umu runtime
# and saves all live in that one directory. Nothing else in the host home is
# visible. All launchers share that data, so they are one trust domain: keep
# their grants identical here rather than per launcher.
{
lib,
pkgs,
sloth,
...
}:
let
# The FHS layers Steam, Heroic and umu build have their own /etc/fonts, whose
# config lists only DejaVu and the XDG font directory, so the CJK fonts those
# layers ship in /usr/share/fonts were never scanned (Steam rendered tofu).
# Include both that directory and the host conf.d snippet that lists the host
# font directories: Steam's CEF only reads the FHS fonts by family name,
# while Wine and Heroic's Electron use the host families too.
fontsConf = pkgs.writeText "gaming-fonts.conf" ''
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
<include ignore_missing="yes">/etc/fonts/conf.d</include>
<dir>/usr/share/fonts</dir>
<dir prefix="xdg">fonts</dir>
<cachedir prefix="xdg">fontconfig</cachedir>
</fontconfig>
'';
in
{
imports = [
./desktop-base.nix
./network.nix
];
# Games write saves straight into $HOME (~/.factorio, ~/.renpy, ...), which
# per-app XDG directories would silently discard on exit.
xdgStorage.enable = false;
pasta.mode = "isolate";
# Steam and Heroic register a StatusNotifierItem for their tray icon; without
# these grants the desktop shell has nothing to show for it (the same reason
# WeChat's tray icon needed them).
desktopIntegration.tray = true;
portalGroups = {
openUri = true;
inhibit = true;
inputMethod = true;
};
bubblewrap = {
bind.rw = [
[
(sloth.mkdir (sloth.concat' sloth.homeDir "/Gaming"))
sloth.homeDir
]
];
# Wine and the Steam client talk X11 (XWayland).
sockets.x11 = true;
bind.ro = [
# 32-bit games and Proton's 32-bit half; nixpak maps only the 64-bit
# /run/opengl-driver.
"/run/opengl-driver-32"
# pressure-vessel resolves the user and D-Bus needs a machine ID. The
# buildFHSEnv layer takes both from this /etc.
"/etc/passwd"
"/etc/group"
"/etc/machine-id"
# Flatpak's --device=all sysfs set minus /sys/block: Wine reads the CPU
# topology from /sys/devices/system/cpu, and SDL, Wine's winebus and
# pressure-vessel enumerate GPUs and controllers through sysfs and udev.
"/sys/bus"
"/sys/class"
"/sys/dev"
"/sys/devices"
"/run/udev/data"
"/run/udev/control"
];
# Joystick evdev nodes only, hard-linked by the host udev rules in
# ../default.nix; the whole /dev/input would expose every keyboard.
bind.dev = [
[
"/dev/gamepad-input"
"/dev/input"
]
];
env = {
DISPLAY = sloth.envOr "DISPLAY" "";
XAUTHORITY = sloth.envOr "XAUTHORITY" (sloth.concat' sloth.homeDir "/.Xauthority");
# See fontsConf: the FHS layers' own /etc/fonts never scans their
# /usr/share/fonts, and desktop-base's value points there.
FONTCONFIG_FILE = "${fontsConf}";
# Proton builds that Steam offers per game and protontricks resolves, as
# programs.steam.extraCompatPackages would set. Store paths: a game can
# read them but not modify them. DW-Proton is WA-019.
STEAM_EXTRA_COMPAT_TOOLS_PATHS = lib.makeSearchPathOutput "steamcompattool" "" [
pkgs.dwproton-bin
pkgs.proton-ge-bin
];
};
};
}