This commit is contained in:
ashisgreat22 2026-02-20 20:59:09 +01:00
parent 7529c0c5c4
commit 735aa76ea3
57 changed files with 3366 additions and 2482 deletions

View file

@ -36,26 +36,38 @@ in
systemd.user.services.antigravity2api = {
Unit = {
Description = "Antigravity API to OpenAI Proxy";
After = [ "network.target" ];
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
WorkingDirectory = workDir;
ExecStartPre = pkgs.writeShellScript "antigravity2api-init" ''
export PATH="${pkgs.coreutils}/bin:$PATH"
export PATH="${pkgs.coreutils}/bin:${pkgs.iputils}/bin:$PATH"
mkdir -p "${workDir}"
# Ensure network exists (Not needed for host network)
# ${pkgs.podman}/bin/podman network create antigravity-net --ignore >/dev/null 2>&1 || true
# Wait for connectivity to 8.8.8.8 (Google DNS) used by the container
echo "Waiting for internet connectivity..."
until ping -c1 -W1 8.8.8.8 >/dev/null 2>&1; do
sleep 2
done
echo "Connectivity check passed."
cat > "${workDir}/.env" <<EOF
API_KEY=${cfg.credentials.apiKey}
ADMIN_USERNAME=${cfg.credentials.username}
ADMIN_PASSWORD=${cfg.credentials.password}
SYSTEM_INSTRUCTION=""
SYSTEM_INSTRUCTION=""
OFFICIAL_SYSTEM_PROMPT="You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Proactiveness**"
EOF
'';
ExecStart = ''
${pkgs.podman}/bin/podman run --replace --rm --name antigravity2api \
-p 127.0.0.1:8045:8045 \
--network=host \
-v ${workDir}/data:/app/data \
-v ${workDir}/public/images:/app/public/images \
-v ${workDir}/.env:/app/.env \

View file

@ -70,6 +70,33 @@ in
pkgs.lxqt.lxqt-policykit
pkgs.libnotify
pkgs.swww
(pkgs.writeShellScriptBin "freeze-shot" ''
# Capture the screen to a temp file
file=$(mktemp --suffix=.png)
${pkgs.grim}/bin/grim "$file"
# Open imv in fullscreen to simulate freeze
# We run it in the background
${pkgs.imv}/bin/imv -f "$file" &
pid=$!
# Give imv a moment to open
sleep 0.2
# Run slurp to select region
geometry=$(${pkgs.slurp}/bin/slurp)
# Close the "frozen" overlay
kill "$pid"
# If we got a selection, crop and copy
if [ -n "$geometry" ]; then
${pkgs.imagemagick}/bin/magick "$file" -crop "$geometry" - | ${pkgs.wl-clipboard}/bin/wl-copy
fi
# Cleanup
rm "$file"
'')
];
xdg.portal = {
@ -234,7 +261,7 @@ in
Mod+Shift+E { spawn "bemoji" "-t"; }
Print { spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy"; }
Print { spawn "freeze-shot"; }
// Browsers
Mod+W { spawn "firefox"; }

View file

@ -61,6 +61,29 @@ in
mShadow = mocha.crust;
};
plugins = {
sources = [
{
enabled = true;
name = "Official Noctalia Plugins";
url = "https://github.com/noctalia-dev/noctalia-plugins";
branch = "main"; # Explicitly set branch just in case
}
];
states = {
"assistant-panel" = {
enabled = true;
sourceUrl = "https://github.com/noctalia-dev/noctalia-plugins";
};
};
};
pluginSettings = {
"assistant-panel" = {
service = "openai";
};
};
settings = {
colorSchemes = {
darkMode = true;

View file

@ -38,7 +38,7 @@ let
glfw
];
defaultJvmArgs = "-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1 -XX:ConcGCThreads=4";
defaultJvmArgs = "-Djava.net.preferIPv4Stack=true -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1 -XX:ConcGCThreads=4";
in
{
@ -66,225 +66,230 @@ in
config = lib.mkIf cfg.enable {
home.packages = [
(bwrapperPkgs.mkBwrapper {
app = {
id = "org.prismlauncher.PrismLauncher";
package = pkgs.prismlauncher.overrideAttrs (old: {
pname = "prismlauncher";
version = old.version or "9.1"; # Fallback or keep current if valid
buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.jemalloc ];
(
let
sandboxed = bwrapperPkgs.mkBwrapper {
app = {
id = "org.prismlauncher.PrismLauncher";
package = pkgs.prismlauncher.overrideAttrs (old: {
pname = "prismlauncher";
version = old.version or "9.1"; # Fallback or keep current if valid
buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.jemalloc ];
# Keep runtimeLibs in closure without injecting them into environment
postInstall = (old.postInstall or "") + ''
mkdir -p $out/share/prismlauncher-sandboxed
echo "${lib.makeLibraryPath runtimeLibs}" > $out/share/prismlauncher-sandboxed/libs
'';
# Keep runtimeLibs in closure without injecting them into environment
postInstall = (old.postInstall or "") + ''
mkdir -p $out/share/prismlauncher-sandboxed
echo "${lib.makeLibraryPath runtimeLibs}" > $out/share/prismlauncher-sandboxed/libs
'';
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
"--set JEMALLOC_PATH ${pkgs.jemalloc}/lib/libjemalloc.so"
"--prefix LD_PRELOAD : ${pkgs.jemalloc}/lib/libjemalloc.so"
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
"--set JEMALLOC_PATH ${pkgs.jemalloc}/lib/libjemalloc.so"
"--prefix LD_PRELOAD : ${pkgs.jemalloc}/lib/libjemalloc.so"
];
});
env = {
# Propagate XDG_DATA_DIRS so themes/icons can be found
BROWSER = "firefox";
QT_QPA_PLATFORM = "xcb";
GDK_BACKEND = "x11";
NO_AT_BRIDGE = "1";
QT_QPA_PLATFORMTHEME = "";
QT_STYLE_OVERRIDE = "fusion";
# Sanitize Desktop Environment to prevent loading conflicting platform themes
XDG_CURRENT_DESKTOP = "X-Generic";
XDG_SESSION_TYPE = "x11";
GTK_USE_PORTAL = "0";
GTK_THEME = "Adwaita"; # Force a safe theme or empty?
# Unset potential conflict variables
GTK_MODULES = "";
GTK3_MODULES = "";
};
};
sockets.x11 = true;
sockets.wayland = true;
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString cfg.uid}"
# Bind ro system paths commonly needed
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--ro-bind /run/dbus /run/dbus"
];
});
env = {
# Propagate XDG_DATA_DIRS so themes/icons can be found
BROWSER = "firefox";
QT_QPA_PLATFORM = "xcb";
GDK_BACKEND = "x11";
NO_AT_BRIDGE = "1";
QT_QPA_PLATFORMTHEME = "";
QT_STYLE_OVERRIDE = "fusion";
# Sanitize Desktop Environment to prevent loading conflicting platform themes
XDG_CURRENT_DESKTOP = "X-Generic";
XDG_SESSION_TYPE = "x11";
GTK_USE_PORTAL = "0";
GTK_THEME = "Adwaita"; # Force a safe theme or empty?
# Unset potential conflict variables
GTK_MODULES = "";
GTK3_MODULES = "";
};
};
sockets.x11 = true;
sockets.wayland = true;
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString cfg.uid}"
# Bind ro system paths commonly needed
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--ro-bind /run/dbus /run/dbus"
];
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/qt6ct"
"$HOME/.config/Kvantum"
"$HOME/.config/MangoHud"
"$HOME/Downloads"
];
readWrite = [
"$HOME/.local/share/PrismLauncher"
"$HOME/.cache/PrismLauncher"
];
};
dbus.enable = false;
script.preCmds.stage2 =
let
glfwPath = "${cfg.glfwPackage}/lib/libglfw.so.3";
# We need to access the sandbox-utils.nix. Since it's in system modules,
# we can't easily import it relative to here if it's not exported.
# But the content was small, let's inline what we need or check if we can source it.
# For now, I'll assume the dbus-proxy logic is needed.
# Reimplementing mkDbusProxyScript from sandbox-utils.nix inline to avoid path dependency
mkDbusProxyScript =
{ appId, proxyArgs }:
let
proxyArgsStr = lib.escapeShellArgs proxyArgs;
appDir = "$XDG_RUNTIME_DIR/app/${appId}";
proxySocket = "${appDir}/bus";
in
''
mkdir -p "${appDir}"
# Start xdg-dbus-proxy
${pkgs.xdg-dbus-proxy}/bin/xdg-dbus-proxy \
"$DBUS_SESSION_BUS_ADDRESS" "${proxySocket}" \
${proxyArgsStr} &
DBUS_PROXY_PID=$!
# Kill proxy on exit
trap "kill $DBUS_PROXY_PID" EXIT
# Wait for socket to be created
for i in {1..50}; do
if [ -S "${proxySocket}" ]; then
break
fi
if ! kill -0 $DBUS_PROXY_PID 2>/dev/null; then
echo "xdg-dbus-proxy died unexpectedly"
exit 1
fi
sleep 0.1
done
'';
dbusScript = mkDbusProxyScript {
appId = "org.prismlauncher.PrismLauncher";
proxyArgs = [
"--filter"
"--talk=org.freedesktop.portal.*"
"--call=org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"
"--talk=org.freedesktop.Notifications"
"--own=org.prismlauncher.PrismLauncher"
"--own=org.prismlauncher.PrismLauncher.*"
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/qt6ct"
"$HOME/.config/Kvantum"
"$HOME/.config/MangoHud"
"$HOME/Downloads"
];
readWrite = [
"$HOME/.local/share/PrismLauncher"
"$HOME/.cache/PrismLauncher"
];
};
in
''
${dbusScript}
# Sanitize Environment
unset QT_QPA_PLATFORMTHEME
unset GTK_THEME
unset XDG_CURRENT_DESKTOP
export QT_QPA_PLATFORM=xcb
export GDK_BACKEND=x11
export NO_AT_BRIDGE=1
dbus.enable = false;
# Force Configs (JVM Args + GLFW)
cfg="$HOME/.local/share/PrismLauncher/prismlauncher.cfg"
if [ -f "$cfg" ]; then
# JVM Args
if ${pkgs.gnugrep}/bin/grep -q "^JvmArgs=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^JvmArgs=.*|JvmArgs=${cfg.jvmArgs}|" "$cfg"
else
if ${pkgs.gnugrep}/bin/grep -q "^\\[General\\]" "$cfg"; then
${pkgs.gnused}/bin/sed -i "/^\\[General\\]/a JvmArgs=${cfg.jvmArgs}" "$cfg"
else
echo "JvmArgs=${cfg.jvmArgs}" >> "$cfg"
fi
fi
script.preCmds.stage2 =
let
glfwPath = "${cfg.glfwPackage}/lib/libglfw.so.3";
# GLFW Settings
# 1. CustomGLFWPath
if ${pkgs.gnugrep}/bin/grep -q "^CustomGLFWPath=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^CustomGLFWPath=.*|CustomGLFWPath=${glfwPath}|" "$cfg"
else
echo "CustomGLFWPath=${glfwPath}" >> "$cfg"
fi
# We need to access the sandbox-utils.nix. Since it's in system modules,
# we can't easily import it relative to here if it's not exported.
# But the content was small, let's inline what we need or check if we can source it.
# For now, I'll assume the dbus-proxy logic is needed.
# 2. UseNativeGLFW
if ${pkgs.gnugrep}/bin/grep -q "^UseNativeGLFW=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^UseNativeGLFW=.*|UseNativeGLFW=true|" "$cfg"
else
echo "UseNativeGLFW=true" >> "$cfg"
fi
fi
'';
# Reimplementing mkDbusProxyScript from sandbox-utils.nix inline to avoid path dependency
mkDbusProxyScript =
{ appId, proxyArgs }:
let
proxyArgsStr = lib.escapeShellArgs proxyArgs;
appDir = "$XDG_RUNTIME_DIR/app/${appId}";
proxySocket = "${appDir}/bus";
in
''
mkdir -p "${appDir}"
# Start xdg-dbus-proxy
${pkgs.xdg-dbus-proxy}/bin/xdg-dbus-proxy \
"$DBUS_SESSION_BUS_ADDRESS" "${proxySocket}" \
${proxyArgsStr} &
DBUS_PROXY_PID=$!
fhsenv.bwrap.additionalArgs = [
# D-Bus proxy
''--bind "$XDG_RUNTIME_DIR/bus" "$XDG_RUNTIME_DIR/bus"''
# Note: The original code bound a specific path TO ./bus.
# "''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''"
# But mkDbusProxyScript (if standard) creates a socket.
# The logic in prismlauncher-sandboxed.nix imported sandbox-utils.nix.
# I'll try to match the original bind logic if possible.
# Kill proxy on exit
trap "kill $DBUS_PROXY_PID" EXIT
# The original code had:
# ''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
# But my inline mkDbusProxyScript sets up "$XDG_RUNTIME_DIR/bus" as the listen socket *inside* the script execution?
# Wait, xdg-dbus-proxy runs inside the outer unshared namespace or outside?
# In mkBwrapper, preCmds run *inside* the bwrap?
# No, typically preCmds run before the final exec?
# Actually, looking at nix-bwrapper, `preCmds.stage2` runs *inside* the sandbox?
# Wait for socket to be created
for i in {1..50}; do
if [ -S "${proxySocket}" ]; then
break
fi
if ! kill -0 $DBUS_PROXY_PID 2>/dev/null; then
echo "xdg-dbus-proxy died unexpectedly"
exit 1
fi
sleep 0.1
done
'';
# Let's start with the binds exactly as they were, assuming `sandbox-utils` logic.
# If I can't import sandbox-utils, I have to rely on what I can see.
# The original `sandbox-utils.nix` likely set up the proxy.
# I will copy the binds from the original file.
dbusScript = mkDbusProxyScript {
appId = "org.prismlauncher.PrismLauncher";
proxyArgs = [
"--filter"
"--talk=org.freedesktop.portal.*"
"--call=org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"
"--talk=org.freedesktop.Notifications"
"--own=org.prismlauncher.PrismLauncher"
"--own=org.prismlauncher.PrismLauncher.*"
];
};
in
''
${dbusScript}
''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
# Sanitize Environment
unset QT_QPA_PLATFORMTHEME
unset GTK_THEME
unset XDG_CURRENT_DESKTOP
export QT_QPA_PLATFORM=xcb
export GDK_BACKEND=x11
export NO_AT_BRIDGE=1
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# Force Configs (JVM Args + GLFW)
cfg="$HOME/.local/share/PrismLauncher/prismlauncher.cfg"
if [ -f "$cfg" ]; then
# JVM Args
if ${pkgs.gnugrep}/bin/grep -q "^JvmArgs=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^JvmArgs=.*|JvmArgs=${cfg.jvmArgs}|" "$cfg"
else
if ${pkgs.gnugrep}/bin/grep -q "^\\[General\\]" "$cfg"; then
${pkgs.gnused}/bin/sed -i "/^\\[General\\]/a JvmArgs=${cfg.jvmArgs}" "$cfg"
else
echo "JvmArgs=${cfg.jvmArgs}" >> "$cfg"
fi
fi
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
})
# GLFW Settings
# 1. CustomGLFWPath
if ${pkgs.gnugrep}/bin/grep -q "^CustomGLFWPath=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^CustomGLFWPath=.*|CustomGLFWPath=${glfwPath}|" "$cfg"
else
echo "CustomGLFWPath=${glfwPath}" >> "$cfg"
fi
# 2. UseNativeGLFW
if ${pkgs.gnugrep}/bin/grep -q "^UseNativeGLFW=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^UseNativeGLFW=.*|UseNativeGLFW=true|" "$cfg"
else
echo "UseNativeGLFW=true" >> "$cfg"
fi
fi
'';
fhsenv.bwrap.additionalArgs = [
# D-Bus proxy
''--bind "$XDG_RUNTIME_DIR/bus" "$XDG_RUNTIME_DIR/bus"''
# Note: The original code bound a specific path TO ./bus.
# "''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''"
# But mkDbusProxyScript (if standard) creates a socket.
# The logic in prismlauncher-sandboxed.nix imported sandbox-utils.nix.
# I'll try to match the original bind logic if possible.
# The original code had:
# ''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
# But my inline mkDbusProxyScript sets up "$XDG_RUNTIME_DIR/bus" as the listen socket *inside* the script execution?
# Wait, xdg-dbus-proxy runs inside the outer unshared namespace or outside?
# In mkBwrapper, preCmds run *inside* the bwrap?
# No, typically preCmds run before the final exec?
# Actually, looking at nix-bwrapper, `preCmds.stage2` runs *inside* the sandbox?
# Let's start with the binds exactly as they were, assuming `sandbox-utils` logic.
# If I can't import sandbox-utils, I have to rely on what I can see.
# The original `sandbox-utils.nix` likely set up the proxy.
# I will copy the binds from the original file.
''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
};
in
sandboxed
)
];
};
}

