Created
January 20, 2026 13:36
-
-
Save andir/8c1488f422bc335850ecd3a2cb7ead14 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { pkgs, lib, config, ... }: | |
| let | |
| inherit (lib) types; | |
| sandboxOptions = types.submodule ({ name, ... }: { | |
| options = { | |
| packages = lib.mkOption { | |
| type = types.listOf types.package; | |
| default = [ ]; | |
| }; | |
| entryPoint = lib.mkOption { | |
| type = types.nullOr types.str; | |
| default = "bin/zsh"; | |
| }; | |
| includeBasePackages = lib.mkOption { | |
| type = types.bool; | |
| default = true; | |
| }; | |
| persistent = lib.mkOption { | |
| default = false; | |
| type = types.bool; | |
| }; | |
| # relative to unsandboxed HOME | |
| stateDirectory = lib.mkOption { | |
| default = ".sandbox/${name}"; | |
| type = types.str; | |
| }; | |
| homeManagerModules = lib.mkOption { | |
| default = [ ]; | |
| # type = types.listOf types.any; | |
| }; | |
| nixpakModules = lib.mkOption { | |
| default = [ ]; | |
| }; | |
| }; | |
| }); | |
| cfg = config.sandbox; | |
| hasSandboxes = (builtins.length (builtins.attrNames cfg)) > 0; | |
| basePackages = builtins.map (n: pkgs.${n}) [ | |
| "bashInteractive" | |
| "zsh" | |
| "coreutils-full" | |
| "procps" | |
| "gnugrep" | |
| "ripgrep" | |
| "strace" | |
| ]; | |
| mkSandbox = name: options: | |
| let | |
| homeManagerConfig = | |
| if (options.homeManagerModules != [ ]) then | |
| (pkgs.home-manager.lib.homeManagerConfiguration { | |
| inherit pkgs; | |
| modules = options.homeManagerModules ++ [ | |
| { | |
| # inherit configuration from outer home-manager | |
| home.stateVersion = lib.mkDefault config.home.stateVersion; | |
| home.username = lib.mkDefault config.home.username; | |
| home.homeDirectory = lib.mkDefault config.home.homeDirectory; | |
| } | |
| ]; | |
| } | |
| ).config | |
| else null; | |
| runtimePackage = pkgs.symlinkJoin { | |
| name = "runtime-paths-${name}"; | |
| paths = options.packages | |
| ++ basePackages | |
| ++ ( | |
| if homeManagerConfig != null then | |
| homeManagerConfig.home.packages | |
| ++ [ homeManagerConfig.home.activationPackage ] | |
| else [ ] | |
| ); | |
| }; | |
| in | |
| pkgs.mkNixPak { | |
| config = { sloth, lib, ... }: { | |
| imports = options.nixpakModules ++ [ | |
| (lib.mkIf (options.persistent != false) { | |
| bubblewrap.bind.rw = [ | |
| [ | |
| (sloth.concat' (sloth.concat' sloth.homeDir options.stateDirectory) "/home") | |
| sloth.homeDir | |
| ] | |
| ]; | |
| }) | |
| (lib.mkIf (homeManagerConfig != null) { | |
| bubblewrap.bind.ro = | |
| let | |
| mapDirectory = dir: if lib.hasPrefix homeManagerConfig.home.homeDirectory dir then dir else "${homeManagerConfig.home.homeDirectory}/${dir}"; | |
| in | |
| lib.mapAttrsToList | |
| (name: value: [ | |
| (builtins.toString value.source) | |
| (mapDirectory name) | |
| ]) | |
| homeManagerConfig.home.file; | |
| }) | |
| ]; | |
| bubblewrap.bindEntireStore = false; | |
| bubblewrap.env.PATH = "${runtimePackage}/bin"; | |
| bubblewrap.env.SHELL = "${runtimePackage}/bin/zsh"; | |
| bubblewrap.tmpfs = [ "/tmp" ]; | |
| bubblewrap.bind.ro = [ | |
| # [ "${runtimePackage}" "/run/current-system/sw" ] | |
| [ | |
| "${runtimePackage}/bin/sh" | |
| "/bin/sh" | |
| ] | |
| [ | |
| (toString (pkgs.writeText "passwd" "${config.home.username}:x:1000:100::${config.home.homeDirectory}:${runtimePackage}/bin/zsh")) | |
| "/etc/passwd" | |
| ] | |
| ]; | |
| app.package = runtimePackage; | |
| app.binPath = options.entryPoint; | |
| flatpak.appId = "de.rammhold.sandbox.${name}"; | |
| }; | |
| }; | |
| in | |
| { | |
| options.sandbox = lib.mkOption { | |
| type = types.attrsOf sandboxOptions; | |
| }; | |
| config = lib.mkIf hasSandboxes { | |
| home.packages = lib.mapAttrsToList | |
| (name: value: | |
| pkgs.writeShellScriptBin "sandbox-${name}" '' | |
| echo ${name} | |
| exec ${(mkSandbox name value).config.script}/${value.entryPoint} | |
| '' | |
| ) | |
| cfg; | |
| home.file = lib.mapAttrs' | |
| (name: c: | |
| lib.nameValuePair "${c.stateDirectory}/home/.placeholder" { text = "# ignore me"; }) | |
| cfg; | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment