A devenv module that runs profile-driven
bubblewrap sandboxes as devenv
scripts. Enable it, declare a profile, and get a sandbox-<name> command that
drops you into an isolated shell with a persistent home, deny-by-default
network, and only the mounts and secrets you allow.
Requires Linux (bubblewrap + unprivileged user namespaces).
devenv.yaml:
inputs:
sandbox:
url: github:mirql/devenv-sandbox # this repo
flake: false
imports:
- sandboxdevenv.nix:
{ ... }: {
sandbox.enable = true;
sandbox.profiles.dev = {
blocks = [ "ssh" "git" ]; # reusable building blocks
network.enable = false; # deny-by-default
mounts.rw.project.src = "~/code/myproject";
env.set.EDITOR = "nvim";
};
}Then:
devenv shell sandbox-dev # enter the sandbox
devenv shell sandbox-dev -- zsh -c 'id; echo $SANDBOX'
SANDBOX_DRYRUN=1 devenv shell sandbox-dev # print the bwrap command, run nothingPin to a commit for reproducibility (url: github:mirql/devenv-sandbox/<commit>)
and bump with devenv update.
If your project drives devenv from a flake.nix, add this repo as an input and
pass the exported module to devenv.lib.mkShell:
{
inputs = {
nixpkgs.url = "github:cachix/devenv-nixpkgs/rolling";
devenv.url = "github:cachix/devenv";
sandbox.url = "github:mirql/devenv-sandbox"; # this repo (a flake)
};
outputs = { nixpkgs, devenv, sandbox, ... } @ inputs:
let system = "x86_64-linux"; in {
devShells.${system}.default = devenv.lib.mkShell {
inherit inputs;
pkgs = nixpkgs.legacyPackages.${system};
modules = [
sandbox.devenvModule
{ sandbox.enable = true; sandbox.profiles.dev.blocks = [ "ssh" "git" ]; }
];
};
};
}Enter with nix develop --no-pure-eval, then run sandbox-dev.
| Block | Adds |
|---|---|
ssh |
read-only ~/.ssh |
git |
read-only ~/.gitconfig |
nvim |
read-write ~/.config/nvim + ~/.local/share/nvim |
ai-keys |
passthrough OPENAI_API_KEY, ANTHROPIC_API_KEY, GITHUB_TOKEN |
blocks— list of block names from the table abovenetwork.enable— share the host network (default: off)mounts.ro.<name>/mounts.rw.<name>—{ src, dst ? null }env.passthrough— host env var names to forward if setenv.set— env vars to set inside the sandboxroBinds— system paths bound read-only (sensible defaults)
See examples/ai-dev/ for a full profile composing all four
blocks. That example imports the module via a local ../.. path; a real
consumer imports it by input name as shown above.
devenv.nix module entrypoint (imports ./modules/sandbox.nix)
flake.nix exposes the module for flake-based consumers
modules/
sandbox.nix options + config (generates one script per profile)
launcher.sh the runtime shim (home seeding, mounts, exec bwrap)
examples/ai-dev/ a consumer using blocks = [ ... ]