View file

@ -0,0 +1,81 @@
# Automatic Updates Module
# Provides:
# 1. Weekly Nix flake updates for the system configuration
# 2. Daily NixOS system upgrades via system.autoUpgrade
# 3. Daily Podman container updates for services with 'io.containers.autoupdate' label
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.autoUpdate;
repoPath = "/home/ashie/nixos";
in
{
options.myModules.autoUpdate = {
enable = lib.mkEnableOption "system-wide automatic updates";
};
config = lib.mkIf cfg.enable {
# 1. NixOS System Upgrades
system.autoUpgrade = {
enable = true;
dates = "04:30";
flake = "${repoPath}#nixos";
allowReboot = false;
flags = [
"--refresh"
];
};
# 2. Flake Update Service (Runs before autoUpgrade)
# This ensures the local flake.lock is updated so autoUpgrade has new versions to pull.
systemd.services.nix-flake-update = {
description = "Update Nix Flake Lockfile";
serviceConfig = {
Type = "oneshot";
User = "root";
};
path = [ pkgs.nix pkgs.git pkgs.openssh ];
script = ''
cd ${repoPath}
# Only update if it's a git repo and we have permissions
if [ -d .git ]; then
nix flake update --commit-lock-file
else
nix flake update
fi
'';
startAt = "04:00"; # Run 30 mins before autoUpgrade
};
# 3. Podman Container Auto-Updates
# Runs 'podman auto-update' to refresh containers with the 'io.containers.autoupdate' label.
systemd.services.podman-auto-update = {
description = "Podman Container Auto-Update";
after = [ "network-online.target" "nixos-upgrade.service" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.podman}/bin/podman auto-update";
ExecStartPost = "${pkgs.podman}/bin/podman image prune -f";
};
};
systemd.timers.podman-auto-update = {
description = "Podman Container Auto-Update Timer";
timerConfig = {
OnCalendar = "05:00";
Persistent = true;
};
wantedBy = [ "timers.target" ];
};
# Ensure the auto-upgrade service waits for the flake update
systemd.services.nixos-upgrade.after = [ "nix-flake-update.service" ];
};
}

View file

@ -1,13 +1,14 @@
{
config,
{ config,
lib,
pkgs,
inputs,
...
...
}:
let
let
cfg = config.myModules.azaharSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
pname = "azahar";
version = "2123.4";
@ -35,58 +36,68 @@ let
};
in
{
nixpkgs.overlays = [
(final: prev: {
azahar-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = azahar;
id = "org.azahar_emu.Azahar";
env = {
QT_QPA_PLATFORM = "wayland;xcb";
XDG_CURRENT_DESKTOP = "KDE";
options.myModules.azaharSandboxed = {
enable = lib.mkEnableOption "sandboxed Azahar with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
azahar-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = azahar;
id = "org.azahar_emu.Azahar";
env = {
QT_QPA_PLATFORM = "wayland;xcb";
XDG_CURRENT_DESKTOP = "KDE";
};
};
flatpak.enable = false;
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { });
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/org.azahar_emu.Azahar/bus" "$XDG_RUNTIME_DIR/bus"''
];
mounts = {
read = sandboxUtils.mkGuiMounts.read;
readWrite = [
"$HOME/Games/3DS"
"$HOME/.config/azahar"
"$HOME/.local/share/azahar"
] ++ cfg.extraBindMounts;
};
dbus.enable = false;
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "org.azahar_emu.Azahar";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.Flatpak"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.secrets"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
];
};
};
})
];
flatpak.enable = false;
fhsenv.bwrap.additionalArgs = [
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
''--bind "$XDG_RUNTIME_DIR/app/org.azahar_emu.Azahar/bus" "$XDG_RUNTIME_DIR/bus"''
];
mounts = {
read = [
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
"$HOME/.config/fontconfig"
"$HOME/.icons"
"$HOME/.config/MangoHud"
];
readWrite = [
"$HOME/Games/3DS"
"$HOME/.config/azahar"
"$HOME/.local/share/azahar"
];
};
dbus.enable = false;
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "org.azahar_emu.Azahar";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.Flatpak"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.secrets"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
];
};
};
})
];
environment.systemPackages = [ pkgs.azahar-sandboxed ];
};
}

View file

@ -7,7 +7,9 @@
}:
let
cfg = config.myModules.braveSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
# create a custom settings.ini to force dark mode
darkSettingsIni = pkgs.writeText "settings.ini" ''
@ -31,138 +33,150 @@ let
);
in
{
nixpkgs.overlays = [
(final: prev: {
brave-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = pkgs.symlinkJoin {
name = "brave-single-desktop";
paths = [ prev.brave ];
inherit (prev.brave) pname version meta;
postBuild = ''
rm $out/share/applications/com.brave.Browser.desktop
'';
options.myModules.braveSandboxed = {
enable = lib.mkEnableOption "sandboxed Brave with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
brave-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = pkgs.symlinkJoin {
name = "brave-single-desktop";
paths = [ prev.brave ];
inherit (prev.brave) pname version meta;
postBuild = ''
rm $out/share/applications/com.brave.Browser.desktop
'';
};
# id = "brave-browser"; # Omit app.id to avoid potential bind errors (like Firefox)
env = {
# Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
GTK_THEME = "catppuccin-mocha-mauve-standard";
HYPRCURSOR_THEME = "Future-Cyan-Hyprcursor_Theme";
HYPRCURSOR_SIZE = "32";
# Force ozone/wayland usage for Brave/Chromium
NIXOS_OZONE_WL = "1";
};
};
# id = "brave-browser"; # Omit app.id to avoid potential bind errors (like Firefox)
env = {
# Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
GTK_THEME = "catppuccin-mocha-mauve-standard";
HYPRCURSOR_THEME = "Future-Cyan-Hyprcursor_Theme";
HYPRCURSOR_SIZE = "32";
# Force ozone/wayland usage for Brave/Chromium
NIXOS_OZONE_WL = "1";
flatpak.enable = false;
sockets.x11 = false;
sockets.wayland = true;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
};
flatpak.enable = false;
sockets.x11 = false;
sockets.wayland = true;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri"
"--tmpfs /home"
"--tmpfs /mnt"
"--tmpfs /run"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/booted-system /run/booted-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
# Brave flags
"--setenv NIXOS_OZONE_WL \"1\""
"--setenv NOTIFY_IGNORE_PORTAL 1"
# Bind policies for Theme
"--dir /etc/brave/policies/managed"
"--ro-bind ${bravePolicies} /etc/brave/policies/managed/policies.json"
# Fallback paths for Chromium/Chrome base
"--dir /etc/chromium/policies/managed"
"--ro-bind ${bravePolicies} /etc/chromium/policies/managed/policies.json"
"--dir /etc/opt/chrome/policies/managed"
"--ro-bind ${bravePolicies} /etc/opt/chrome/policies/managed/policies.json"
];
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri"
"--tmpfs /home"
"--tmpfs /mnt"
"--tmpfs /run"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/booted-system /run/booted-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
# Brave flags
"--setenv NIXOS_OZONE_WL \"1\""
"--setenv NOTIFY_IGNORE_PORTAL 1"
# Bind policies for Theme
"--dir /etc/brave/policies/managed"
"--ro-bind ${bravePolicies} /etc/brave/policies/managed/policies.json"
# Fallback paths for Chromium/Chrome base
"--dir /etc/chromium/policies/managed"
"--ro-bind ${bravePolicies} /etc/chromium/policies/managed/policies.json"
"--dir /etc/opt/chrome/policies/managed"
"--ro-bind ${bravePolicies} /etc/opt/chrome/policies/managed/policies.json"
# Filesystem: Limited to Brave directories and Downloads
mounts = {
read = [
"$HOME/.config/kdedefaults"
"$HOME/.config/fontconfig"
"$HOME/.config/user-dirs.dirs"
"$HOME/.config/mimeapps.list"
"$HOME/.local/share/color-schemes"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/gtk-3.0"
];
readWrite = [
"$HOME/.config/BraveSoftware"
"$HOME/.cache/BraveSoftware"
"$HOME/Downloads"
] ++ cfg.extraBindMounts;
};
# Bind mount systemd-resolved socket for DNS and required system files
# Disable built-in DBus module because it invokes bwrap without --unshare-user
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "nix.bwrapper.brave";
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.portal.FileChooser"''
''--talk="org.freedesktop.secrets"''
''--talk="org.kde.StatusNotifierWatcher"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="org.chromium.LibCrosService"'' # Chromium/Brave specific
''--own="org.mpris.MediaPlayer2.chromium.*"''
''--own="org.mpris.MediaPlayer2.brave.*"''
];
enableSystemBus = true;
systemProxyArgs = [
"--filter"
''--talk="org.freedesktop.NetworkManager"''
];
};
fhsenv.bwrap.additionalArgs = [
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus" "$XDG_RUNTIME_DIR/bus"''
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus_system" /run/dbus/system_bus_socket''
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--bind-try /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf"
];
};
})
];
environment.systemPackages = [
(pkgs.writeShellScriptBin "brave" ''
exec ${config.myModules.system.repoPath}/scripts/launch-vpn-app.sh ${pkgs.brave-sandboxed}/bin/brave "$@"
'')
(pkgs.makeDesktopItem {
name = "brave-vpn";
desktopName = "Brave Web Browser";
exec = "brave %U";
icon = "brave-browser";
categories = [
"Network"
"WebBrowser"
];
# Filesystem: Limited to Brave directories and Downloads
mounts = {
read = [
"$HOME/.config/kdedefaults"
"$HOME/.config/fontconfig"
"$HOME/.config/user-dirs.dirs"
"$HOME/.config/mimeapps.list"
"$HOME/.local/share/color-schemes"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/gtk-3.0"
];
readWrite = [
"$HOME/.config/BraveSoftware"
"$HOME/.cache/BraveSoftware"
"$HOME/Downloads"
];
};
# Bind mount systemd-resolved socket for DNS and required system files
# Disable built-in DBus module because it invokes bwrap without --unshare-user
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "nix.bwrapper.brave";
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.portal.FileChooser"''
''--talk="org.freedesktop.secrets"''
''--talk="org.kde.StatusNotifierWatcher"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="org.chromium.LibCrosService"'' # Chromium/Brave specific
''--own="org.mpris.MediaPlayer2.chromium.*"''
''--own="org.mpris.MediaPlayer2.brave.*"''
];
enableSystemBus = true;
systemProxyArgs = [
"--filter"
''--talk="org.freedesktop.NetworkManager"''
];
};
fhsenv.bwrap.additionalArgs = [
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus" "$XDG_RUNTIME_DIR/bus"''
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus_system" /run/dbus/system_bus_socket''
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
];
};
})
];
environment.systemPackages = [
(pkgs.writeShellScriptBin "brave" ''
exec /home/ashie/nixos/scripts/launch-vpn-app.sh ${pkgs.brave-sandboxed}/bin/brave "$@"
'')
(pkgs.makeDesktopItem {
name = "brave-vpn";
desktopName = "Brave Web Browser";
exec = "brave %U";
icon = "brave-browser";
categories = [
"Network"
"WebBrowser"
];
})
];
}
})
];
};
}

64
modules/nixos/braveapi.py Normal file
View file

@ -0,0 +1,64 @@
"""
Search Engine: Brave Search API
"""
import json
import os
from urllib.parse import quote
# About the engine
about = {
"website": "https://brave.com/search/api/",
"use_official_api": True,
"require_api_key": True,
"results": "JSON",
}
categories = ['general', 'web']
paging = True
max_page = 10
page_size = 20
# API Endpoint
base_url = "https://api.search.brave.com/res/v1/web/search"
def request(query, params):
# Get key from environment
api_key = os.getenv('BRAVE_API_KEY')
# Brave expects offset 0-9 for pages
pageno = min(params.get('pageno', 1), 10)
offset = pageno - 1
# Simple query string construction with proper quoting
# Using 'x-subscription-token' (lowercase) which was verified to work
params['url'] = f"{base_url}?q={quote(query)}&count=20&offset={offset}"
params['headers'] = {
'x-subscription-token': api_key,
'Accept': 'application/json'
}
params['method'] = 'GET'
# Clean up SearXNG defaults to prevent interference
if 'data' in params: del params['data']
if 'params' in params: del params['params']
return params
def response(resp):
results = []
data = json.loads(resp.text)
# The Brave API returns 'web' results
web_results = data.get('web', {}).get('results', [])
for item in web_results:
results.append({
'url': item.get('url'),
'title': item.get('title'),
'content': item.get('description'),
})
return results

View file

@ -18,6 +18,7 @@
let
cfg = config.myModules.browserVpn;
mainUser = config.myModules.system.mainUser;
# Helper function for auto-recovery from podman namespace corruption
# Detects "cannot re-exec process" errors and runs migrate to fix
@ -78,7 +79,8 @@ let
echo "Build complete."
;;
run)
if ! podman_with_recovery image exists ${imageName}:latest 2>/dev/null; then
if ! podman_with_recovery image exists ${imageName}:latest 2>/dev/null;
then
echo "Building ${name} container image..."
podman_with_recovery build -t ${imageName}:latest "$REPO_DIR/containers/${name}-wayland/"
fi
@ -168,8 +170,8 @@ let
Fingerprinting = true;
};
Preferences = {
"network.dns.disableIPv6" = true;
"network.ipv6" = false;
"network.dns.disableIPv6" = false;
"network.ipv6" = true;
"network.http.fast-fallback-to-IPv4" = true;
"network.trr.mode" = 5; # Disable DNS over HTTPS (use system/VPN DNS)
"ui.systemUsesDarkTheme" = 1;
@ -231,7 +233,8 @@ let
echo "Build complete."
;;
run)
if ! podman_with_recovery image exists localhost/thorium-wayland:latest 2>/dev/null; then
if ! podman_with_recovery image exists localhost/thorium-wayland:latest 2>/dev/null;
then
echo "Building thorium-dev container image..."
podman_with_recovery build -t localhost/thorium-wayland:latest "$REPO_DIR/containers/thorium-wayland/"
fi
@ -315,7 +318,8 @@ let
echo "Build complete."
;;
run)
if ! podman_with_recovery image exists localhost/arch-kitty:latest 2>/dev/null; then
if ! podman_with_recovery image exists localhost/arch-kitty:latest 2>/dev/null;
then
echo "Building Arch Kitty container image..."
podman_with_recovery build -t localhost/arch-kitty:latest "$REPO_DIR/containers/arch-kitty/"
fi
@ -450,13 +454,13 @@ in
kittyConfigDir = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/.config/kitty";
default = "/home/${mainUser}/.config/kitty";
description = "Path to kitty configuration directory";
};
bashrcPath = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/.bashrc";
default = "/home/${mainUser}/.bashrc";
description = "Path to bashrc file for Kitty container";
};
};
@ -464,4 +468,4 @@ in
config = lib.mkIf cfg.enable {
environment.systemPackages = enabledPackages ++ [ desktopEntriesPackage ];
};
}
}

