Commit 589522a

HPCesia <me@hpcesia.com>
2026-10-08 03:02:50
desktop: migrate flatpak to nixpak
deprecate dingtalk since it has an EOL dependency OpenSSL 1.1 Assisted-by: crush:deepseek-v4.1-flash
1 parent aa7aa11
Changed files (11)
modules/desktop/aesthetic/fonts/default.nix
@@ -140,5 +140,12 @@
         };
       };
     };
+
+    hjem = {
+      xdg.data.files."fonts" = {
+        type = "symlink";
+        source = "/run/current-system/sw/share/X11/fonts";
+      };
+    };
   };
 }
modules/desktop/app/nixpak/_modules/common.nix
@@ -0,0 +1,163 @@
+# https://github.com/zno233/nixos-config/blob/319a4813e09af421beefa21146516fc1fb338e3f/pkgs/nixpaks/modules/common.nix
+{
+  pkgs,
+  lib,
+  config,
+  sloth,
+  ...
+}: let
+  inherit (config.flatpak) appId;
+in {
+  config = {
+    dbus = {
+      policies =
+        {
+          # `see`: The bus name can be enumerated by the application.
+          # `talk`: The application can send messages to, and receive replies and signals from, the bus name.
+          # `own`: The application can own the bus name
+
+          # === General ===
+          "${appId}" = "own";
+          "${appId}.*" = "own";
+          "org.freedesktop.DBus" = "talk";
+          "ca.desrt.dconf" = "talk";
+          "org.freedesktop.appearance" = "talk";
+          "org.freedesktop.appearance.*" = "talk";
+          "org.freedesktop.FileManager1" = "talk";
+          "org.freedesktop.Notifications" = "talk";
+          "com.canonical.AppMenu.Registrar" = "talk";
+          "org.gnome.Shell.Screencast" = "talk";
+
+          # === Portal ===
+          "org.freedesktop.portal.Documents" = "talk";
+          "org.freedesktop.portal.FileTransfer" = "talk";
+          "org.freedesktop.portal.FileTransfer.*" = "talk";
+          "org.freedesktop.portal.Notification" = "talk";
+          "org.freedesktop.portal.OpenURI" = "talk";
+          "org.freedesktop.portal.OpenURI.OpenFile" = "talk";
+          "org.freedesktop.portal.OpenURI.OpenURI" = "talk";
+          "org.freedesktop.portal.Print" = "talk";
+          "org.freedesktop.portal.Request" = "see";
+
+          # === MPRIS ===
+          "org.mpris.MediaPlayer2.${appId}" = "own";
+          "org.mpris.MediaPlayer2.${appId}.*" = "own";
+          "org.mpris.MediaPlayer2.${lib.lists.last (lib.strings.splitString "." appId)}" = "own";
+          "org.mpris.MediaPlayer2.${lib.lists.last (lib.strings.splitString "." appId)}.*" = "own";
+        }
+        # Tray icons register under their own small sandbox pid, so a low pid range covers them.
+        // (builtins.listToAttrs (
+          map (id: lib.nameValuePair "org.kde.StatusNotifierItem-${toString id}-1" "own") (
+            lib.lists.range 2 29
+          )
+        ))
+        // {
+          "org.kde.StatusNotifierWatcher" = "talk";
+        };
+
+      # 'call' rules permit specific method calls on D-Bus interfaces.
+      rules.call = {
+        "org.freedesktop.FileManager1" = ["*"];
+        "org.freedesktop.Notifications.*" = ["*"];
+        "org.freedesktop.portal.Documents" = ["*"];
+        "org.freedesktop.portal.FileTransfer" = ["*"];
+        "org.freedesktop.portal.FileTransfer.*" = ["*"];
+        "org.freedesktop.portal.Notification" = ["*"];
+        "org.freedesktop.portal.OpenURI" = ["*"];
+        "org.freedesktop.portal.OpenURI.OpenFile" = ["*"];
+        "org.freedesktop.portal.OpenURI.OpenURI" = ["*"];
+        "org.freedesktop.portal.Print" = ["*"];
+        "org.freedesktop.portal.Request" = ["*"];
+
+        "org.freedesktop.portal.Desktop" = [
+          "org.freedesktop.DBus.Properties.GetAll"
+          "org.freedesktop.DBus.Properties.Get@/org/freedesktop/portal/desktop"
+          "org.freedesktop.portal.Session.Close"
+          "org.freedesktop.portal.Settings.ReadAll"
+          "org.freedesktop.portal.Settings.Read"
+          "org.freedesktop.portal.Account.GetUserInformation"
+
+          "org.freedesktop.portal.NetworkMonitor"
+          "org.freedesktop.portal.NetworkMonitor.*"
+          "org.freedesktop.portal.ProxyResolver.Lookup"
+          "org.freedesktop.portal.ProxyResolver.Lookup.*"
+
+          "org.freedesktop.portal.ScreenCast"
+          "org.freedesktop.portal.ScreenCast.*"
+          "org.freedesktop.portal.Screenshot"
+          "org.freedesktop.portal.Screenshot.Screenshot"
+
+          "org.freedesktop.portal.Camera"
+          "org.freedesktop.portal.Camera.*"
+          "org.freedesktop.portal.Usb"
+          "org.freedesktop.portal.Usb.*"
+
+          "org.freedesktop.portal.RemoteDesktop"
+          "org.freedesktop.portal.RemoteDesktop.*"
+
+          "org.freedesktop.portal.Documents"
+          "org.freedesktop.portal.Documents.*"
+          "org.freedesktop.portal.FileChooser"
+          "org.freedesktop.portal.FileChooser.*"
+          "org.freedesktop.portal.FileTransfer"
+          "org.freedesktop.portal.FileTransfer.*"
+
+          "org.freedesktop.portal.Notification"
+          "org.freedesktop.portal.Notification.*"
+
+          "org.freedesktop.portal.Print"
+          "org.freedesktop.portal.Print.*"
+
+          "org.freedesktop.portal.OpenURI"
+          "org.freedesktop.portal.OpenURI.*"
+          "org.freedesktop.portal.Email.ComposeEmail"
+
+          "org.freedesktop.portal.Secret"
+          "org.freedesktop.portal.Secret.RetrieveSecret"
+
+          "org.freedesktop.portal.Inhibit"
+          "org.freedesktop.portal.Inhibit.*"
+
+          "org.freedesktop.portal.Request"
+        ];
+      };
+
+      # 'broadcast' rules permit receiving signals from D-Bus names.
+      rules.broadcast = {
+        "org.freedesktop.portal.*" = ["@/org/freedesktop/portal/*"];
+      };
+
+      args = [
+        "--filter"
+        "--sloppy-names"
+        "--log"
+      ];
+    };
+
+    locale.enable = lib.mkDefault true;
+
+    bubblewrap = {
+      network = lib.mkDefault false;
+
+      bind.rw = [
+        [(sloth.mkdir sloth.appDataDir) sloth.xdgDataHome]
+        [(sloth.mkdir sloth.appConfigDir) sloth.xdgConfigHome]
+        [(sloth.mkdir sloth.appCacheDir) sloth.xdgCacheHome]
+
+        (sloth.concat' sloth.runtimeDir "/doc") # For file chooser
+
+        (sloth.concat' sloth.runtimeDir "/dconf")
+      ];
+      bind.ro = [
+        (sloth.concat' sloth.xdgConfigHome "/dconf")
+        [(lib.getExe pkgs.bashInteractive) "/bin/sh"]
+
+        # /etc/localtime is a symlink into /etc/zoneinfo on NixOS
+        "/etc/localtime"
+        "/etc/zoneinfo"
+      ];
+      bind.dev = ["/dev/shm"];
+      tmpfs = ["/tmp"];
+    };
+  };
+}
modules/desktop/app/nixpak/_modules/gui.nix
@@ -0,0 +1,93 @@
+{
+  pkgs,
+  lib,
+  sloth,
+  ...
+}: {
+  imports = [./common.nix];
+
+  config = {
+    dbus = {
+      policies = {
+        "org.gtk.vfs" = "talk";
+        "org.gtk.vfs.*" = "talk";
+        "org.a11y.Bus" = "see";
+        # Input methods talk to the daemon directly, fcitx5 exposes the fcitx4
+        # compat name as org.fcitx.Fcitx-<n>, hence the prefix wildcard.
+        "org.fcitx.*" = "talk";
+        "org.freedesktop.portal.Fcitx" = "talk";
+        "org.freedesktop.portal.Fcitx.*" = "talk";
+        "org.freedesktop.portal.IBus" = "talk";
+        "org.freedesktop.portal.IBus.*" = "talk";
+      };
+      rules.call = {
+        "org.a11y.Bus" = [
+          "org.a11y.Bus.GetAddress@/org/a11y/bus"
+          "org.freedesktop.DBus.Properties.Get@/org/a11y/bus"
+        ];
+        "org.freedesktop.portal.Fcitx" = ["*"];
+        "org.freedesktop.portal.Fcitx.*" = ["*"];
+        "org.freedesktop.portal.IBus" = ["*"];
+        "org.freedesktop.portal.IBus.*" = ["*"];
+      };
+    };
+
+    # `nixos` provider reuses the host's /run/opengl-driver
+    gpu = {
+      enable = lib.mkDefault true;
+      provider = "nixos";
+    };
+
+    # nixpak's bundled fontconfig has no CJK font
+    fonts.enable = lib.mkDefault false;
+
+    bubblewrap = {
+      sockets = {
+        wayland = lib.mkDefault true;
+        pulse = true;
+      };
+
+      bind.rw = [
+        (sloth.concat' sloth.xdgCacheHome "/fontconfig")
+        (sloth.concat' sloth.xdgCacheHome "/mesa_shader_cache")
+        (sloth.concat' sloth.xdgCacheHome "/mesa_shader_cache_db")
+        (sloth.concat' sloth.xdgCacheHome "/radv_builtin_shaders")
+
+        (sloth.concat' sloth.runtimeDir "/at-spi/bus")
+        (sloth.concat' sloth.runtimeDir "/gvfsd")
+      ];
+      bind.ro = [
+        "/etc/fonts"
+        # Without /etc/egl, libEGL warns: failed to create dri2 screen
+        "/etc/egl"
+        # Cursor and icon themes come from the host, so the session's
+        # XCURSOR_PATH resolves instead of the app keeping its own copy.
+        (sloth.concat' sloth.xdgDataHome "/icons")
+        (sloth.concat' sloth.homeDir "/.icons")
+
+        (sloth.concat' sloth.xdgConfigHome "/fontconfig")
+        (sloth.concat' sloth.xdgConfigHome "/gtk-2.0")
+        (sloth.concat' sloth.xdgConfigHome "/gtk-3.0")
+        (sloth.concat' sloth.xdgConfigHome "/gtk-4.0")
+        (sloth.concat' sloth.xdgConfigHome "/kdeglobals")
+      ];
+      bind.dev =
+        (map (id: "/dev/video${toString id}") (lib.lists.range 0 9))
+        ++ [
+          "/dev/nvidia0"
+          "/dev/nvidiactl"
+          "/dev/nvidia-modeset"
+          "/dev/nvidia-uvm"
+        ]
+        # nixpak mounts the X11 sockets before its own tmpfs /tmp, which would
+        # hide them, and bind.dev is the only slot applied after that tmpfs.
+        ++ ["/tmp/.X11-unix"];
+
+      env = {
+        XDG_DATA_DIRS = lib.makeSearchPath "share" [
+          pkgs.shared-mime-info
+        ];
+      };
+    };
+  };
+}
modules/desktop/app/nixpak/_modules/network.nix
@@ -0,0 +1,5 @@
+{
+  etc.sslCertificates.enable = true;
+  # nixpak already binds /etc/resolv.conf and /etc/hosts for networked sandboxes.
+  bubblewrap.network = true;
+}
modules/desktop/app/nixpak/default.nix
@@ -0,0 +1,26 @@
+{inputs, ...}: {
+  flake-file.inputs.nixpak = {
+    url = "github:nixpak/nixpak";
+    inputs.nixpkgs.follows = "nixpkgs";
+    inputs.flake-parts.follows = "flake-parts";
+  };
+
+  den.aspects.desktop.app.nixpak = {
+    nixos = {
+      nixpkgs.overlays = [
+        (_final: prev: {
+          mkNixpak = inputs.nixpak.lib.nixpak {
+            inherit (prev) lib;
+            pkgs = prev;
+          };
+
+          nixpakModules = {
+            common = ./_modules/common.nix;
+            gui = ./_modules/gui.nix;
+            network = ./_modules/network.nix;
+          };
+        })
+      ];
+    };
+  };
+}
modules/desktop/app/nixpak/qq.nix
@@ -0,0 +1,49 @@
+{den, ...}: {
+  den.aspects.desktop.app.nixpak.qq = let
+    appId = "com.qq.QQ";
+  in {
+    includes = [den.aspects.desktop.app.nixpak];
+
+    persistHome = {
+      directories = [
+        ".var/app/${appId}"
+      ];
+    };
+
+    hjem = {
+      pkgs,
+      lib,
+      ...
+    }: let
+      qq-wrapped = pkgs.mkNixpak {
+        config = {sloth, ...}: {
+          imports = [
+            pkgs.nixpakModules.common
+            pkgs.nixpakModules.gui
+            pkgs.nixpakModules.network
+          ];
+
+          app.package = pkgs.qq;
+          app.binPath = "bin/qq";
+          flatpak = {inherit appId;};
+
+          dbus.policies."org.freedesktop.ScreenSaver" = "talk";
+
+          bubblewrap = {
+            sockets.pipewire = true;
+
+            bind.rw = [sloth.xdgDownloadDir];
+
+            env = {
+              # xdg-open has to come from PATH.
+              PATH = lib.makeBinPath [pkgs.flatpak-xdg-utils];
+              NIXOS_OZONE_WL = "1";
+            };
+          };
+        };
+      };
+    in {
+      packages = [qq-wrapped.config.env];
+    };
+  };
+}
modules/desktop/app/nixpak/wechat.nix
@@ -0,0 +1,60 @@
+{den, ...}: {
+  den.aspects.desktop.app.nixpak.wechat = let
+    appId = "com.tencent.WeChat";
+  in {
+    includes = [den.aspects.desktop.app.nixpak];
+
+    persistHome = {
+      directories = [
+        ".var/app/${appId}"
+      ];
+    };
+
+    hjem = {
+      pkgs,
+      lib,
+      ...
+    }: let
+      wechat-wrapped = pkgs.mkNixpak {
+        config = {sloth, ...}: {
+          imports = [
+            pkgs.nixpakModules.common
+            pkgs.nixpakModules.gui
+            pkgs.nixpakModules.network
+          ];
+
+          app.package = pkgs.wechat;
+          flatpak = {inherit appId;};
+
+          bubblewrap = {
+            # 4.1.1.8 runs on X11.
+            # TODO: Use Wayland for 4.1.1.13 or later
+            sockets = {
+              wayland = false;
+              x11 = true;
+            };
+
+            bind.rw = with sloth; [
+              [(mkdir (concat' appDir "/.xwechat")) (concat' homeDir "/.xwechat")]
+              [(mkdir (concat' appDir "/xwechat_files")) (concat' homeDir "/xwechat_files")]
+            ];
+            bind.ro = [
+              "/etc/passwd"
+              "/etc/machine-id"
+            ];
+
+            env = {
+              # xdg-open has to come from PATH.
+              PATH = lib.makeBinPath [pkgs.flatpak-xdg-utils];
+              QT_QPA_PLATFORM = "xcb";
+              QT_IM_MODULE = "fcitx";
+              GTK_IM_MODULE = "fcitx"; # For web view
+            };
+          };
+        };
+      };
+    in {
+      packages = [wechat-wrapped.config.env];
+    };
+  };
+}
modules/desktop/flatpak.nix → modules/desktop/_flatpak.nix
@@ -85,11 +85,6 @@
           (user.system.settings.desktop.flatpak.overrides)
         ];
       };
-
-      xdg.data.files."fonts" = {
-        type = "symlink";
-        source = "/run/current-system/sw/share/X11/fonts";
-      };
     };
   };
 }
modules/users/hpcesia/desktop.nix
@@ -6,22 +6,6 @@
           browser.defaults = ["floorp.desktop"];
           term.default = "foot";
         };
-        flatpak = {
-          packages = [
-            "com.qq.QQ"
-            "com.tencent.WeChat"
-            "com.dingtalk.DingTalk"
-          ];
-          overrides = {
-            "com.tencent.WeChat" = {
-              Environment = {
-                LANG = "zh_CN.UTF-8"; # Fix wechat CJK font variant
-                GTK_IM_MODULE = "fcitx";
-                QT_IM_MODULE = "fcitx";
-              };
-            };
-          };
-        };
       };
     };
   };
@@ -40,12 +24,13 @@
         desktop.app.kelivo
         desktop.app.libreoffice
         desktop.app.mpv
+        desktop.app.nixpak.qq
+        desktop.app.nixpak.wechat
         desktop.app.nocturne
         desktop.app.obs-studio
         desktop.app.picard
         desktop.app.term.foot
         desktop.app.thunderbird
-        desktop.flatpak
         desktop.ime.fcitx5
         desktop.noctalia
         desktop.yubikey
flake.lock
@@ -183,6 +183,31 @@
         "url": "https://codeberg.org/maxschipper/helix-plugins-nix/archive/main.tar.gz"
       }
     },
+    "hercules-ci-effects": {
+      "inputs": {
+        "flake-parts": [
+          "nixpak",
+          "flake-parts"
+        ],
+        "nixpkgs": [
+          "nixpak",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1789448436,
+        "narHash": "sha256-fknJsE4XnFjUfo87T9jyZ15ivJZB/varWvsJMYk0BNY=",
+        "owner": "hercules-ci",
+        "repo": "hercules-ci-effects",
+        "rev": "6c58de7236d1cd634deea07bd15d52c7ce470bd3",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "hercules-ci-effects",
+        "type": "github"
+      }
+    },
     "hjem": {
       "inputs": {
         "nixpkgs": [
@@ -238,18 +263,27 @@
         "type": "github"
       }
     },
-    "nix-flatpak": {
+    "nixpak": {
+      "inputs": {
+        "flake-parts": [
+          "flake-parts"
+        ],
+        "hercules-ci-effects": "hercules-ci-effects",
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
       "locked": {
-        "lastModified": 1783368811,
-        "narHash": "sha256-0H8jDwR4Kegb3heaTrH1ftbgKfZVDT8JE+46uXxDy/Q=",
-        "owner": "gmodena",
-        "repo": "nix-flatpak",
-        "rev": "20d42f0ee98c9fe9f85e8d1de474f1409ed10d05",
+        "lastModified": 1791426201,
+        "narHash": "sha256-m7eWQTRuwNQeAXxliSO1lxuQje/1l58tzCFJwaDlUAc=",
+        "owner": "nixpak",
+        "repo": "nixpak",
+        "rev": "dd037bd073edc9f8ce2186c6c1975f47db245bed",
         "type": "github"
       },
       "original": {
-        "owner": "gmodena",
-        "repo": "nix-flatpak",
+        "owner": "nixpak",
+        "repo": "nixpak",
         "type": "github"
       }
     },
@@ -363,7 +397,7 @@
         "hjem": "hjem",
         "import-tree": "import-tree",
         "ncro": "ncro",
-        "nix-flatpak": "nix-flatpak",
+        "nixpak": "nixpak",
         "nixpkgs": "nixpkgs",
         "nur": "nur",
         "nur-hpcesia": "nur-hpcesia",
flake.nix
@@ -41,7 +41,13 @@
       url = "github:manic-systems/ncro";
       inputs.nixpkgs.follows = "nixpkgs";
     };
-    nix-flatpak.url = "github:gmodena/nix-flatpak";
+    nixpak = {
+      url = "github:nixpak/nixpak";
+      inputs = {
+        flake-parts.follows = "flake-parts";
+        nixpkgs.follows = "nixpkgs";
+      };
+    };
     nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
     nur = {
       url = "github:nix-community/NUR";