-
Notifications
You must be signed in to change notification settings - Fork 1
Implement macOS support using sandbox-exec and refactor for multi-platform support #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
srid
merged 32 commits into
srid:master
from
adrian-gierakowski:darwin-implementation-via-sandbox-exec
Mar 6, 2026
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
86109c6
add macos support via sandbox-exec
adrian-gierakowski 04ea4c2
improve darwin impl (sandbox-exec) to pass tests added in 635ebfb
adrian-gierakowski ca9ca02
sandbox-exec impl: clean up and add comments
adrian-gierakowski 178550f
sandbox-exec fix tty
adrian-gierakowski 5860fc5
fix tty test in nix sandbox
adrian-gierakowski 767ae07
fix indentation
adrian-gierakowski 03a2271
only include glibc on linux
adrian-gierakowski bd59345
remove flake-parts debug = true
adrian-gierakowski 552f39a
test ls with /etc
adrian-gierakowski 619a5c9
use pkgs.perl
adrian-gierakowski b656502
fix missing USER
adrian-gierakowski bfa7629
further cleanup and add test features.tmp
adrian-gierakowski 47fb1cb
allow exec from tmp
adrian-gierakowski 2014598
Refactor Darwin implementation into its own directory
google-labs-jules[bot] 1ecbf4b
create linux, darwin and common dirs to seperate platform dependant code
adrian-gierakowski 8ba32c2
vira: add macOS
srid f78c1b3
ci: run tests on github actions
srid fbd88dd
oi
srid b12e3b9
ci: use nixbuild/nix-quick-install-action v34
adrian-gierakowski b4ccd84
ci: use cachix/install-nix-action@v31
adrian-gierakowski 44e3245
ci: fix TTY tests by providing a pseudo-terminal (PTY) via script
adrian-gierakowski 9fba4a9
Revert "ci: use cachix/install-nix-action@v31"
adrian-gierakowski 9c441e0
Address PR #18 review comments
google-labs-jules[bot] 82438fc
update example claude-sandboxed to use parent flake
adrian-gierakowski f6b9b9b
fix mktemp sandbox-profile on macos
adrian-gierakowski 455179d
use coreutils mktemp in modules/flake-parts/landrun/darwin/wrapper.nix
adrian-gierakowski b1c3408
add coreutils to runtimeInputs of modules/flake-parts/landrun/darwin/…
adrian-gierakowski bd596a4
allow setting `name` different than submodule.name
adrian-gierakowski 905091f
add meta.mainProgram to wrapper
adrian-gierakowski aac4746
use lib.getExe to get bin path of langrunApp when mapping to flake apps
adrian-gierakowski b4444e9
Revert "add meta.mainProgram to wrapper"
adrian-gierakowski 664d588
improve env var handling on darwin
google-labs-jules[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| name: Tests | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main, master] | ||
| pull_request: | ||
|
|
||
| jobs: | ||
| test: | ||
| strategy: | ||
| matrix: | ||
| os: [macos-latest, ubuntu-latest] | ||
| runs-on: ${{ matrix.os }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Install Nix | ||
| uses: nixbuild/nix-quick-install-action@v34 | ||
|
|
||
| - name: Install just | ||
| uses: extractions/setup-just@v2 | ||
|
|
||
| - name: Run tests | ||
| run: just test |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,15 @@ | ||
| # Run the claude-sandboxed example with local landrun-nix override | ||
| run-example: | ||
| nix run ./examples/claude-sandboxed --override-input landrun-nix . | ||
|
|
||
| # Run integration tests | ||
| test: | ||
| #!/usr/bin/env bash | ||
| if [ "$(uname)" = "Darwin" ]; then | ||
| # macOS: BSD script syntax: script -q <output> <command> | ||
| SCRIPT_ARGS="-q /dev/null ./tests/test.bats" | ||
| else | ||
| # Linux: util-linux script syntax: script -qec <command> <output> | ||
| SCRIPT_ARGS="-qec ./tests/test.bats /dev/null" | ||
| fi | ||
| nix develop ./tests --override-input landrun-nix path:./. -c script $SCRIPT_ARGS |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { lib, config, ... }: | ||
| { | ||
| config = { | ||
| # Auto-configure CLI options based on high-level flags | ||
| cli = lib.mkMerge [ | ||
| # TTY support | ||
| (lib.mkIf config.features.tty { | ||
| rw = [ | ||
| "/dev/null" | ||
| "/dev/tty" | ||
| ]; | ||
| rox = [ | ||
| "/dev/zero" | ||
| "/dev/random" | ||
| "/dev/urandom" | ||
| "/usr/share/terminfo" | ||
| ]; | ||
| env = [ | ||
| "TERM" | ||
| "SHELL" | ||
| "COLORTERM" | ||
| "LANG" | ||
| "LC_ALL" | ||
| ]; | ||
| }) | ||
|
|
||
| # Nix support | ||
| (lib.mkIf config.features.nix { | ||
| rox = [ | ||
| "/nix" | ||
| "/usr" | ||
| "/lib" | ||
| ]; | ||
| rw = [ | ||
| "$HOME/.cache/nix" | ||
| ]; | ||
| ro = [ | ||
| "/etc/nix" | ||
| "$HOME/.local/share/nix" | ||
| ]; | ||
| env = [ | ||
| "PATH" # Required for programs to find executables | ||
| "NIX_PATH" | ||
| "NIX_SSL_CERT_FILE" | ||
| ]; | ||
| }) | ||
|
|
||
| # Network support | ||
| (lib.mkIf config.features.network { | ||
| rox = [ | ||
| "/etc/resolv.conf" | ||
| "/etc/ssl" | ||
| ]; | ||
| unrestrictedNetwork = true; | ||
| }) | ||
|
|
||
| # Tmp support | ||
| (lib.mkIf config.features.tmp { | ||
| rwx = [ "/tmp" ]; | ||
| }) | ||
| ]; | ||
| }; | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| { lib, config, pkgs, ... }: | ||
| { | ||
| config = lib.mkIf pkgs.stdenv.isDarwin { | ||
| cli = lib.mkMerge [ | ||
| # TTY support | ||
| (lib.mkIf config.features.tty { | ||
| rox = [ | ||
| "/etc/profile" # Shell initialization | ||
| ]; | ||
| }) | ||
|
|
||
| # Nix support | ||
| (lib.mkIf config.features.nix { | ||
| rox = [ | ||
| "/bin" | ||
| ]; | ||
| ro = [ | ||
| "/var/run/syslog" # Often needed for logging on macOS | ||
| ]; | ||
| }) | ||
|
|
||
| # Tmp support | ||
| (lib.mkIf config.features.tmp { | ||
| rwx = [ "/var/folders" ]; | ||
| }) | ||
| ]; | ||
| }; | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| { lib, config, pkgs, ... }: | ||
| let | ||
| pkg = pkgs.writeShellApplication { | ||
| name = config.name; | ||
| runtimeInputs = [ pkgs.coreutils ]; | ||
| text = '' | ||
| user=''${USER:-nobody} | ||
| PROFILE_FILE=$(mktemp "/tmp/sandbox-profile-$user-XXXXXX.sb") | ||
| trap 'rm -f "$PROFILE_FILE"' EXIT | ||
|
|
||
| cat > "$PROFILE_FILE" <<EOF | ||
| ;; Sandbox Profile Language (SBPL) version 1 | ||
| (version 1) | ||
|
|
||
| ;; Default deny policy: Everything is denied unless explicitly allowed | ||
| (deny default) | ||
|
|
||
| ;; Allow forking child processes (essential for shell wrappers and most apps) | ||
| (allow process-fork) | ||
|
|
||
| ;; Allow sending signals to processes | ||
| (allow signal) | ||
|
|
||
| ;; Import standard system profile | ||
| ;; This is required for basic system functionality: | ||
| ;; - Dynamic linker (dyld) operation | ||
| ;; - System frameworks and libraries (libc, libSystem, etc.) | ||
| ;; - Basic kernel interactions (mach lookups, sysctls) | ||
| ;; Without this, almost no binary can execute on macOS. | ||
| (import "system.sb") | ||
|
|
||
| ;; Allow broad metadata access | ||
| ;; 'file-read-metadata' is needed for: | ||
| ;; - 'ls' (directory listing implies metadata read) | ||
| ;; - 'getcwd' path resolution | ||
| ;; - 'realpath' and symlink resolution | ||
| ;; 'file-test-existence' is needed for: | ||
| ;; - Library probing (dyld checking potential paths) | ||
| ;; - Applications checking for optional config files | ||
| ;; Note: This allows seeing *if* files exist, but not reading their content. | ||
| (allow file-read-metadata) | ||
| (allow file-test-existence) | ||
|
|
||
| ;; Standard POSIX device nodes | ||
| ;; Most applications expect these to be available. | ||
| (allow file-read* | ||
| (literal "/dev/null") | ||
| (literal "/dev/zero") | ||
| (literal "/dev/random") | ||
| (literal "/dev/urandom")) | ||
|
|
||
| ${lib.optionalString config.features.tty '' | ||
| ;; Allow PTY ioctls (enabled by features.tty) | ||
| ;; Necessary for interactive applications (stty, shells, REPLs) to control the terminal. | ||
| (allow file-ioctl (regex #"^/dev/ttys[0-9]+")) | ||
| ''} | ||
|
|
||
| ;; Network Access Control | ||
| ;; Based on 'features.network' or 'cli.unrestrictedNetwork' | ||
| ${if config.cli.unrestrictedNetwork then "(allow network*)" else "(deny network*)"} | ||
|
|
||
| ;; Filesystem Access Control | ||
| ;; Based on 'cli.unrestrictedFilesystem' | ||
| ${if config.cli.unrestrictedFilesystem then "(allow file*)" else ""} | ||
|
|
||
| EOF | ||
|
|
||
| # Isolation of environment variables (like landrun does) | ||
| ALLOWED_VARS=(${lib.concatStringsSep " " (map (e: "\"${e}\"") config.cli.env)}) | ||
| KEEP_VARS=("HOME" "USER" "LOGNAME" "PATH" "TERM" "SHELL" "LANG" "LC_ALL" "DISPLAY") | ||
|
|
||
| ENV_ARGS=() | ||
| for var in "''${ALLOWED_VARS[@]}" "''${KEEP_VARS[@]}"; do | ||
| # Check if variable is set | ||
| if [[ -v "$var" ]]; then | ||
| ENV_ARGS+=("$var=''${!var}") | ||
| fi | ||
| done | ||
|
|
||
| # Function to add paths to SBPL profile | ||
| add_paths() { | ||
| local op=$1 | ||
| shift | ||
| for p in "$@"; do | ||
| # Expand $HOME and $UID if present in path | ||
| p_expanded=''${p//\$HOME/$HOME} | ||
| p_expanded=''${p_expanded//\$UID/$(id -u)} | ||
|
|
||
| # Make path absolute if it's relative | ||
| if [[ "$p_expanded" != /* ]]; then | ||
| p_expanded="$(pwd -P)/$p_expanded" | ||
| fi | ||
|
|
||
| if [ -e "$p_expanded" ]; then | ||
| # Resolve to real path for macOS sandbox | ||
| p_real=$(${lib.getExe pkgs.perl} -e 'use Cwd "abs_path"; print abs_path(shift)' "$p_expanded") | ||
|
|
||
| case "$op" in | ||
| rox) | ||
| echo "(allow file-read* (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| echo "(allow process-exec (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| ;; | ||
| ro) | ||
| echo "(allow file-read* (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| ;; | ||
| rw) | ||
| echo "(allow file-read* (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| echo "(allow file-write* (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| echo "(allow file-ioctl (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| ;; | ||
| rwx) | ||
| echo "(allow file-read* (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| echo "(allow file-write* (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| echo "(allow file-ioctl (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| echo "(allow process-exec (subpath \"$p_real\"))" >> "$PROFILE_FILE" | ||
| ;; | ||
| esac | ||
| fi | ||
| done | ||
| } | ||
|
|
||
| # Fix getcwd by allowing traversal of parents | ||
| # Use physical path for CURR_PATH | ||
| CURR_PATH="$(pwd -P)" | ||
| while [ "$CURR_PATH" != "/" ]; do | ||
| echo "(allow file-read* (literal \"$CURR_PATH\"))" >> "$PROFILE_FILE" | ||
| CURR_PATH=$(dirname "$CURR_PATH") | ||
| done | ||
|
|
||
| ${lib.optionalString config.cli.addExec '' | ||
| echo "(allow file-read* (literal \"${config.program}\"))" >> "$PROFILE_FILE" | ||
| echo "(allow process-exec (literal \"${config.program}\"))" >> "$PROFILE_FILE" | ||
| ''} | ||
|
|
||
| # shellcheck disable=SC2016 | ||
| ${lib.optionalString (config.cli.rox != []) "add_paths rox ${lib.escapeShellArgs config.cli.rox}\n"} | ||
| # shellcheck disable=SC2016 | ||
| ${lib.optionalString (config.cli.ro != []) "add_paths ro ${lib.escapeShellArgs config.cli.ro}\n"} | ||
| # shellcheck disable=SC2016 | ||
| ${lib.optionalString (config.cli.rw != []) "add_paths rw ${lib.escapeShellArgs config.cli.rw}\n"} | ||
| # shellcheck disable=SC2016 | ||
| ${lib.optionalString (config.cli.rwx != []) "add_paths rwx ${lib.escapeShellArgs config.cli.rwx}\n"} | ||
|
|
||
| # Execute with isolated environment | ||
| exec env -i "''${ENV_ARGS[@]}" sandbox-exec -f "$PROFILE_FILE" ${config.program} "$@" | ||
| ''; | ||
| }; | ||
| in | ||
| { | ||
| config = lib.mkIf pkgs.stdenv.isDarwin { | ||
| wrappedPackage = | ||
| if config.cli.extraArgs != [ ] then | ||
| lib.warn "landrun-nix: extraArgs are ignored on Darwin as sandbox-exec does not support them." pkg | ||
| else | ||
| pkg; | ||
| }; | ||
| } |
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.