View file

@ -10,7 +10,9 @@
}:
let
cfg = config.myModules.citronSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
pname = "citron";
version = "0.12.25";
@ -74,123 +76,110 @@ let
};
in
{
nixpkgs.overlays = [
(final: prev: {
citron-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = citron;
id = appId;
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
QT_QPA_PLATFORM = "wayland;xcb";
XDG_CURRENT_DESKTOP = "KDE";
# Allow AppImage to extract and run
APPIMAGE_EXTRACT_AND_RUN = "1";
options.myModules.citronSandboxed = {
enable = lib.mkEnableOption "sandboxed Citron with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
citron-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = citron;
id = appId;
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
QT_QPA_PLATFORM = "wayland;xcb";
XDG_CURRENT_DESKTOP = "KDE";
# Allow AppImage to extract and run
APPIMAGE_EXTRACT_AND_RUN = "1";
};
};
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Disable Flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for online features
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { } ++ [
# Fix for amdgpu.ids missing - use tmpfs so mkdir can succeed
"--tmpfs /usr/share"
"--ro-bind ${pkgs.libdrm}/share/libdrm /usr/share/libdrm"
"--ro-bind-try /nix/store /nix/store"
]);
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
# Also create required directories before bwrap runs
script.preCmds.stage2 = ''
# Create directories that bwrap will bind
mkdir -p "$HOME/.cache/citron-tmp"
mkdir -p "$HOME/.config/citron"
mkdir -p "$HOME/.config/Citron"
mkdir -p "$HOME/.local/share/citron"
mkdir -p "$HOME/.local/share/Citron"
mkdir -p "$HOME/Games/Switch"
''
+ sandboxUtils.mkDbusProxyScript {
inherit appId;
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--own="${appId}"''
''--own="${appId}.*"''
];
};
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
# Read-write mounts
"--bind $HOME/Games/Switch $HOME/Games/Switch"
"--bind $HOME/.config/citron $HOME/.config/citron"
"--bind $HOME/.config/Citron $HOME/.config/Citron"
"--bind $HOME/.local/share/citron $HOME/.local/share/citron"
"--bind $HOME/.local/share/Citron $HOME/.local/share/Citron"
"--bind $HOME/.cache/citron-tmp $HOME/.cache/citron-tmp"
];
mounts = {
read = sandboxUtils.mkGuiMounts.read;
readWrite = cfg.extraBindMounts;
};
};
})
];
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Disable Flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for online features
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--dev-bind /dev/shm /dev/shm" # Shared memory
"--dev-bind-try /dev/uinput /dev/uinput" # Controller support
"--dev-bind-try /dev/input /dev/input"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# Fix for amdgpu.ids missing - use tmpfs so mkdir can succeed
"--tmpfs /usr/share"
"--ro-bind ${pkgs.libdrm}/share/libdrm /usr/share/libdrm"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--ro-bind-try /nix/store /nix/store"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
# udev for controller hotplug
"--ro-bind-try /run/udev /run/udev"
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
# Also create required directories before bwrap runs
script.preCmds.stage2 = ''
# Create directories that bwrap will bind
mkdir -p "$HOME/.cache/citron-tmp"
mkdir -p "$HOME/.config/citron"
mkdir -p "$HOME/.config/Citron"
mkdir -p "$HOME/.local/share/citron"
mkdir -p "$HOME/.local/share/Citron"
mkdir -p "$HOME/Games/Switch"
''
+ (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
inherit appId;
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--own="${appId}"''
''--own="${appId}.*"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
# Manual mounts for data persistence
"--ro-bind-try $HOME/.config/kdedefaults $HOME/.config/kdedefaults"
"--ro-bind-try $HOME/.local/share/color-schemes $HOME/.local/share/color-schemes"
"--ro-bind-try $HOME/.config/fontconfig $HOME/.config/fontconfig"
"--ro-bind-try $HOME/.local/share/fonts $HOME/.local/share/fonts"
"--ro-bind-try $HOME/.icons $HOME/.icons"
"--ro-bind-try $HOME/.themes $HOME/.themes"
"--ro-bind-try $HOME/.config/qt6ct $HOME/.config/qt6ct"
"--ro-bind-try $HOME/.config/Kvantum $HOME/.config/Kvantum"
"--ro-bind-try $HOME/.config/MangoHud $HOME/.config/MangoHud"
# Read-write mounts
"--bind $HOME/Games/Switch $HOME/Games/Switch"
"--bind $HOME/.config/citron $HOME/.config/citron"
"--bind $HOME/.config/Citron $HOME/.config/Citron"
"--bind $HOME/.local/share/citron $HOME/.local/share/citron"
"--bind $HOME/.local/share/Citron $HOME/.local/share/Citron"
"--bind $HOME/.cache/citron-tmp $HOME/.cache/citron-tmp"
];
};
})
];
environment.systemPackages = [ pkgs.citron-sandboxed ];
};
}

View file

@ -118,13 +118,16 @@ in
# Allow all traffic from internal container interfaces (Podman/CNI)
# This allows containers to reach the host (DNS, Gateway)
iifname "podman*" accept
iifname "cni*" accept
# Allow container subnet (fixes issues with non-podman* interface names)
ip saddr 10.89.0.0/16 accept
# Allow RFC1918 Private Networks (LAN, Containers, Link-Local)
${lib.optionalString cfg.allowLocalTraffic ''
ip saddr 10.0.0.0/8 accept
ip saddr 172.16.0.0/12 accept
ip saddr 192.168.0.0/16 accept
ip saddr 10.0.0.0/8 accept
ip saddr 172.16.0.0/12 accept
ip saddr 192.168.0.0/16 accept
''}
ip saddr 169.254.0.0/16 accept
@ -137,7 +140,7 @@ in
${lib.optionalString (cfg.restrictedPorts != [ ]) ''
ip saddr @cloudflare_ipv4 tcp dport { ${lib.concatStringsSep ", " (map toString cfg.restrictedPorts)} } accept
ip6 saddr @cloudflare_ipv6 tcp dport { ${lib.concatStringsSep ", " (map toString cfg.restrictedPorts)} } accept
# Drop all other traffic to restricted ports
tcp dport { ${lib.concatStringsSep ", " (map toString cfg.restrictedPorts)} } drop
''}
@ -151,6 +154,9 @@ in
oifname "podman*" accept
iifname "cni*" accept
oifname "cni*" accept
# Allow container subnet forwarding
ip saddr 10.89.0.0/16 accept
ip daddr 10.89.0.0/16 accept
# Allow established/related forwarding
ct state established,related accept

View file

@ -10,5 +10,11 @@
default = "/home/ashie/nixos";
description = "Path to the main NixOS configuration repository";
};
mainUser = lib.mkOption {
type = lib.types.str;
default = "ashie";
description = "Username of the main user";
};
};
}

View file

@ -1,14 +1,25 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.myModules.desktop.cosmic;
in
{
services.desktopManager.cosmic.enable = true;
services.displayManager.cosmic-greeter.enable = false;
options.myModules.desktop.cosmic = {
enable = lib.mkEnableOption "Cosmic Desktop Environment";
};
# Optimization
services.system76-scheduler.enable = true;
config = lib.mkIf cfg.enable {
services.desktopManager.cosmic.enable = true;
services.displayManager.cosmic-greeter.enable = false;
# Clipboard support (unstable protocol)
environment.sessionVariables.COSMIC_DATA_CONTROL_ENABLED = "1";
}
# Optimization
services.system76-scheduler.enable = true;
# Clipboard support (unstable protocol)
environment.sessionVariables.COSMIC_DATA_CONTROL_ENABLED = "1";
};
}

View file

@ -36,5 +36,10 @@
./tutanota-sandboxed.nix
./hardened-malloc.nix
./searxng.nix
./media.nix
./cosmic.nix
./steam-gamemode.nix
./redlib.nix
./auto-update.nix
];
}
}

View file

@ -10,138 +10,125 @@
}:
let
cfg = config.myModules.faugusSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
in
{
nixpkgs.overlays = [
(final: prev: {
faugus-sandboxed =
let
singleDesktopPkg =
pkgs.symlinkJoin {
name = "faugus-launcher-single";
paths = [ prev.faugus-launcher ];
postBuild = ''
rm -rf $out/share/applications
mkdir -p $out/share/applications
ln -s ${prev.faugus-launcher}/share/applications/faugus-launcher.desktop $out/share/applications/io.github.faugus.Launcher.desktop
'';
}
// {
inherit (prev.faugus-launcher) pname version meta;
options.myModules.faugusSandboxed = {
enable = lib.mkEnableOption "sandboxed Faugus Launcher with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
faugus-sandboxed =
let
singleDesktopPkg =
pkgs.symlinkJoin {
name = "faugus-launcher-single";
paths = [ prev.faugus-launcher ];
postBuild = ''
rm -rf $out/share/applications
mkdir -p $out/share/applications
ln -s ${prev.faugus-launcher}/share/applications/faugus-launcher.desktop $out/share/applications/io.github.faugus.Launcher.desktop
'';
}
// {
inherit (prev.faugus-launcher) pname version meta;
};
in
bwrapperPkgs.mkBwrapper {
app = {
package = singleDesktopPkg;
id = "io.github.faugus.Launcher";
env = {
# Propagate XDG_DATA_DIRS so themes/icons can be found
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Fix for file dialogs/theming
XDG_CURRENT_DESKTOP = "KDE";
# GTK theming
GTK_THEME = "catppuccin-frappe-blue-standard";
# Force GTK to use the portal for file dialogs
GTK_USE_PORTAL = "1";
# Force Wayland backend to ensure xdg-foreign protocol works
GDK_BACKEND = "wayland";
};
};
in
bwrapperPkgs.mkBwrapper {
app = {
package = singleDesktopPkg;
id = "io.github.faugus.Launcher";
env = {
# Propagate XDG_DATA_DIRS so themes/icons can be found
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Fix for file dialogs/theming
XDG_CURRENT_DESKTOP = "KDE";
# GTK theming
GTK_THEME = "catppuccin-frappe-blue-standard";
# Force GTK to use the portal for file dialogs
GTK_USE_PORTAL = "1";
# Force Wayland backend to ensure xdg-foreign protocol works
GDK_BACKEND = "wayland";
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Disable Flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network
unshareIpc = false;
};
};
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { });
# Disable Flatpak emulation
flatpak.enable = false;
mounts = {
read = sandboxUtils.mkGuiMounts.read ++ [
"$HOME/.gtkrc-2.0"
];
readWrite = [
"$HOME/Games"
"$HOME/.config/faugus-launcher"
"$HOME/.local/share/faugus-launcher"
"$HOME/.cache/faugus-launcher"
"$HOME/.config/qt6ct" # Allow theming
] ++ cfg.extraBindMounts;
};
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network
unshareIpc = false;
};
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--dev-bind /dev/shm /dev/shm" # Shared memory
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
];
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "io.github.faugus.Launcher";
enableSystemBus = false; # No system bus access
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--talk="org.freedesktop.portal.FileChooser"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="org.freedesktop.portal.Settings"''
''--talk="com.feralinteractive.GameMode"''
''--own="io.github.faugus.Launcher"''
''--own="io.github.faugus.Launcher.*"''
];
};
mounts = {
read = [
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
"$HOME/.config/fontconfig"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.local/share/fonts"
"$HOME/.config/Kvantum"
"$HOME/.config/gtk-3.0"
"$HOME/.config/gtk-4.0"
"$HOME/.gtkrc-2.0"
"$HOME/.config/MangoHud"
];
readWrite = [
"$HOME/Games"
"$HOME/.config/faugus-launcher"
"$HOME/.local/share/faugus-launcher"
"$HOME/.cache/faugus-launcher"
"$HOME/.config/qt6ct" # Allow theming
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/io.github.faugus.Launcher/bus" "$XDG_RUNTIME_DIR/bus"''
# dconf for GTK settings
"--bind-try /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf"
];
};
})
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "io.github.faugus.Launcher";
enableSystemBus = false; # No system bus access
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--talk="org.freedesktop.portal.FileChooser"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="org.freedesktop.portal.Settings"''
''--talk="com.feralinteractive.GameMode"''
''--own="io.github.faugus.Launcher"''
''--own="io.github.faugus.Launcher.*"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/io.github.faugus.Launcher/bus" "$XDG_RUNTIME_DIR/bus"''
# dconf for GTK settings
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
];
};
})
];
environment.systemPackages = [ pkgs.faugus-sandboxed ];
};
}

View file

@ -7,38 +7,14 @@
}:
let
cfg = config.myModules.firefoxSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
# create a custom settings.ini to force dark mode
darkSettingsIni = pkgs.writeText "settings.ini" ''
[Settings]
gtk-theme-name=catppuccin-mocha-mauve-standard
gtk-application-prefer-dark-theme=1
gtk-cursor-theme-name=Future-Cyan-Hyprcursor_Theme
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintslight
gtk-xft-rgba=rgb
'';
# Define policies.json with Catppuccin Theme and P-Stream extension
firefoxPolicies = pkgs.writeText "policies.json" (
builtins.toJSON {
policies = {
ExtensionSettings = {
# Catppuccin Mocha Mauve (Official)
"catppuccin-mocha-mauve-official@catppuccin.com" = {
install_url = "https://addons.mozilla.org/firefox/downloads/latest/catppuccin-mocha-mauve-official/latest.xpi";
installation_mode = "force_installed";
};
# P-Stream extension
"{de055456-589b-45fe-8342-c685a7ffb424}" = {
install_url = "https://github.com/p-stream/extension/releases/download/1.3.5/firefox-mv3-prod.xpi";
installation_mode = "force_installed";
};
};
Preferences = {
"extensions.activeThemeID" = "catppuccin-mocha-mauve-official@catppuccin.com";
"xpinstall.signatures.required" = false;
};
};
@ -46,112 +22,112 @@ let
);
in
{
nixpkgs.overlays = [
(final: prev: {
firefox-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.firefox;
# Omit app.id to avoid document portal bind that fails on FUSE
env = {
MOZ_ENABLE_WAYLAND = "1";
LD_PRELOAD = "";
# Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
GTK_THEME = "catppuccin-mocha-mauve-standard";
HYPRCURSOR_THEME = "Future-Cyan-Hyprcursor_Theme";
HYPRCURSOR_SIZE = "32";
options.myModules.firefoxSandboxed = {
enable = lib.mkEnableOption "sandboxed Firefox with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
firefox-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.firefox-esr;
# Omit app.id to avoid document portal bind that fails on FUSE
env = {
MOZ_ENABLE_WAYLAND = "1";
LD_PRELOAD = "";
# Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
GTK_THEME = "catppuccin-mocha-mauve-standard";
HYPRCURSOR_THEME = "Future-Cyan-Hyprcursor_Theme";
HYPRCURSOR_SIZE = "32";
};
};
};
flatpak.enable = false;
sockets.x11 = false;
sockets.wayland = true;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
flatpak.enable = false;
sockets.x11 = false;
sockets.wayland = true;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri"
"--tmpfs /home"
"--tmpfs /mnt"
"--tmpfs /run"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/booted-system /run/booted-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
# Removed: --bind "$XDG_RUNTIME_DIR/doc/by-app/..." which causes FUSE errors
"--unsetenv LD_PRELOAD"
"--setenv MOZ_ENABLE_WAYLAND \"1\""
"--setenv NOTIFY_IGNORE_PORTAL 1"
"--dir /etc"
"--dir /etc/firefox"
"--dir /etc/firefox/policies"
"--ro-bind ${firefoxPolicies} /etc/firefox/policies/policies.json"
];
fhsenv.bwrap.baseArgs = lib.mkForce (
sandboxUtils.mkCommonBindArgs { inherit config lib; }
++ sandboxUtils.mkGamingBindArgs { }
++ [
"--tmpfs /mnt"
"--ro-bind-try /run/booted-system /run/booted-system"
"--setenv MOZ_ENABLE_WAYLAND \"1\""
"--setenv NOTIFY_IGNORE_PORTAL 1"
"--dir /etc"
"--dir /etc/firefox"
"--dir /etc/firefox/policies"
"--ro-bind ${firefoxPolicies} /etc/firefox/policies/policies.json"
]
);
# Filesystem: Limited to Mozilla directories and Downloads
mounts = {
read = [
"$HOME/.config/kdedefaults"
"$HOME/.config/fontconfig"
"$HOME/.config/user-dirs.dirs"
"$HOME/.config/mimeapps.list"
"$HOME/.local/share/color-schemes"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/gtk-3.0"
];
readWrite = [
"$HOME/.mozilla"
"$HOME/.cache/mozilla"
"$HOME/Downloads"
# Filesystem: Limited to Mozilla directories and Downloads
mounts = {
read = sandboxUtils.mkGuiMounts.read ++ [
"$HOME/.config/user-dirs.dirs"
"$HOME/.config/mimeapps.list"
];
readWrite = [
"$HOME/.mozilla"
"$HOME/.cache/mozilla"
"$HOME/Downloads"
]
++ cfg.extraBindMounts;
};
# Bind mount systemd-resolved socket for DNS and required system files
# Disable built-in DBus module because it invokes bwrap without --unshare-user
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "nix.bwrapper.firefox";
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.portal.FileChooser"''
''--talk="org.freedesktop.secrets"''
''--talk="org.kde.StatusNotifierWatcher"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="org.mozilla.firefox"''
''--own="org.mozilla.firefox.*"''
''--own="org.mpris.MediaPlayer2.firefox.*"''
];
enableSystemBus = true;
systemProxyArgs = [
"--filter"
''--talk="org.freedesktop.NetworkManager"''
];
};
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus" "$XDG_RUNTIME_DIR/bus"''
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus_system" /run/dbus/system_bus_socket''
"--bind-try /run/user/${
toString config.users.users.${config.myModules.system.mainUser}.uid
}/dconf /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}/dconf"
];
};
})
];
# Bind mount systemd-resolved socket for DNS and required system files
# Disable built-in DBus module because it invokes bwrap without --unshare-user
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "nix.bwrapper.firefox";
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.portal.FileChooser"''
''--talk="org.freedesktop.secrets"''
''--talk="org.kde.StatusNotifierWatcher"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="org.mozilla.firefox"''
''--own="org.mozilla.firefox.*"''
''--own="org.mpris.MediaPlayer2.firefox.*"''
];
enableSystemBus = true;
systemProxyArgs = [
"--filter"
''--talk="org.freedesktop.NetworkManager"''
];
};
fhsenv.bwrap.additionalArgs = [
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus" "$XDG_RUNTIME_DIR/bus"''
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus_system" /run/dbus/system_bus_socket''
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
];
};
})
];
environment.systemPackages = [ pkgs.firefox-sandboxed ];
};
}

