initial implementation: multi-user Steam shared library via bwrap overlay

Share one Steam game library across multiple Linux users with fully
isolated Proton prefixes. Uses bubblewrap to create a per-user kernel
overlay on /opt/steam/steamapps/compatdata/ so game files stay shared
while Proton prefixes are isolated per user, with no compatibility
tool selection or per-game configuration required.

Includes:
- steam-shared launcher that sets up the per-user overlay and execs
  Steam inside a bwrap mount namespace
- activate/uninstall scripts plus an add-user helper for steamshare
  group membership
- permission watcher (steam-fix-perms.path/.service) to keep ACLs
  correct under pressure-vessel's restrictive mode bits
- .desktop override that routes the system Steam launcher through
  steam-shared
- Nix flake exposing activate, uninstall, and add-user packages
- design doc and implementation plan covering the approach
This commit is contained in:
2026-04-15 09:53:09 +02:00
commit 1ece944a45
12 changed files with 1524 additions and 0 deletions

68
scripts/steam-shared.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# steam-shared — launch Steam with per-user Proton prefix isolation
#
# Uses bubblewrap to overlay /opt/steam/steamapps/compatdata/ with a
# per-user directory. Writes (Proton prefixes) go to the user's home,
# reads fall through to the shared library. Each user gets their own
# mount namespace — concurrent sessions are fully isolated.
set -euo pipefail
SHARED_LIBRARY="/opt/steam"
SHARED_COMPATDATA="$SHARED_LIBRARY/steamapps/compatdata"
OVERLAY_DIR="$HOME/.local/share/steam-shared"
OVERLAY_UPPER="$OVERLAY_DIR/upper"
OVERLAY_WORK="$OVERLAY_DIR/work"
# --- preflight checks ---
if [[ ! -d "$SHARED_COMPATDATA" ]]; then
echo "steam-shared: shared library not found at $SHARED_COMPATDATA" >&2
echo "steam-shared: run the activate script first (nix run .#activate)" >&2
exit 1
fi
if ! id -nG | grep -qw steamshare; then
echo "steam-shared: current user is not in the 'steamshare' group" >&2
echo "steam-shared: run: sudo usermod -aG steamshare $(whoami)" >&2
exit 1
fi
if ! command -v bwrap &>/dev/null; then
echo "steam-shared: bubblewrap (bwrap) is not installed" >&2
exit 1
fi
# --- detect Steam installation (prefer native over Flatpak) ---
STEAM_CMD=()
if [[ -x /usr/bin/steam ]]; then
STEAM_CMD=(/usr/bin/steam)
elif command -v flatpak &>/dev/null && flatpak info com.valvesoftware.Steam &>/dev/null; then
# grant Flatpak access to the shared library
flatpak override --user --filesystem="$SHARED_LIBRARY" com.valvesoftware.Steam 2>/dev/null
STEAM_CMD=(flatpak run com.valvesoftware.Steam)
else
echo "steam-shared: no Steam installation found" >&2
echo "steam-shared: install native Steam (pacman -S steam) or Flatpak Steam" >&2
exit 1
fi
# --- set up per-user overlay dirs ---
mkdir -p "$OVERLAY_UPPER" "$OVERLAY_WORK"
# clean stale overlayfs work dir (causes "Device or resource busy")
# overlayfs sets work/work to mode 0000 — must chmod before removing
if [[ -d "$OVERLAY_WORK/work" ]]; then
chmod -R u+rwx "$OVERLAY_WORK/work" 2>/dev/null
rm -rf "$OVERLAY_WORK/work"
fi
# --- launch Steam inside bwrap with overlay ---
exec bwrap \
--dev-bind / / \
--overlay-src "$SHARED_COMPATDATA" \
--overlay "$OVERLAY_UPPER" "$OVERLAY_WORK" "$SHARED_COMPATDATA" \
-- "${STEAM_CMD[@]}" "$@"