View file

@ -57,6 +57,7 @@
"Music"
"Pictures"
"Videos"
"src/aadniz-searxng"
"Torrents"
"nixos" # Config repo
".local/share/PrismLauncher" # Minecraft
@ -71,7 +72,8 @@
".config/Antigravity" # Antigravity Config
".config/modprobed-db" # Local modconfig database
".config/VSCodium" # Codium Config
".config/sops" # Sops Keys
".config/sops" # Sops keys
".config/easyeffects"
".config/gh" # Github CLI Auth
".local/share/keyrings" # Gnome Keyrings (Passwords)
".local/share/nvim" # NeoVim data (LazyVim, Mason, etc.)
@ -91,6 +93,8 @@
".local/share/umu"
".cache/mesa_shader_cache"
# ".local/share/Steam" # Symlinked to /games/Steam (Already Persistent)
".paradoxlauncher"
".local/share/Paradox Interactive"
".steam" # Steam Symlinks and logs
".config/steamtinkerlaunch" # Example of extra tools
".local/share/applications" # Desktop entries
@ -99,6 +103,11 @@
".local/share/qBittorrent"
".local/share/jellyfin-desktop"
".cache/jellyfin-desktop"
".local/share/zoxide"
".local/share/fish"
"fabric-docs-mcp"
];
files = [
];
};
};

View file

@ -69,6 +69,9 @@ in
"vsyscall=none"
"oops=panic"
"module.sig_enforce=1"
"amd_iommu=on"
"mitigations=auto"
"lockdown=confidentiality"
];
# Kernel sysctl hardening

View file

@ -9,136 +9,141 @@
}:
let
cfg = config.myModules.lutrisSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
in
{
nixpkgs.overlays = [
(final: prev: {
lutris-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.lutris.override {
extraPkgs = pkgs: [
pkgs.curl
pkgs.wget
pkgs.gnutar
pkgs.gzip
pkgs.zstd
pkgs.xz
pkgs.p7zip
pkgs.which
pkgs.file
pkgs.zenity
pkgs.vulkan-loader
pkgs.vulkan-tools
pkgs.unzip
pkgs.cabextract
pkgs.pciutils
pkgs.gamemode.lib
pkgs.xdg-utils
options.myModules.lutrisSandboxed = {
enable = lib.mkEnableOption "sandboxed Lutris with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
lutris-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.lutris.override {
extraPkgs = pkgs: [
pkgs.curl
pkgs.wget
pkgs.gnutar
pkgs.gzip
pkgs.zstd
pkgs.xz
pkgs.p7zip
pkgs.which
pkgs.file
pkgs.zenity
pkgs.vulkan-loader
pkgs.vulkan-tools
pkgs.unzip
pkgs.cabextract
pkgs.pciutils
pkgs.gamemode.lib
pkgs.xdg-utils
pkgs.umu-launcher
pkgs.sdl3
];
};
isFhsenv = true;
id = "net.lutris.Lutris";
env = {
WEBKIT_DISABLE_DMABUF_RENDERER = 1;
APPIMAGE_EXTRACT_AND_RUN = 1;
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION = "python";
GTK_THEME = "catppuccin-mocha-blue-standard";
BROWSER = "xdg-open";
XDG_CURRENT_DESKTOP = "niri";
XDG_SESSION_TYPE = "wayland";
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
VK_ICD_FILENAMES = "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json:/run/opengl-driver-32/share/vulkan/icd.d/radeon_icd.i686.json";
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
};
};
fhsenv = {
skipExtraInstallCmds = false;
};
# Disable Flatpak emulation
flatpak.enable = false;
# Filesystem: Limited to Games directory
mounts = {
read = sandboxUtils.mkGuiMounts.read ++ [
"$HOME/.local/share/Steam/compatibilitytools.d"
"/data/Torrents/Games" # Repack installers
];
readWrite = [
"$HOME/Games/windows"
"$HOME/Games/linux"
"$HOME/.local/share/icons"
"$HOME/.config/lutris"
"$HOME/.local/share/lutris"
"$HOME/.cache/lutris"
"$HOME/.steam"
"$HOME/.local/share/steam"
"$HOME/.local/share/umu"
"$HOME/.local/share/applications"
"$HOME/.local/share/desktop-directories"
]
++ cfg.extraBindMounts;
};
# Bind mount systemd-resolved socket directory to fix DNS
# The sandbox mounts a tmpfs on /run, so we need to validly expose this
fhsenv.bwrap.baseArgs = lib.mkForce (
sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { }
);
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy
''--bind "$XDG_RUNTIME_DIR/app/net.lutris.Lutris/bus" "$XDG_RUNTIME_DIR/bus"''
# D-Bus system proxy
''--bind "$XDG_RUNTIME_DIR/app/net.lutris.Lutris/bus_system" /run/dbus/system_bus_socket''
# Bind system themes to /usr/share
"--ro-bind /run/current-system/sw/share/themes /usr/share/themes"
"--ro-bind /run/current-system/sw/share/icons /usr/share/icons"
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "net.lutris.Lutris";
enableSystemBus = true;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.Flatpak"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.freedesktop.portal.*"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="net.lutris.Lutris"''
];
systemProxyArgs = [
"--filter"
''--talk="org.freedesktop.UDisks2"'' # Disk detection
];
};
isFhsenv = true;
id = "net.lutris.Lutris";
env = {
WEBKIT_DISABLE_DMABUF_RENDERER = 1;
APPIMAGE_EXTRACT_AND_RUN = 1;
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION = "python";
GTK_THEME = "catppuccin-mocha-blue-standard";
BROWSER = "xdg-open";
XDG_CURRENT_DESKTOP = "niri";
XDG_SESSION_TYPE = "wayland";
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
VK_ICD_FILENAMES = "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json:/run/opengl-driver-32/share/vulkan/icd.d/radeon_icd.i686.json";
};
};
})
];
fhsenv = {
skipExtraInstallCmds = false;
};
# Disable Flatpak emulation
flatpak.enable = false;
# Filesystem: Limited to Games directory
mounts = {
read = [
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
"$HOME/.local/share/Steam/compatibilitytools.d"
"$HOME/.config/gtk-3.0"
"$HOME/.config/gtk-4.0"
"$HOME/.icons"
"/data/Torrents/Games" # Repack installers
];
readWrite = [
"$HOME/Games/windows"
"$HOME/.local/share/icons"
"$HOME/.config/lutris"
"$HOME/.local/share/lutris"
"$HOME/.cache/lutris"
"$HOME/.steam"
"$HOME/.local/share/steam"
"$HOME/.local/share/umu"
"$HOME/.local/share/applications"
"$HOME/.local/share/desktop-directories"
];
};
# Bind mount systemd-resolved socket directory to fix DNS
# The sandbox mounts a tmpfs on /run, so we need to validly expose this
fhsenv.bwrap.additionalArgs = [
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
# D-Bus session proxy
''--bind "$XDG_RUNTIME_DIR/app/net.lutris.Lutris/bus" "$XDG_RUNTIME_DIR/bus"''
# D-Bus system proxy
''--bind "$XDG_RUNTIME_DIR/app/net.lutris.Lutris/bus_system" /run/dbus/system_bus_socket''
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
# Hardware access
"--dev-bind /dev/dri /dev/dri"
"--dev-bind /dev/shm /dev/shm"
"--ro-bind /sys /sys"
# Bind system themes to /usr/share
"--ro-bind /run/current-system/sw/share/themes /usr/share/themes"
"--ro-bind /run/current-system/sw/share/icons /usr/share/icons"
# OpenGL/Vulkan drivers
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "net.lutris.Lutris";
enableSystemBus = true;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.Flatpak"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.freedesktop.portal.*"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="net.lutris.Lutris"''
];
systemProxyArgs = [
"--filter"
''--talk="org.freedesktop.UDisks2"'' # Disk detection
];
};
};
})
];
environment.systemPackages = [ pkgs.lutris-sandboxed ];
};
}

View file

@ -6,11 +6,11 @@
}:
let
# Define the user and group consistently
user = "ashie";
cfg = config.myModules.media;
user = config.myModules.system.mainUser;
group = "users";
puid = "1000";
pgid = "100";
puid = toString config.users.users.${user}.uid;
pgid = "100"; # GID for 'users' group
# Common env vars to avoid repetition
commonEnv = {
@ -20,183 +20,202 @@ let
};
in
{
# 1. Enable Podman (required backend)
virtualisation = {
podman = {
enable = true;
autoPrune.enable = true;
};
oci-containers.backend = "podman";
options.myModules.media = {
enable = lib.mkEnableOption "media server stack (Arr suite + Jellyfin)";
};
# 2. Container Definitions
virtualisation.oci-containers.containers = {
config = lib.mkIf cfg.enable {
# 1. Enable Podman (required backend)
myModules.podman.enable = true;
virtualisation.podman.autoPrune.enable = true;
# --- VPN Gateway ---
vpn = {
image = "docker.io/qmcgaw/gluetun";
# The VPN manages the ports for the attached containers
ports = [
"8080:8080" # qBittorrent WebUI
"36630:36630" # Torrent Port TCP
"36630:36630/udp"
"9696:9696" # Prowlarr
"8191:8191" # Flaresolverr
];
environmentFiles = [ config.sops.templates."gluetun.env".path ];
environment = {
TZ = "Europe/Berlin";
DOT = "off"; # DNS over TLS off (optional)
FIREWALL_OUTBOUND_SUBNETS = "10.89.0.0/24"; # Allow access to local docker network
FIREWALL_VPN_INPUT_PORTS = "36630"; # Allow incoming torrent traffic
# 2. Container Definitions
virtualisation.oci-containers.containers = {
# --- VPN Gateway ---
vpn = {
image = "docker.io/qmcgaw/gluetun";
labels = { "io.containers.autoupdate" = "registry"; };
# The VPN manages the ports for the attached containers
ports = [
"127.0.0.1:8080:8080" # qBittorrent WebUI (Localhost only)
"36630:36630" # Torrent Port TCP (Public)
"36630:36630/udp" # Torrent Port UDP (Public)
"127.0.0.1:8191:8191" # Flaresolverr (Localhost only)
"127.0.0.1:9696:9696" # Prowlarr (Localhost only)
];
environmentFiles = [ config.sops.templates."gluetun.env".path ];
environment = {
TZ = "Europe/Berlin";
DOT = "off"; # DNS over TLS off (optional)
FIREWALL_OUTBOUND_SUBNETS = "10.89.0.0/24"; # Allow access to local docker network
FIREWALL_VPN_INPUT_PORTS = "36630"; # Allow incoming torrent traffic
};
extraOptions = [
"--cap-add=NET_ADMIN"
"--cap-add=NET_RAW"
"--device=/dev/net/tun:/dev/net/tun"
"--network=media" # It joins the bridge so others can talk to it
"--ip=10.89.0.5" # Static IP for VPN/Flaresolverr
"--network-alias=flaresolverr" # Allow other containers to reach Flaresolverr via VPN
"--add-host=sonarr:10.89.0.50" # Allow Prowlarr to reach Sonarr
"--add-host=radarr:10.89.0.51" # Allow Prowlarr to reach Radarr
"--add-host=prowlarr:127.0.0.1" # Prowlarr matches VPN IP for self-reference if needed
];
};
extraOptions = [
"--cap-add=NET_ADMIN"
"--cap-add=NET_RAW"
"--device=/dev/net/tun:/dev/net/tun"
"--network=media" # It joins the bridge so others can talk to it
"--network-alias=prowlarr" # Allow other containers to reach Prowlarr via VPN
"--network-alias=flaresolverr" # Allow other containers to reach Flaresolverr via VPN
"--add-host=sonarr:10.89.0.50" # Allow Prowlarr to reach Sonarr
"--add-host=radarr:10.89.0.51" # Allow Prowlarr to reach Radarr
];
};
# --- Torrent Client (Routed via VPN) ---
torrent = {
image = "lscr.io/linuxserver/qbittorrent:latest";
# VITAL: Reuse the VPN container's network stack
extraOptions = [ "--network=container:vpn" ];
dependsOn = [ "vpn" ];
environment = commonEnv // {
WEBUI_PORT = "8080";
# --- Torrent Client (Routed via VPN) ---
torrent = {
image = "lscr.io/linuxserver/qbittorrent:latest";
labels = { "io.containers.autoupdate" = "registry"; };
# VITAL: Reuse the VPN container's network stack
extraOptions = [ "--network=container:vpn" ];
dependsOn = [ "vpn" ];
environment = commonEnv // {
WEBUI_PORT = "8080";
};
volumes = [
"/var/lib/qbittorrent:/config"
"/data:/data"
];
};
volumes = [
"/var/lib/qbittorrent:/config"
"/data:/data"
];
};
# --- The Arr Stack ---
prowlarr = {
image = "lscr.io/linuxserver/prowlarr:latest";
extraOptions = [
"--network=container:vpn"
];
dependsOn = [ "vpn" ];
environment = commonEnv;
volumes = [ "/var/lib/nixarr/prowlarr:/config" ];
};
# --- The Arr Stack ---
prowlarr = {
image = "lscr.io/linuxserver/prowlarr:latest";
labels = { "io.containers.autoupdate" = "registry"; };
extraOptions = [
"--network=container:vpn"
];
dependsOn = [ "vpn" ];
environment = commonEnv;
volumes = [ "/var/lib/nixarr/prowlarr:/config" ];
};
sonarr = {
image = "lscr.io/linuxserver/sonarr:latest";
extraOptions = [
"--network=media"
"--ip=10.89.0.50"
];
ports = [ "8989:8989" ];
environment = commonEnv;
volumes = [
"/var/lib/nixarr/sonarr:/config"
"/data:/data"
];
};
sonarr = {
image = "lscr.io/linuxserver/sonarr:latest";
labels = { "io.containers.autoupdate" = "registry"; };
extraOptions = [
"--network=media"
"--ip=10.89.0.50"
"--dns=8.8.8.8"
"--add-host=qbittorrent:10.89.0.5"
"--add-host=prowlarr:10.89.0.5"
];
ports = [ "127.0.0.1:8989:8989" ];
environment = commonEnv;
volumes = [
"/var/lib/nixarr/sonarr:/config"
"/data:/data"
];
};
radarr = {
image = "lscr.io/linuxserver/radarr:latest";
extraOptions = [
"--network=media"
"--ip=10.89.0.51"
];
ports = [ "7878:7878" ];
environment = commonEnv;
volumes = [
"/var/lib/nixarr/radarr:/config"
"/data:/data"
];
};
radarr = {
image = "lscr.io/linuxserver/radarr:latest";
labels = { "io.containers.autoupdate" = "registry"; };
extraOptions = [
"--network=media"
"--ip=10.89.0.51"
"--dns=8.8.8.8"
"--add-host=qbittorrent:10.89.0.5"
"--add-host=prowlarr:10.89.0.5"
];
ports = [ "127.0.0.1:7878:7878" ];
environment = commonEnv;
volumes = [
"/var/lib/nixarr/radarr:/config"
"/data:/data"
];
};
# --- Media Server ---
jellyfin = {
image = "lscr.io/linuxserver/jellyfin:latest";
extraOptions = [
"--network=media"
"--device=/dev/dri:/dev/dri"
];
ports = [ "8096:8096" ];
environment = commonEnv;
volumes = [
"/var/lib/nixarr/jellyfin:/config"
"/data:/data"
];
};
# --- Media Server ---
jellyfin = {
image = "lscr.io/linuxserver/jellyfin:latest";
labels = { "io.containers.autoupdate" = "registry"; };
extraOptions = [
"--network=media"
"--device=/dev/dri:/dev/dri"
"--dns=8.8.8.8"
"--ip=10.89.0.4"
];
ports = [ "127.0.0.1:8096:8096" ];
environment = commonEnv;
volumes = [
"/var/lib/nixarr/jellyfin:/config"
"/data:/data"
];
};
jellyseerr = {
image = "ghcr.io/fallenbagel/jellyseerr:latest";
extraOptions = [ "--network=media" ];
ports = [ "5055:5055" ];
environment = commonEnv;
volumes = [ "/var/lib/nixarr/jellyseerr:/app/config" ];
};
jellyseerr = {
image = "ghcr.io/seerr-team/seerr:latest"; # Migrated from jellyseerr (stale) to seerr (v3+)
labels = { "io.containers.autoupdate" = "registry"; };
extraOptions = [
"--init" # Required for Seerr
"--network=media"
"--dns=8.8.8.8"
"--ip=10.89.0.3"
"--add-host=sonarr:10.89.0.50"
"--add-host=radarr:10.89.0.51"
"--add-host=jellyfin:10.89.0.4"
];
ports = [ "127.0.0.1:5055:5055" ];
environment = commonEnv;
volumes = [ "/var/lib/nixarr/jellyseerr:/app/config" ];
};
flaresolverr = {
image = "ghcr.io/flaresolverr/flaresolverr:latest";
extraOptions = [ "--network=container:vpn" ];
dependsOn = [ "vpn" ];
environment = {
TZ = "Europe/Berlin";
flaresolverr = {
image = "ghcr.io/flaresolverr/flaresolverr:latest";
labels = { "io.containers.autoupdate" = "registry"; };
extraOptions = [ "--network=container:vpn" ];
dependsOn = [ "vpn" ];
environment = {
TZ = "Europe/Berlin";
};
};
};
# 3. Network Setup (Fixed)
# Ensure the network is created before ANY container starts
systemd.services.create-media-network = {
script = ''
${pkgs.podman}/bin/podman network exists media || ${pkgs.podman}/bin/podman network create --subnet 10.89.0.0/24 media
'';
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# Removed 'User = ashie' -> Networks created by root are visible to root services
};
};
# Ensure containers wait for the network
systemd.services."podman-vpn".requires = [ "create-media-network.service" ];
systemd.services."podman-vpn".after = [ "create-media-network.service" ];
# (Repeat for others if they don't depend on VPN, but usually unnecessary if they all join 'media')
# 4. Permissions
systemd.tmpfiles.rules = [
"d /data 0775 ${user} ${group} - -"
"d /var/lib/nixarr/prowlarr 0755 ${user} ${group} - -"
"d /var/lib/nixarr/sonarr 0755 ${user} ${group} - -"
"d /var/lib/nixarr/radarr 0755 ${user} ${group} - -"
"d /var/lib/nixarr/jellyfin 0755 ${user} ${group} - -"
"d /var/lib/nixarr/jellyseerr 0755 ${user} ${group} - -"
"d /var/lib/qbittorrent 0755 ${user} ${group} - -"
];
users.users.${user}.extraGroups = [ "media" ];
# 5. Firewall
networking.firewall.allowedTCPPorts = [
80
443
36630
9696
];
networking.firewall.allowedUDPPorts = [
36630
443
];
};
# 3. Network Setup (Fixed)
# Ensure the network is created before ANY container starts
systemd.services.create-media-network = {
script = ''
${pkgs.podman}/bin/podman network exists media || ${pkgs.podman}/bin/podman network create media
'';
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# Removed 'User = ashie' -> Networks created by root are visible to root services
};
};
# Ensure containers wait for the network
systemd.services."podman-vpn".requires = [ "create-media-network.service" ];
systemd.services."podman-vpn".after = [ "create-media-network.service" ];
# (Repeat for others if they don't depend on VPN, but usually unnecessary if they all join 'media')
# 4. Permissions
systemd.tmpfiles.rules = [
"d /data 0775 ${user} media - -"
"d /var/lib/nixarr/prowlarr 0755 ${user} ${group} - -"
"d /var/lib/nixarr/sonarr 0755 ${user} ${group} - -"
"d /var/lib/nixarr/radarr 0755 ${user} ${group} - -"
"d /var/lib/nixarr/jellyfin 0755 ${user} ${group} - -"
"d /var/lib/nixarr/jellyseerr 0755 ${user} ${group} - -"
"d /var/lib/qbittorrent 0755 ${user} ${group} - -"
];
users.users.${user}.extraGroups = [ "media" ];
# 5. Firewall
networking.firewall.allowedTCPPorts = [
80
443
9696
8989
7878
8096
5055
8080
36630
8082
8191
];
networking.firewall.allowedUDPPorts = [
36630
443
];
}

View file

@ -147,6 +147,7 @@ in
];
ExecStart = ''
${pkgs.podman}/bin/podman run --rm --name ollama \
--label "io.containers.autoupdate=registry" \
--network=antigravity-net \
--network-alias=ollama \
--dns=8.8.8.8 \

View file

@ -112,6 +112,7 @@ in
];
ExecStart = ''
${pkgs.podman}/bin/podman run --rm --name open-webui \
--label "io.containers.autoupdate=registry" \
--network=antigravity-net \
--dns=8.8.8.8 \
--userns=auto \

View file

@ -8,7 +8,9 @@
}:
let
cfg = config.myModules.prismlauncherSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
# Libraries required by Minecraft natives (LWJGL), various mods,
# and the Microsoft authentication flow (NSS/NSPR).
@ -48,153 +50,137 @@ let
];
in
{
nixpkgs.overlays = [
(final: prev: {
prismlauncher-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
id = "org.prismlauncher.PrismLauncher";
package = pkgs.prismlauncher.overrideAttrs (old: {
pname = "prismlauncher";
version = old.version or "9.1";
buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.mimalloc ];
options.myModules.prismlauncherSandboxed = {
enable = lib.mkEnableOption "sandboxed PrismLauncher with nix-bwrapper";
# Keep runtimeLibs in closure without injecting them into environment
postInstall = (old.postInstall or "") + ''
mkdir -p $out/share/prismlauncher-sandboxed
echo "${lib.makeLibraryPath runtimeLibs}" > $out/share/prismlauncher-sandboxed/libs
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
prismlauncher-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
id = "org.prismlauncher.PrismLauncher";
package = pkgs.prismlauncher.overrideAttrs (old: {
pname = "prismlauncher";
version = old.version or "9.1";
buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.mimalloc ];
# Keep runtimeLibs in closure without injecting them into environment
postInstall = (old.postInstall or "") + ''
mkdir -p $out/share/prismlauncher-sandboxed
echo "${lib.makeLibraryPath runtimeLibs}" > $out/share/prismlauncher-sandboxed/libs
'';
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
"--set MIMALLOC_PATH ${pkgs.mimalloc}/lib/libmimalloc.so"
"--prefix LD_PRELOAD : ${pkgs.mimalloc}/lib/libmimalloc.so"
];
});
env = {
# Propagate XDG_DATA_DIRS so themes/icons can be found
# XDG_DATA_DIRS = "$XDG_DATA_DIRS";
BROWSER = "firefox";
QT_QPA_PLATFORMTHEME = "";
QT_STYLE_OVERRIDE = "fusion";
};
};
sockets.x11 = true; # Old versions of minecraft require X11, and forge still doesnt care its breaking wayland.
sockets.wayland = true;
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { } ++ [
"--ro-bind /run/dbus /run/dbus"
]);
mounts = {
read = sandboxUtils.mkGuiMounts.read ++ [
"$HOME/Downloads"
];
readWrite = [
"$HOME/.local/share/PrismLauncher"
"$HOME/.cache/PrismLauncher"
] ++ cfg.extraBindMounts;
};
dbus.enable = false;
script.preCmds.stage2 =
let
jvmArgs = "-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1 -XX:ConcGCThreads=4";
glfwPath = "${pkgs.glfw}/lib/libglfw.so.3";
dbusScript = sandboxUtils.mkDbusProxyScript {
appId = "org.prismlauncher.PrismLauncher";
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--own="org.prismlauncher.PrismLauncher"''
''--own="org.prismlauncher.PrismLauncher.*"''
];
};
in
''
${dbusScript}
# Force Configs (JVM Args + GLFW)
cfg="$HOME/.local/share/PrismLauncher/prismlauncher.cfg"
if [ -f "$cfg" ]; then
# JVM Args
if ${pkgs.gnugrep}/bin/grep -q "^JvmArgs=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^JvmArgs=.*|JvmArgs=${jvmArgs}|" "$cfg"
else
if ${pkgs.gnugrep}/bin/grep -q "^\[General\]" "$cfg"; then
${pkgs.gnused}/bin/sed -i "/^\[General\]/a JvmArgs=${jvmArgs}" "$cfg"
else
echo "JvmArgs=${jvmArgs}" >> "$cfg"
fi
fi
# GLFW Settings
# 1. CustomGLFWPath
if ${pkgs.gnugrep}/bin/grep -q "^CustomGLFWPath=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^CustomGLFWPath=.*|CustomGLFWPath=${glfwPath}|" "$cfg"
else
echo "CustomGLFWPath=${glfwPath}" >> "$cfg"
fi
# 2. UseNativeGLFW
if ${pkgs.gnugrep}/bin/grep -q "^UseNativeGLFW=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^UseNativeGLFW=.*|UseNativeGLFW=true|" "$cfg"
else
echo "UseNativeGLFW=true" >> "$cfg"
fi
fi
'';
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
"--set MIMALLOC_PATH ${pkgs.mimalloc}/lib/libmimalloc.so"
"--prefix LD_PRELOAD : ${pkgs.mimalloc}/lib/libmimalloc.so"
];
});
env = {
# Propagate XDG_DATA_DIRS so themes/icons can be found
# XDG_DATA_DIRS = "$XDG_DATA_DIRS";
BROWSER = "firefox";
QT_QPA_PLATFORMTHEME = "";
QT_STYLE_OVERRIDE = "fusion";
};
};
sockets.x11 = true; # Old versions of minecraft require X11, and forge still doesnt care its breaking wayland.
sockets.wayland = true;
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# Bind ro system paths commonly needed
# "--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--ro-bind /run/dbus /run/dbus"
];
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/qt6ct"
"$HOME/.config/Kvantum"
"$HOME/.config/MangoHud"
"$HOME/Downloads"
];
readWrite = [
"$HOME/.local/share/PrismLauncher"
"$HOME/.cache/PrismLauncher"
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus proxy
''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
];
};
})
];
dbus.enable = false;
script.preCmds.stage2 =
let
jvmArgs = "-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1 -XX:ConcGCThreads=4";
glfwPath = "${pkgs.glfw}/lib/libglfw.so.3";
dbusScript = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "org.prismlauncher.PrismLauncher";
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--own="org.prismlauncher.PrismLauncher"''
''--own="org.prismlauncher.PrismLauncher.*"''
];
};
in
''
${dbusScript}
# Force Configs (JVM Args + GLFW)
cfg="$HOME/.local/share/PrismLauncher/prismlauncher.cfg"
if [ -f "$cfg" ]; then
# JVM Args
if ${pkgs.gnugrep}/bin/grep -q "^JvmArgs=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^JvmArgs=.*|JvmArgs=${jvmArgs}|" "$cfg"
else
if ${pkgs.gnugrep}/bin/grep -q "^\[General\]" "$cfg"; then
${pkgs.gnused}/bin/sed -i "/^\[General\]/a JvmArgs=${jvmArgs}" "$cfg"
else
echo "JvmArgs=${jvmArgs}" >> "$cfg"
fi
fi
# GLFW Settings
# 1. CustomGLFWPath
if ${pkgs.gnugrep}/bin/grep -q "^CustomGLFWPath=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^CustomGLFWPath=.*|CustomGLFWPath=${glfwPath}|" "$cfg"
else
echo "CustomGLFWPath=${glfwPath}" >> "$cfg"
fi
# 2. UseNativeGLFW
if ${pkgs.gnugrep}/bin/grep -q "^UseNativeGLFW=" "$cfg"; then
${pkgs.gnused}/bin/sed -i "s|^UseNativeGLFW=.*|UseNativeGLFW=true|" "$cfg"
else
echo "UseNativeGLFW=true" >> "$cfg"
fi
fi
'';
fhsenv.bwrap.additionalArgs = [
# D-Bus proxy
''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse (PipeWire hosts both)
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
};
})
];
environment.systemPackages = [ pkgs.prismlauncher-sandboxed ];
};
}

View file

@ -16,6 +16,8 @@
let
cfg = config.myModules.redlib;
mainUser = config.myModules.system.mainUser;
mainUserUid = toString config.users.users.${mainUser}.uid;
in
{
options.myModules.redlib = {
@ -34,6 +36,7 @@ in
# Redlib Container
virtualisation.oci-containers.containers."redlib" = {
image = "quay.io/redlib/redlib:latest";
labels = { "io.containers.autoupdate" = "registry"; };
# ports = [ "127.0.0.1:${toString cfg.port}:8080" ]; # Port exposed via VPN
extraOptions = [
"--pull=always"
@ -44,20 +47,20 @@ in
};
# Rootless Overrides
systemd.services."podman-redlib".serviceConfig.User = lib.mkForce "ashie";
systemd.services."podman-redlib".serviceConfig.User = lib.mkForce mainUser;
systemd.services."podman-redlib".environment = {
HOME = "/home/ashie";
XDG_RUNTIME_DIR = "/run/user/1000";
HOME = "/home/${mainUser}";
XDG_RUNTIME_DIR = "/run/user/${mainUserUid}";
};
systemd.services."podman-redlib".serviceConfig.Type = lib.mkForce "simple";
systemd.services."podman-redlib".serviceConfig.Delegate = true;
systemd.services."podman-redlib".after = [
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"podman-vpn.service"
];
systemd.services."podman-redlib".requires = [
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"podman-vpn.service"
];
};
}
}

View file

@ -7,122 +7,114 @@
}:
let
cfg = config.myModules.ryubingSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
appId = "org.ryubing.Ryubing";
in
{
nixpkgs.overlays = [
(final: prev: {
ryubing-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = pkgs.ryubing;
id = appId;
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Ryubing uses Avalonia which works better with X11
AVALONIA_SCREEN_SCALE_FACTOR = "1";
options.myModules.ryubingSandboxed = {
enable = lib.mkEnableOption "sandboxed Ryubing with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
ryubing-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = pkgs.ryubing;
id = appId;
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Ryubing uses Avalonia which works better with X11
AVALONIA_SCREEN_SCALE_FACTOR = "1";
};
};
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Disable Flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for online features
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { } ++ [
# Fix for amdgpu.ids missing - use tmpfs so mkdir can succeed
"--tmpfs /usr/share"
"--ro-bind ${pkgs.libdrm}/share/libdrm /usr/share/libdrm"
"--ro-bind-try /nix/store /nix/store"
]);
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
# Also create required directories before bwrap runs
script.preCmds.stage2 = ''
# Create directories that bwrap will bind
# Note: Ryubing still uses Ryujinx config paths
mkdir -p "$HOME/.config/Ryujinx/system"
mkdir -p "$HOME/.config/Ryujinx/bis/system/Contents/registered"
mkdir -p "$HOME/.local/share/Ryujinx"
mkdir -p "$HOME/Games/Switch"
''
+ sandboxUtils.mkDbusProxyScript {
inherit appId;
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.portal.FileChooser"''
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--own="${appId}"''
''--own="${appId}.*"''
];
};
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
# Read-write mounts
"--bind $HOME/Games/Switch $HOME/Games/Switch"
"--bind $HOME/.config/Ryujinx $HOME/.config/Ryujinx"
"--bind $HOME/.local/share/Ryujinx $HOME/.local/share/Ryujinx"
];
mounts = {
read = sandboxUtils.mkGuiMounts.read;
readWrite = cfg.extraBindMounts;
};
};
})
];
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Disable Flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for online features
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--dev-bind /dev/shm /dev/shm" # Shared memory
"--dev-bind-try /dev/uinput /dev/uinput" # Controller support
"--dev-bind-try /dev/input /dev/input"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# Fix for amdgpu.ids missing - use tmpfs so mkdir can succeed
"--tmpfs /usr/share"
"--ro-bind ${pkgs.libdrm}/share/libdrm /usr/share/libdrm"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--ro-bind-try /nix/store /nix/store"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
# udev for controller hotplug
"--ro-bind-try /run/udev /run/udev"
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
# Also create required directories before bwrap runs
script.preCmds.stage2 = ''
# Create directories that bwrap will bind
# Note: Ryubing still uses Ryujinx config paths
mkdir -p "$HOME/.config/Ryujinx/system"
mkdir -p "$HOME/.config/Ryujinx/bis/system/Contents/registered"
mkdir -p "$HOME/.local/share/Ryujinx"
mkdir -p "$HOME/Games/Switch"
''
+ (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
inherit appId;
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.Desktop"''
''--talk="org.freedesktop.portal.OpenURI"''
''--talk="org.freedesktop.portal.FileChooser"''
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--own="${appId}"''
''--own="${appId}.*"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
# Manual mounts for data persistence
"--ro-bind-try $HOME/.config/kdedefaults $HOME/.config/kdedefaults"
"--ro-bind-try $HOME/.local/share/color-schemes $HOME/.local/share/color-schemes"
"--ro-bind-try $HOME/.config/fontconfig $HOME/.config/fontconfig"
"--ro-bind-try $HOME/.local/share/fonts $HOME/.local/share/fonts"
"--ro-bind-try $HOME/.icons $HOME/.icons"
"--ro-bind-try $HOME/.themes $HOME/.themes"
"--ro-bind-try $HOME/.config/MangoHud $HOME/.config/MangoHud"
# Read-write mounts
"--bind $HOME/Games/Switch $HOME/Games/Switch"
"--bind $HOME/.config/Ryujinx $HOME/.config/Ryujinx"
"--bind $HOME/.local/share/Ryujinx $HOME/.local/share/Ryujinx"
];
};
})
];
environment.systemPackages = [ pkgs.ryubing-sandboxed ];
};
}

View file

@ -1,91 +1,311 @@
{ pkgs, lib }:
{
let
# Generates the shell script content to set up xdg-dbus-proxy inside a bwrap user namespace.
# This is used for sandboxed apps that run with unshareUser = true.
mkDbusProxyScript = {
appId, # Unique ID for the app (e.g. "org.mozilla.firefox")
proxyArgs, # Arguments for xdg-dbus-proxy (session bus). Can be string or list.
socketPath ? "$XDG_RUNTIME_DIR/app/${appId}/bus",
upstreamSocket ? "$XDG_RUNTIME_DIR/bus",
enableSystemBus ? false,
systemProxyArgs ? "", # Arguments for xdg-dbus-proxy (system bus). Can be string or list.
systemSocketPath ? "$XDG_RUNTIME_DIR/app/${appId}/bus_system",
systemUpstreamSocket ? "/run/dbus/system_bus_socket"
}:
let
bwrap = "${pkgs.bubblewrap}/bin/bwrap";
dbusProxy = "${pkgs.xdg-dbus-proxy}/bin/xdg-dbus-proxy";
coreutils = "${pkgs.coreutils}/bin";
# Helper to normalize args (support list or string)
normalizeArgs = args: if builtins.isList args then lib.concatStringsSep " " args else args;
pArgs = normalizeArgs proxyArgs;
sArgs = normalizeArgs systemProxyArgs;
mkDbusProxyScript =
{
appId,
# Unique ID for the app (e.g. "org.mozilla.firefox")
proxyArgs, # Arguments for xdg-dbus-proxy (session bus). Can be string or list.
socketPath ? "$XDG_RUNTIME_DIR/app/${appId}/bus",
upstreamSocket ? "$XDG_RUNTIME_DIR/bus",
enableSystemBus ? false,
systemProxyArgs ? "", # Arguments for xdg-dbus-proxy (system bus). Can be string or list.
systemSocketPath ? "$XDG_RUNTIME_DIR/app/${appId}/bus_system",
systemUpstreamSocket ? "/run/dbus/system_bus_socket",
}:
let
bwrap = "${pkgs.bubblewrap}/bin/bwrap";
dbusProxy = "${pkgs.xdg-dbus-proxy}/bin/xdg-dbus-proxy";
coreutils = "${pkgs.coreutils}/bin";
# Helper to generate the function definition
# We bind XDG_RUNTIME_DIR to allow creating the socket.
# We optionally bind /run/dbus for the system bus socket.
mkProxyFunc = name: upstream: sock: args: bindSystem: ''
${name}() {
${coreutils}/mkdir -p "$(${coreutils}/dirname "${sock}")"
${bwrap} \
--unshare-user \
--dev /dev \
--proc /proc \
--new-session \
--ro-bind /nix/store /nix/store \
--bind "$XDG_RUNTIME_DIR" "$XDG_RUNTIME_DIR" \
${if bindSystem then "--ro-bind /run/dbus /run/dbus" else ""} \
--die-with-parent \
--clearenv \
-- \
${dbusProxy} "unix:path=${upstream}" "${sock}" ${args}
# Helper to normalize args (support list or string)
normalizeArgs = args: if builtins.isList args then lib.concatStringsSep " " args else args;
pArgs = normalizeArgs proxyArgs;
sArgs = normalizeArgs systemProxyArgs;
# Helper to generate the function definition
# We bind XDG_RUNTIME_DIR to allow creating the socket.
# We optionally bind /run/dbus for the system bus socket.
mkProxyFunc = name: upstream: sock: args: bindSystem: ''
${name}() {
${coreutils}/mkdir -p "$(${coreutils}/dirname "${sock}")"
${bwrap} \
--unshare-user \
--dev /dev \
--proc /proc \
--new-session \
--ro-bind /nix/store /nix/store \
--bind "$XDG_RUNTIME_DIR" "$XDG_RUNTIME_DIR" \
${if bindSystem then "--ro-bind /run/dbus /run/dbus" else ""} \
--die-with-parent \
--clearenv \
-- \
${dbusProxy} "unix:path=${upstream}" "${sock}" ${args}
}
'';
sessionFunc = mkProxyFunc "set_up_dbus_proxy" upstreamSocket socketPath pArgs false;
systemFunc =
if enableSystemBus then
mkProxyFunc "set_up_system_dbus_proxy" systemUpstreamSocket systemSocketPath sArgs true
else
"";
waitLoop = ''
# Wait for socket(s) with fail-fast check
for i in $(${coreutils}/seq 1 50);
do
# Check if processes are still running
if ! kill -0 "$PID_SESSION" 2>/dev/null;
then
echo "Error: Session D-Bus proxy (PID $PID_SESSION) died unexpectedly." >&2
exit 1
fi
${
if enableSystemBus then
''
if ! kill -0 "$PID_SYSTEM" 2>/dev/null;
then
echo "Error: System D-Bus proxy (PID $PID_SYSTEM) died unexpectedly." >&2
exit 1
fi
''
else
""
}
# Check for sockets
if [ -S "${socketPath}" ]${if enableSystemBus then " && [ -S \"${systemSocketPath}\" ]" else ""};
then
break
fi
${coreutils}/sleep 0.02
done
'';
in
''
${sessionFunc}
${systemFunc}
set_up_dbus_proxy &
PID_SESSION=$!
${
if enableSystemBus then
''
set_up_system_dbus_proxy &
PID_SYSTEM=$!
''
else
""
}
${waitLoop}
'';
sessionFunc = mkProxyFunc "set_up_dbus_proxy" upstreamSocket socketPath pArgs false;
systemFunc = if enableSystemBus
then mkProxyFunc "set_up_system_dbus_proxy" systemUpstreamSocket systemSocketPath sArgs true
else "";
waitLoop = ''
# Wait for socket(s) with fail-fast check
for i in $(${coreutils}/seq 1 50);
do
# Check if processes are still running
if ! kill -0 "$PID_SESSION" 2>/dev/null;
then
echo "Error: Session D-Bus proxy (PID $PID_SESSION) died unexpectedly." >&2
exit 1
fi
${if enableSystemBus then ''
if ! kill -0 "$PID_SYSTEM" 2>/dev/null;
then
echo "Error: System D-Bus proxy (PID $PID_SYSTEM) died unexpectedly." >&2
exit 1
fi
'' else ""}
# Standard Common Binds (System Essentials)
mkCommonBindArgs =
{ config, lib }:
[
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind-try /dev/ntsync /dev/ntsync"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.${config.myModules.system.mainUser}.uid}"
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--unsetenv LD_PRELOAD"
];
# Check for sockets
if [ -S "${socketPath}" ]${if enableSystemBus then " && [ -S \"${systemSocketPath}\" ]" else ""};
then
break
fi
${coreutils}/sleep 0.02
done
'';
# GUI Application Binds (Fonts, Themes, Wayland/X11 sockets)
mkGuiBindArgs =
{ }:
[
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
in ''
${sessionFunc}
${systemFunc}
# GUI Mounts (Fonts, Configs)
mkGuiMounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/qt6ct"
"$HOME/.config/Kvantum"
"$HOME/.config/MangoHud"
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
"$HOME/.config/gtk-3.0"
"$HOME/.config/gtk-4.0"
];
};
set_up_dbus_proxy &
PID_SESSION=$!
${if enableSystemBus then ''
set_up_system_dbus_proxy &
PID_SYSTEM=$!
'' else ""}
${waitLoop}
'';
}
# Gaming Binds (GPU, Controllers, Input)
mkGamingBindArgs =
{ }:
[
"--dev-bind /dev/dri /dev/dri" # GPU
"--dev-bind /dev/shm /dev/shm" # Shared Mem
"--dev-bind-try /dev/uinput /dev/uinput"
"--dev-bind-try /dev/input /dev/input"
"--dev-bind-try /dev/hidraw0 /dev/hidraw0"
"--dev-bind-try /dev/hidraw1 /dev/hidraw1"
"--dev-bind-try /dev/hidraw2 /dev/hidraw2"
"--dev-bind-try /dev/hidraw3 /dev/hidraw3"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--ro-bind-try /run/udev /run/udev"
];
# Helper to create a sandboxed application module
mkSandboxedApp =
{
config,
lib,
pkgs,
inputs,
optionName, # e.g. "steamSandboxed"
packageName, # e.g. "steam-sandboxed"
description,
package, # e.g. pkgs.steam
appId, # e.g. "com.valvesoftware.Steam"
isFhsenv ? false,
env ? { },
sockets ? {
x11 = true;
wayland = true;
},
flatpak ? false,
# Mounts
mounts ? {
read = [ ];
readWrite = [ ];
},
# Bwrap/Fhsenv options
fhsenvOpts ? {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
},
fhsenvExtra ? { }, # merged into fhsenv
# Args
baseArgs ? (mkCommonBindArgs { inherit config lib; }),
additionalArgs ? [ ], # merged into fhsenv.bwrap.additionalArgs
# DBus
dbusProxy ? null, # { rules ? [], systemRules ? [], enableSystemBus ? false }
}:
let
cfg = config.myModules.${optionName};
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
# Handle DBus Proxy Logic
hasDbusProxy = dbusProxy != null;
proxyScript =
if hasDbusProxy then
mkDbusProxyScript {
inherit appId;
enableSystemBus = dbusProxy.enableSystemBus or false;
proxyArgs = dbusProxy.rules or [ ];
systemProxyArgs = dbusProxy.systemRules or [ ];
}
else
"";
dbusBindArgs =
if hasDbusProxy then
[
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
]
++ (
if (dbusProxy.enableSystemBus or false) then
[
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus_system" /run/dbus/system_bus_socket''
]
else
[ ]
)
else
[ ];
in
{
options.myModules.${optionName} = {
enable = lib.mkEnableOption description;
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
${packageName} = bwrapperPkgs.mkBwrapper {
app = {
package = package;
inherit isFhsenv;
env = env;
id = appId;
};
inherit sockets;
flatpak.enable = flatpak;
fhsenv = fhsenvExtra // {
opts = fhsenvOpts;
bwrap = {
baseArgs = lib.mkForce baseArgs;
additionalArgs = mkGuiBindArgs { } ++ additionalArgs ++ dbusBindArgs;
};
};
mounts = {
read = mkGuiMounts.read ++ mounts.read;
readWrite = mounts.readWrite ++ cfg.extraBindMounts;
};
dbus.enable = false;
script.preCmds.stage2 = proxyScript;
};
})
];
environment.systemPackages = [ pkgs.${packageName} ];
};
};
in
{
inherit
mkDbusProxyScript
mkCommonBindArgs
mkGuiBindArgs
mkGuiMounts
mkGamingBindArgs
mkSandboxedApp
;
}

View file

@ -17,189 +17,8 @@
let
cfg = config.myModules.searxng;
catppuccinCss = pkgs.writeText "searxng-catppuccin.css" ''
:root {
/* Mocha (Dark) */
--cat-rosewater: #f5e0dc;
--cat-flamingo: #f2cdcd;
--cat-pink: #f5c2e7;
--cat-mauve: #cba6f7;
--cat-red: #f38ba8;
--cat-maroon: #eba0ac;
--cat-peach: #fab387;
--cat-yellow: #f9e2af;
--cat-green: #a6e3a1;
--cat-teal: #94e2d5;
--cat-sky: #89dceb;
--cat-sapphire: #74c7ec;
--cat-blue: #89b4fa;
--cat-lavender: #b4befe;
--cat-text: #cdd6f4;
--cat-subtext1: #bac2de;
--cat-subtext0: #a6adc8;
--cat-overlay2: #9399b2;
--cat-overlay1: #7f849c;
--cat-overlay0: #6c7086;
--cat-surface2: #585b70;
--cat-surface1: #45475a;
--cat-surface0: #313244;
--cat-base: #1e1e2e;
--cat-mantle: #181825;
--cat-crust: #11111b;
}
@media (prefers-color-scheme: light) {
:root {
/* Latte (Light) */
--cat-rosewater: #dc8a78;
--cat-flamingo: #dd7878;
--cat-pink: #ea76cb;
--cat-mauve: #8839ef;
--cat-red: #d20f39;
--cat-maroon: #e64553;
--cat-peach: #fe640b;
--cat-yellow: #df8e1d;
--cat-green: #40a02b;
--cat-teal: #179287;
--cat-sky: #04a5e5;
--cat-sapphire: #209fb5;
--cat-blue: #1e66f5;
--cat-lavender: #7287fd;
--cat-text: #4c4f69;
--cat-subtext1: #5c5f77;
--cat-subtext0: #6c6f85;
--cat-overlay2: #7c7f93;
--cat-overlay1: #8c8fa1;
--cat-overlay0: #9ca0b0;
--cat-surface2: #acb0be;
--cat-surface1: #bcc0cc;
--cat-surface0: #ccd0da;
--cat-base: #eff1f5;
--cat-mantle: #e6e9ef;
--cat-crust: #dce0e8;
}
}
/* Apply variables */
:root {
--color-base-font: var(--cat-text);
--color-base-background: var(--cat-base);
--color-base-background-mobile: var(--cat-base);
--color-url-font: var(--cat-mauve);
--color-url-visited-font: var(--cat-mauve);
--color-header-background: var(--cat-mantle);
--color-header-border: var(--cat-mantle);
--color-footer-background: var(--cat-mantle);
--color-footer-border: var(--cat-mantle);
--color-sidebar-border: var(--cat-base);
--color-sidebar-font: var(--cat-text);
--color-sidebar-background: var(--cat-base);
--color-backtotop-font: var(--cat-subtext1);
--color-backtotop-border: var(--cat-surface0);
--color-backtotop-background: var(--cat-surface0);
--color-btn-background: var(--cat-mauve);
--color-btn-font: var(--cat-base);
--color-show-btn-background: var(--cat-mauve);
--color-show-btn-font: var(--cat-base);
--color-search-border: var(--cat-surface0);
--color-search-shadow: 0 2px 8px var(--cat-crust);
--color-search-background: var(--cat-surface0);
--color-search-font: var(--cat-text);
--color-search-background-hover: var(--cat-mauve);
--color-error: var(--cat-red);
--color-error-background: var(--cat-surface0);
--color-warning: var(--cat-yellow);
--color-warning-background: var(--cat-surface0);
--color-success: var(--cat-green);
--color-success-background: var(--cat-surface0);
--color-categories-item-selected-font: var(--cat-text);
--color-categories-item-border-selected: var(--cat-mauve);
--color-autocomplete-font: var(--cat-subtext1);
--color-autocomplete-border: var(--cat-surface0);
--color-autocomplete-shadow: 0 2px 8px var(--cat-crust);
--color-autocomplete-background: var(--cat-surface0);
--color-autocomplete-background-hover: var(--cat-surface1);
--color-answer-font: var(--cat-text);
--color-answer-background: var(--cat-mantle);
--color-result-background: var(--cat-mantle);
--color-result-border: var(--cat-base);
--color-result-url-font: var(--cat-subtext1);
--color-result-vim-selected: var(--cat-surface0);
--color-result-vim-arrow: var(--cat-mauve);
--color-result-description-highlight-font: var(--cat-text);
--color-result-link-font: var(--cat-mauve);
--color-result-link-font-highlight: var(--cat-mauve);
--color-result-link-visited-font: var(--cat-mauve);
--color-result-publishdate-font: var(--cat-surface2);
--color-result-engines-font: var(--cat-surface2);
--color-result-search-url-border: var(--cat-surface2);
--color-result-search-url-font: var(--cat-text);
--color-result-detail-font: var(--cat-text);
--color-result-detail-label-font: var(--cat-subtext0);
--color-result-detail-background: var(--cat-base);
--color-result-detail-hr: var(--cat-base);
--color-result-detail-link: var(--cat-mauve);
--color-result-detail-loader-border: rgba(255, 255, 255, 0.2);
--color-result-detail-loader-borderleft: var(--cat-crust);
--color-result-image-span-font: var(--cat-text);
--color-result-image-span-font-selected: var(--cat-base);
--color-result-image-background: var(--cat-mantle);
--color-settings-tr-hover: var(--cat-surface0);
--color-settings-engine-description-font: var(--cat-text);
--color-settings-engine-group-background: var(--cat-surface0);
--color-toolkit-badge-font: var(--cat-text);
--color-toolkit-badge-background: var(--cat-surface0);
--color-toolkit-kbd-font: var(--cat-text);
--color-toolkit-kbd-background: var(--cat-mantle);
--color-toolkit-dialog-border: var(--cat-mantle);
--color-toolkit-dialog-background: var(--cat-mantle);
--color-toolkit-tabs-label-border: var(--cat-base);
--color-toolkit-tabs-section-border: var(--cat-base);
--color-toolkit-select-background: var(--cat-surface0);
--color-toolkit-select-border: var(--cat-surface0);
--color-toolkit-select-background-hover: var(--cat-surface1);
--color-toolkit-input-text-font: var(--cat-text);
--color-toolkit-checkbox-onoff-off-background: var(--cat-surface0);
--color-toolkit-checkbox-onoff-on-background: var(--cat-surface0);
--color-toolkit-checkbox-onoff-on-mark-background: var(--cat-green);
--color-toolkit-checkbox-onoff-on-mark-color: var(--cat-mantle);
--color-toolkit-checkbox-onoff-off-mark-background: var(--cat-red);
--color-toolkit-checkbox-onoff-off-mark-color: var(--cat-mantle);
--color-toolkit-checkbox-label-background: var(--cat-base);
--color-toolkit-checkbox-label-border: var(--cat-mantle);
--color-toolkit-checkbox-input-border: var(--cat-mauve);
--color-toolkit-engine-tooltip-border: var(--cat-surface0);
--color-toolkit-engine-tooltip-background: var(--cat-surface0);
--color-toolkit-loader-border: rgba(255, 255, 255, 0.2);
--color-toolkit-loader-borderleft: var(--cat-crust);
--color-doc-code: var(--cat-rosewater);
--color-doc-code-background: var(--cat-mantle);
}
#search_logo svg :not([fill="none"]) {
fill: var(--cat-mauve) !important;
}
#search_logo svg :not([stroke="none"]) {
stroke: var(--cat-mauve) !important;
}
/* Additional cute tweaks */
article.result {
background-color: var(--color-result-background);
border-radius: 0.75em;
padding: 0.75em;
margin: 0.5em;
border: 1px solid var(--cat-surface0);
}
article.category-images {
padding-bottom: 4em;
}
input[type="text"] {
border-radius: 2em !important;
}
'';
mainUser = config.myModules.system.mainUser;
mainUserUid = toString config.users.users.${mainUser}.uid;
anubisPolicy = pkgs.writeText "anubis-policy.yml" ''
bots:
- name: "Allow OpenSearch"
@ -217,6 +36,10 @@ let
[favicons.cache]
db_url = "/var/cache/searxng/faviconcache.db"
LIMIT_TOTAL_BYTES = 2147483648
[favicons.proxy.resolver_map]
google = "searx.favicons.resolver.google_resolver"
duckduckgo = "searx.favicons.resolver.duckduckgo_resolver"
'';
in
{
@ -251,25 +74,32 @@ in
# 1. Create Bridge Network
systemd.services."create-searxng-network" = {
serviceConfig.Type = "oneshot";
serviceConfig.User = "ashie";
serviceConfig.User = mainUser;
serviceConfig.RemainAfterExit = true;
after = [ "user-runtime-dir@1000.service" ];
requires = [ "user-runtime-dir@1000.service" ];
after = [ "user-runtime-dir@${mainUserUid}.service" ];
requires = [ "user-runtime-dir@${mainUserUid}.service" ];
path = [
pkgs.podman
pkgs.shadow
];
script = ''
export PATH=/run/wrappers/bin:$PATH
export XDG_RUNTIME_DIR="/run/user/1000"
export HOME="/home/ashie"
podman network create searxng-net --subnet 10.89.2.0/24 --ignore
export XDG_RUNTIME_DIR="/run/user/${mainUserUid}"
export HOME="/home/${mainUser}"
if ! podman network exists searxng-net; then
echo "Creating searxng-net..."
podman network create searxng-net --subnet 10.89.2.0/24
else
echo "searxng-net already exists."
fi
'';
};
# 2. Valkey Container (Cache/Limiter)
virtualisation.oci-containers.containers."searxng-valkey" = {
image = "docker.io/valkey/valkey:alpine";
labels = { "io.containers.autoupdate" = "registry"; };
cmd = [
"valkey-server"
"--save"
@ -286,12 +116,12 @@ in
# 3. SearXNG Container
virtualisation.oci-containers.containers."searxng" = {
image = "ghcr.io/privau/searxng:latest";
# ports = [ "127.0.0.1:${toString cfg.port}:8080" ]; # Port moved to Anubis
image = "localhost/aadniz/searxng:latest";
environment = {
"SEARXNG_BASE_URL" = "https://${cfg.domain}";
"SEARXNG_REDIS_URL" = "valkey://valkey:6379"; # Talk to Valkey via alias
"SEARXNG_URL_BASE" = "https://${cfg.domain}";
"GRANIAN_HOST" = "0.0.0.0";
};
environmentFiles = [
# Contains SEARXNG_SECRET_KEY
@ -308,8 +138,9 @@ in
];
volumes = [
"${config.sops.templates."searxng_settings.yml".path}:/etc/searxng/settings.yml:ro"
"${catppuccinCss}:/etc/searxng/custom.css:ro"
"${faviconsConfig}:/etc/searxng/favicons.toml:ro"
"${./braveapi.py}:/usr/local/searxng/searx/engines/braveapi.py:ro"
"searxng-cache:/var/cache/searxng"
];
dependsOn = [ "searxng-valkey" ];
@ -318,6 +149,7 @@ in
# 4. Anubis Container (AI Firewall)
virtualisation.oci-containers.containers."searxng-anubis" = {
image = "ghcr.io/techarohq/anubis:latest";
labels = { "io.containers.autoupdate" = "registry"; };
ports = [ "127.0.0.1:${toString cfg.port}:8080" ];
environment = {
"TARGET" = "http://searxng:8080";
@ -326,6 +158,7 @@ in
};
extraOptions = [
"--network=searxng-net"
"--network-alias=searxng-anubis"
];
volumes = [
"${anubisPolicy}:/etc/anubis/policy.yml:ro"
@ -333,32 +166,24 @@ in
dependsOn = [ "searxng" ];
};
# 5. Permanent NAT Fix for SearXNG Network
networking.nftables.tables.searxng-nat = {
family = "inet";
content = ''
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.89.2.0/24 masquerade
}
'';
};
sops.templates."searxng.env" = {
owner = "ashie";
owner = mainUser;
content = ''
SEARXNG_SECRET_KEY=${config.sops.placeholder.searxng_secret_key}
BRAVE_API_KEY=${config.sops.placeholder.searxng_brave_api_key}
'';
};
sops.templates."searxng_settings.yml" = {
owner = "ashie";
owner = mainUser;
content = ''
use_default_settings: true
general:
debug: false
instance_name: "Ashie Search"
contact_url: false
issue_url: false
donation_url: ${if cfg.donations ? "Monero" then "\"${cfg.donations.Monero}\"" else "false"}
donations:
${lib.concatStringsSep "\n " (
@ -366,15 +191,20 @@ in
)}
engines:
- name: brave
engine: brave
api_key: "${config.sops.placeholder.searxng_brave_api_key}"
- name: braveapi
engine: braveapi
shortcut: brapi
categories: general
# api_key: set via BRAVE_API_KEY env var
tokens: ["${config.sops.placeholder.searxng_private_token}"]
timeout: 2.0
weight: 2
disabled: false
search:
safe_search: 0
favicon_resolver: "duckduckgo"
favicon_resolver: "google"
autocomplete: "google"
default_lang: "en-US"
formats:
@ -387,17 +217,15 @@ in
secret_key: "${config.sops.placeholder.searxng_secret_key}"
limiter: true
image_proxy: true
public_instance: true
default_http_headers:
Content-Security-Policy: "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'sha256-/ldGxQqxNIMRftg3AGsPF+F281wiBPECUDcL2RJkxdU='; form-action 'self' https://github.com/searxng/searxng/issues/new; font-src 'self'; frame-ancestors 'self'; img-src 'self' data:; connect-src 'self' https://overpass-api.de; manifest-src 'self'"
ui:
default_theme: simple
default_theme_style: kagi
default_theme: red-floof
default_theme_style: dark
static_use_hash: true
# custom_css: custom.css
# theme_args:
# simple_style: kagi
hostname_replace:
'(^|.*\.)reddit\.com$': 'reddit.ashisgreat.xyz'
redis:
url: valkey://valkey:6379/0
@ -410,58 +238,59 @@ in
sops.secrets.searxng_private_token = { };
# Rootless Overrides
systemd.services."podman-searxng".serviceConfig.User = lib.mkForce "ashie";
systemd.services."podman-searxng".serviceConfig.User = lib.mkForce mainUser;
systemd.services."podman-searxng".environment = {
HOME = "/home/ashie";
XDG_RUNTIME_DIR = "/run/user/1000";
HOME = "/home/${mainUser}";
XDG_RUNTIME_DIR = "/run/user/${mainUserUid}";
};
systemd.services."podman-searxng".serviceConfig.Type = lib.mkForce "simple";
systemd.services."podman-searxng".serviceConfig.Delegate = true;
systemd.services."podman-searxng".after = [
"create-searxng-network.service"
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"network-online.target"
];
systemd.services."podman-searxng".requires = [
"create-searxng-network.service"
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"network-online.target"
];
systemd.services."podman-searxng-valkey".serviceConfig.User = lib.mkForce "ashie";
systemd.services."podman-searxng-valkey".serviceConfig.User = lib.mkForce mainUser;
systemd.services."podman-searxng-valkey".environment = {
HOME = "/home/ashie";
XDG_RUNTIME_DIR = "/run/user/1000";
HOME = "/home/${mainUser}";
XDG_RUNTIME_DIR = "/run/user/${mainUserUid}";
};
systemd.services."podman-searxng-valkey".serviceConfig.Type = lib.mkForce "simple";
systemd.services."podman-searxng-valkey".serviceConfig.Delegate = true;
systemd.services."podman-searxng-valkey".after = [
"create-searxng-network.service"
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"network-online.target"
];
systemd.services."podman-searxng-valkey".requires = [
"create-searxng-network.service"
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"network-online.target"
];
systemd.services."podman-searxng-anubis".serviceConfig.User = lib.mkForce "ashie";
systemd.services."podman-searxng-anubis".serviceConfig.User = lib.mkForce mainUser;
systemd.services."podman-searxng-anubis".environment = {
HOME = "/home/ashie";
XDG_RUNTIME_DIR = "/run/user/1000";
HOME = "/home/${mainUser}";
XDG_RUNTIME_DIR = "/run/user/${mainUserUid}";
};
systemd.services."podman-searxng-anubis".serviceConfig.Type = lib.mkForce "simple";
systemd.services."podman-searxng-anubis".serviceConfig.Delegate = true;
systemd.services."podman-searxng-anubis".after = [
"create-searxng-network.service"
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"network-online.target"
];
systemd.services."podman-searxng-anubis".requires = [
"create-searxng-network.service"
"user-runtime-dir@1000.service"
"user-runtime-dir@${mainUserUid}.service"
"network-online.target"
];
};
}

View file

@ -8,111 +8,94 @@
}:
let
cfg = config.myModules.spotifySandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
in
{
nixpkgs.overlays = [
(final: prev: {
spotify-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.spotify;
id = "com.spotify.Client";
env = {
# Propagate XDG_DATA_DIRS for theming
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Force Wayland if preferred, or rely on auto-detection
# DISPLAY variable is handled by sockets.x11/wayland
options.myModules.spotifySandboxed = {
enable = lib.mkEnableOption "sandboxed Spotify with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
spotify-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.spotify;
id = "com.spotify.Client";
env = {
# Propagate XDG_DATA_DIRS for theming
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
};
};
};
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Spotify is not a flatpak ref, so disable flatpak emulation
flatpak.enable = false;
# Spotify is not a flatpak ref, so disable flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for streaming
unshareIpc = false;
};
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for streaming
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
# Audio
"--ro-bind-try /etc/asound.conf /etc/asound.conf"
];
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { } ++ [
# Audio
"--ro-bind-try /etc/asound.conf /etc/asound.conf"
]);
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
];
readWrite = [
"$HOME/.config/spotify"
"$HOME/.cache/spotify"
"$HOME/.local/share/spotify"
mounts = {
read = sandboxUtils.mkGuiMounts.read;
readWrite = [
"$HOME/.config/spotify"
"$HOME/.cache/spotify"
"$HOME/.local/share/spotify"
] ++ cfg.extraBindMounts;
};
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "com.spotify.Client";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.mpris.MediaPlayer2.Player"''
''--own="org.mpris.MediaPlayer2.spotify"''
''--own="com.spotify.Client"''
''--own="com.spotify.Client.*"''
];
};
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/com.spotify.Client/bus" "$XDG_RUNTIME_DIR/bus"''
];
};
})
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "com.spotify.Client";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.mpris.MediaPlayer2.Player"''
''--own="org.mpris.MediaPlayer2.spotify"''
''--own="com.spotify.Client"''
''--own="com.spotify.Client.*"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/com.spotify.Client/bus" "$XDG_RUNTIME_DIR/bus"''
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
};
})
];
environment.systemPackages = [ pkgs.spotify-sandboxed ];
};
}

View file

@ -1,50 +1,63 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.myModules.gaming.gamemode;
in
{
programs.gamescope = {
enable = true;
capSysNice = true;
options.myModules.gaming.gamemode = {
enable = lib.mkEnableOption "Steam GameMode Session";
};
services.displayManager.sessionPackages = [
(pkgs.writeTextFile {
name = "steam-gamemode-session";
destination = "/share/wayland-sessions/steam-gamemode.desktop";
text = ''
[Desktop Entry]
Name=Steam GameMode
Comment=Launch Steam in GameMode with Gamescope
Exec=${pkgs.writeShellScript "steam-gamemode-start" ''
# Load system environment
. /etc/profile
config = lib.mkIf cfg.enable {
programs.gamemode.enable = true;
# Ensure we are in the user's home directory
cd "$HOME" || exit 1
programs.gamescope = {
enable = true;
capSysNice = true;
};
exec >/tmp/steam-gamemode.log 2>&1
echo "Starting Steam GameMode Session at $(date)"
echo "User: $(whoami)"
echo "PATH: $PATH"
echo "Gamescope path: ${pkgs.gamescope}/bin/gamescope"
services.displayManager.sessionPackages = [
(pkgs.writeTextFile {
name = "steam-gamemode-session";
destination = "/share/wayland-sessions/steam-gamemode.desktop";
text = ''
[Desktop Entry]
Name=Steam GameMode
Comment=Launch Steam in GameMode with Gamescope
Exec=${pkgs.writeShellScript "steam-gamemode-start" ''
# Load system environment
. /etc/profile
# Check for steam binary
if ! command -v steam >/dev/null; then
echo "ERROR: steam command not found in PATH"
exit 1
fi
# Ensure we are in the user's home directory
cd "$HOME" || exit 1
echo "Launching gamescope..."
exec ${pkgs.gamescope}/bin/gamescope -f -e -- steam -gamepadui
''}
Type=Application
'';
derivationArgs = {
passthru = {
providedSessions = [ "steam-gamemode" ];
exec >/tmp/steam-gamemode.log 2>&1
echo "Starting Steam GameMode Session at $(date)"
echo "User: $(whoami)"
echo "PATH: $PATH"
echo "Gamescope path: ${pkgs.gamescope}/bin/gamescope"
# Check for steam binary
if ! command -v steam >/dev/null; then
echo "ERROR: steam command not found in PATH"
exit 1
fi
echo "Launching gamescope..."
exec ${pkgs.gamescope}/bin/gamescope -f -e -- steam -gamepadui
''}
Type=Application
'';
derivationArgs = {
passthru = {
providedSessions = [ "steam-gamemode" ];
};
};
};
})
];
}
})
];
};
}

View file

@ -9,144 +9,81 @@
}:
let
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
in
{
nixpkgs.overlays = [
(final: prev: {
steam-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = prev.steam;
isFhsenv = true; # Steam uses buildFHSEnv
id = "com.valvesoftware.Steam";
env = {
# Unset LD_PRELOAD to avoid mimalloc crashes
LD_PRELOAD = "";
# Propagate XDG_DATA_DIRS for theming
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Proton/Wine optimizations
PROTON_USE_NTSYNC = 1;
XDG_CURRENT_DESKTOP = "niri";
XDG_SESSION_TYPE = "wayland";
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
};
};
sandboxUtils.mkSandboxedApp {
inherit
config
lib
pkgs
inputs
;
optionName = "steamSandboxed";
packageName = "steam-sandboxed";
description = "sandboxed Steam with nix-bwrapper";
package = pkgs.steam;
appId = "com.valvesoftware.Steam";
isFhsenv = true;
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
PROTON_USE_NTSYNC = 1;
XDG_CURRENT_DESKTOP = "niri";
XDG_SESSION_TYPE = "wayland";
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
};
# Disable Flatpak emulation
flatpak.enable = false;
fhsenvExtra = {
skipExtraInstallCmds = true;
};
fhsenv = {
skipExtraInstallCmds = true; # Steam package is special, don't try to modify it
};
fhsenvOpts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false;
unshareIpc = false;
};
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false; # Share PIDs for compatibility
unshareNet = false; # Need network for Steam login/downloads
unshareIpc = false;
};
baseArgs =
sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { };
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--dev-bind /dev/shm /dev/shm" # Shared memory (required for games)
"--dev-bind-try /dev/uinput /dev/uinput" # Controller support
"--dev-bind-try /dev/input /dev/input" # Controller/input devices
"--dev-bind-try /dev/hidraw0 /dev/hidraw0" # HID devices (controllers)
"--dev-bind-try /dev/hidraw1 /dev/hidraw1"
"--dev-bind-try /dev/hidraw2 /dev/hidraw2"
"--dev-bind-try /dev/hidraw3 /dev/hidraw3"
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
"--unsetenv LD_PRELOAD"
# udev for controller hotplug
"--ro-bind-try /run/udev /run/udev"
];
mounts = {
read = [ ];
readWrite = [
"$HOME/.steam"
"$HOME/.local/share/Steam"
"$HOME/.local/share/umu"
"$HOME/.local/share/applications"
"$HOME/.local/share/desktop-directories"
"$HOME/.local/share/icons"
"$HOME/.local/share/Larian Studios"
"$HOME/Desktop"
"/games/steam"
"/games/windows/Modlist"
"/games/windows/Modlist_Downloads"
];
};
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/qt6ct"
"$HOME/.config/Kvantum"
"$HOME/.config/MangoHud"
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
];
readWrite = [
"$HOME/.steam"
"$HOME/.local/share/Steam"
"$HOME/.local/share/umu"
"$HOME/.local/share/applications"
"$HOME/.local/share/desktop-directories"
"$HOME/.local/share/icons"
"$HOME/.local/share/Larian Studios"
"$HOME/Desktop"
"/games/steam"
"/games/windows/Modlist"
"/games/windows/Modlist_Downloads"
"$HOME/Games/windows/Modlist/Nordic"
];
};
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "com.valvesoftware.Steam";
enableSystemBus = false; # No system bus access
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--talk="org.freedesktop.portal.*"''
''--own="com.valvesoftware.Steam"''
''--own="com.valvesoftware.Steam.*"''
''--own="com.steampowered.PressureVessel.*"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/com.valvesoftware.Steam/bus" "$XDG_RUNTIME_DIR/bus"''
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
};
})
];
dbusProxy = {
enable = true;
enableSystemBus = false;
rules = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.kde.KWin"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="org.freedesktop.secrets"''
''--talk="com.feralinteractive.GameMode"''
''--talk="org.freedesktop.portal.*"''
''--own="com.valvesoftware.Steam"''
''--own="com.valvesoftware.Steam.*"''
''--own="com.steampowered.PressureVessel.*"''
];
};
}

View file

@ -3,11 +3,12 @@
lib,
pkgs,
inputs,
...
}:
...}:
let
cfg = config.myModules.tutanotaSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
pname = "tutanota-desktop";
version = "319.260107.1";
@ -34,86 +35,73 @@ let
};
in
{
nixpkgs.overlays = [
(final: prev: {
tutanota-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = tutanota;
id = "com.tutanota.Tutanota";
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
options.myModules.tutanotaSandboxed = {
enable = lib.mkEnableOption "sandboxed Tutanota Desktop with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
tutanota-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = tutanota;
id = "com.tutanota.Tutanota";
env = {
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
};
};
};
flatpak.enable = false;
flatpak.enable = false;
# Basic sandboxing
fhsenv.opts = {
unshareUser = true;
unshareUts = true;
unshareCgroup = true;
unsharePid = true;
unshareNet = false; # Needs network
unshareIpc = true;
};
# Basic sandboxing
fhsenv.opts = {
unshareUser = true;
unshareUts = true;
unshareCgroup = true;
unsharePid = true;
unshareNet = false; # Needs network
unshareIpc = true;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
];
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { });
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
];
readWrite = [
"$HOME/.config/tutanota-desktop"
"$HOME/Downloads"
mounts = {
read = sandboxUtils.mkGuiMounts.read;
readWrite = [
"$HOME/.config/tutanota-desktop"
"$HOME/Downloads"
] ++ cfg.extraBindMounts;
};
dbus.enable = false;
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "com.tutanota.Tutanota";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.secrets"''
''--talk="org.gnome.keyring.SystemPrompter"'' # Often needed for secrets
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="com.tutanota.Tutanota"''
];
};
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/com.tutanota.Tutanota/bus" "$XDG_RUNTIME_DIR/bus"''
];
};
})
];
dbus.enable = false;
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "com.tutanota.Tutanota";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.secrets"''
''--talk="org.gnome.keyring.SystemPrompter"'' # Often needed for secrets
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--own="com.tutanota.Tutanota"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/com.tutanota.Tutanota/bus" "$XDG_RUNTIME_DIR/bus"''
# Wayland
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
];
};
})
];
environment.systemPackages = [ pkgs.tutanota-sandboxed ];
};
}

View file

@ -8,7 +8,9 @@
}:
let
cfg = config.myModules.vesktopSandboxed;
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
sandboxUtils = import ./sandbox-utils.nix { inherit pkgs lib; };
# Define specific Vesktop version to avoid build errors from source
vesktop-bin = pkgs.stdenv.mkDerivation rec {
@ -40,106 +42,86 @@ let
};
in
{
nixpkgs.overlays = [
(final: prev: {
vesktop-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = vesktop-bin;
id = "dev.vencord.Vesktop";
env = {
# Propagate XDG_DATA_DIRS for theming
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Force Wayland
NIXOS_OZONE_WL = "1";
options.myModules.vesktopSandboxed = {
enable = lib.mkEnableOption "sandboxed Vesktop with nix-bwrapper";
extraBindMounts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Extra paths to bind mount (read-write) into the sandbox";
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(final: prev: {
vesktop-sandboxed = bwrapperPkgs.mkBwrapper {
app = {
package = vesktop-bin;
id = "dev.vencord.Vesktop";
env = {
# Propagate XDG_DATA_DIRS for theming
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
# Force Wayland
NIXOS_OZONE_WL = "1";
};
};
};
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Enable X11 and Wayland
sockets.x11 = true;
sockets.wayland = true;
# Disable flatpak emulation
flatpak.enable = false;
# Disable flatpak emulation
flatpak.enable = false;
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for Discord
unshareIpc = false;
};
fhsenv.opts = {
unshareUser = true;
unshareUts = false;
unshareCgroup = false;
unsharePid = false;
unshareNet = false; # Need network for Discord
unshareIpc = false;
};
fhsenv.bwrap.baseArgs = lib.mkForce [
"--new-session"
"--proc /proc"
"--dev /dev"
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
"--tmpfs /home"
"--tmpfs /tmp"
"--tmpfs /run"
"--dir /run/user"
"--dir /run/user/${toString config.users.users.ashie.uid}"
# System paths
"--ro-bind /sys /sys"
"--ro-bind-try /run/current-system /run/current-system"
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
"--dir /run/systemd/resolve"
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
# Audio
"--ro-bind-try /etc/asound.conf /etc/asound.conf"
];
fhsenv.bwrap.baseArgs = lib.mkForce (sandboxUtils.mkCommonBindArgs { inherit config lib; } ++ sandboxUtils.mkGamingBindArgs { });
mounts = {
read = [
"$HOME/.config/fontconfig"
"$HOME/.local/share/fonts"
"$HOME/.icons"
"$HOME/.themes"
"$HOME/.local/share/themes"
"$HOME/.config/kdedefaults"
"$HOME/.local/share/color-schemes"
];
readWrite = [
"$HOME/.config/vesktop"
"$HOME/Downloads"
mounts = {
read = sandboxUtils.mkGuiMounts.read;
readWrite = [
"$HOME/.config/vesktop"
"$HOME/Downloads"
] ++ cfg.extraBindMounts;
};
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = sandboxUtils.mkDbusProxyScript {
appId = "dev.vencord.Vesktop";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="com.canonical.AppMenu.Registrar"''
''--own="dev.vencord.Vesktop"''
''--own="dev.vencord.Vesktop.*"''
];
};
fhsenv.bwrap.additionalArgs = sandboxUtils.mkGuiBindArgs { } ++ [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/dev.vencord.Vesktop/bus" "$XDG_RUNTIME_DIR/bus"''
];
};
})
];
# Disable built-in DBus module (invokes bwrap without --unshare-user)
dbus.enable = false;
# Manually set up DBus proxy with --unshare-user (session bus only)
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
appId = "dev.vencord.Vesktop";
enableSystemBus = false;
proxyArgs = [
"--filter"
''--talk="org.freedesktop.portal.*"''
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
''--talk="org.freedesktop.Notifications"''
''--talk="org.freedesktop.ScreenSaver"''
''--talk="org.kde.StatusNotifierWatcher"''
''--talk="org.gnome.Mutter.DisplayConfig"''
''--talk="com.canonical.AppMenu.Registrar"''
''--own="dev.vencord.Vesktop"''
''--own="dev.vencord.Vesktop.*"''
];
};
fhsenv.bwrap.additionalArgs = [
# D-Bus session proxy only
''--bind "$XDG_RUNTIME_DIR/app/dev.vencord.Vesktop/bus" "$XDG_RUNTIME_DIR/bus"''
# Wayland socket
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
# PipeWire + Pulse
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
];
};
})
];
environment.systemPackages = [ pkgs.vesktop-sandboxed ];
};
}