From 1c9249426a274884f8104e26ca63e4b3b3578687 Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Mon, 13 Apr 2026 20:47:25 -0400 Subject: mojo: Refactor --- hostnix/mojo/claude.sb | 296 -------------------------- hostnix/mojo/flake.nix | 105 +++------ hostnix/mojo/modules/apps.nix | 26 +++ hostnix/mojo/modules/system.nix | 23 ++ hostnix/mojo/packages/claude-code/claude.sb | 296 ++++++++++++++++++++++++++ hostnix/mojo/packages/claude-code/default.nix | 11 + 6 files changed, 391 insertions(+), 366 deletions(-) delete mode 100644 hostnix/mojo/claude.sb create mode 100644 hostnix/mojo/modules/apps.nix create mode 100644 hostnix/mojo/modules/system.nix create mode 100644 hostnix/mojo/packages/claude-code/claude.sb create mode 100644 hostnix/mojo/packages/claude-code/default.nix (limited to 'hostnix') diff --git a/hostnix/mojo/claude.sb b/hostnix/mojo/claude.sb deleted file mode 100644 index 72797de..0000000 --- a/hostnix/mojo/claude.sb +++ /dev/null @@ -1,296 +0,0 @@ -(version 1) - -;; Based on Para Sandboxing Profile - Standard - https://github.com/2mawi2/para/blob/218259b6e260be43334f308a74108f31920f7ca4/src/core/sandbox/profiles/standard.sb -;; Forbids reading HOME_DIR except for cwd (TARGET_DIR) -;; Forbids writing other than to cwd (TARGET_DIR) -;; All network is allowed - -;; Deny everything by default -(deny default) - -;; Allow network access (required for Claude API) -(allow network*) - -;; Deny reading files anywhere on host (allow rules override this below) -(deny file-read*) - -(deny file-read* - (subpath "/") - (subpath "/Users") - (subpath (param "HOME_DIR")) - (subpath (string-append (param "HOME_DIR") "/.ssh")) -) - -;; allow directories required to launch claude-code -(allow file-read* - (subpath "/usr") - (subpath "/bin") - (subpath "/opt") - (subpath "/var") - (subpath "/private/var") - (subpath "/etc") - (subpath "/private/etc") - (subpath "/System") - (subpath "/nix") - ) - -;; necessary for nix-darwin's `/run/current-system/sw/bin` -(allow file-read-metadata - (subpath "/run")) - -;; === IMPORTANT === MODIFY this section to include ALL directories leading to claude workdir === -;; for some reason claude-code needs list access to all parent directories of TARGET_DIR -;; - it doesn't need access to read the contents of directories, only the directories -;; themselves. Otherwise it will set PATH to "" and disable colored output -(allow file-read* - (literal "/") - ) - -(allow file-read* - ;; Git configuration (for commits) - (subpath (string-append (param "HOME_DIR") "/.config/git")) - (subpath (string-append (param "HOME_DIR") "/.config/jj")) - (literal (string-append (param "HOME_DIR") "/.gitconfig")) - ;; Nix configuration - (subpath (string-append (param "HOME_DIR") "/.config/nix")) - (subpath (string-append (param "HOME_DIR") "/.local/share/nix")) - ;; Nix profile binaries (symlinks to /nix/store) - (subpath (string-append (param "HOME_DIR") "/.nix-profile")) - (subpath (string-append (param "HOME_DIR") "/.local/state/nix")) - ;; gh CLI - (subpath (string-append (param "HOME_DIR") "/.config/gh")) -) - -;; Allow process execution and forking (children inherit policy) -(allow process-exec) -(allow process-fork) -;; Essential permissions - based on Chrome sandbox policy -;; Process permissions - from https://github.com/anthropic-experimental/sandbox-runtime/blob/1bafa66a2c3ebc52569fc0c1a868e85e778f66a0/src/sandbox/macos-sandbox-utils.ts#L200 -(allow process-info* (target same-sandbox)) -;; Allow signals to all children -(allow signal (target same-sandbox)) -(allow mach-priv-task-port (target same-sandbox)) - -;; User preferences - from https://github.com/anthropic-experimental/sandbox-runtime/blob/1bafa66a2c3ebc52569fc0c1a868e85e778f66a0/src/sandbox/macos-sandbox-utils.ts#L200 -;; (allow user-preference-read) ;; doesn't seem to be required by claude-code - -;; Allow read access to system information -;; From Chromium's sandbox policy for macOS -(allow sysctl-read - (sysctl-name "hw.activecpu") - (sysctl-name "hw.busfrequency_compat") - (sysctl-name "hw.byteorder") - (sysctl-name "hw.cacheconfig") - (sysctl-name "hw.cachelinesize_compat") - (sysctl-name "hw.cpufamily") - (sysctl-name "hw.cpufrequency_compat") - (sysctl-name "hw.cputype") - (sysctl-name "hw.l1dcachesize_compat") - (sysctl-name "hw.l1icachesize_compat") - (sysctl-name "hw.l2cachesize_compat") - (sysctl-name "hw.l3cachesize_compat") - (sysctl-name "hw.logicalcpu_max") - (sysctl-name "hw.machine") - (sysctl-name "hw.memsize") - (sysctl-name "hw.ncpu") - ;; Needed for Lix - (sysctl-name "hw.pagesize") - (sysctl-name "hw.pagesize_compat") - (sysctl-name "hw.physicalcpu_max") - (sysctl-name "hw.tbfrequency_compat") - (sysctl-name "kern.hostname") - (sysctl-name "kern.maxfilesperproc") - (sysctl-name "kern.osproductversion") - (sysctl-name "kern.osrelease") - (sysctl-name "kern.ostype") - (sysctl-name "kern.osversion") - (sysctl-name "kern.secure_kernel") - (sysctl-name "kern.version") -) - -;; Allow file writes to specific paths only -;; Note: file-write* does NOT include file-write-create, so we need both -(allow file-read* file-write* file-write-create file-read-metadata file-ioctl - ;; Project directory - primary workspace - (subpath (param "TARGET_DIR")) - - ;; Temporary directories - (subpath (param "TMP_DIR")) - (subpath "/tmp") - (subpath "/private/tmp") - (subpath "/var/folders") ; macOS temp directory root - (subpath "/private/var/folders") ; Real path (var is symlink to private/var) - - ;; below is from `para`'s' sandbox.sb, but these rules don't work because sandbox-exec uses GLOB 'regexes' - ;; (regex #"^/var/folders/[^/]+/[^/]+/[^/]+/.*") ; macOS temp dirs and subdirs - ;; (regex #"^/private/var/folders/[^/]+/[^/]+/[^/]+/.*") ; Real path version - ;; (regex #"^/var/folders/.*") ; Allow all subdirectories under /var/folders for broader compatibility - ;; (regex #"^/private/var/folders/.*") ; Real path version - - ;; Cache directory - (subpath (param "CACHE_DIR")) - (subpath (string-append (param "HOME_DIR") "/.cache")) - - ;; Claude configuration - (subpath (string-append (param "HOME_DIR") "/.claude")) - (literal (string-append (param "HOME_DIR") "/.claude.json")) - (literal (string-append (param "HOME_DIR") "/.claude.json.backup")) - (subpath (string-append (param "HOME_DIR") "/Library/Caches/claude-cli-nodejs")) - - ;; Gemini configuration - (subpath (string-append (param "HOME_DIR") "/.gemini")) - - ;; Standard I/O devices - (literal "/dev/stdout") - (literal "/dev/stderr") - (literal "/dev/null") - (literal "/dev/zero") - (literal "/dev/tty") - (literal "/dev/ptmx") - (literal "/dev/urandom") - (literal "/dev/random") - (regex #"^/dev/tty*") - (regex #"^/dev/pty*") -) - -;; File I/O on device files - sandbox-runtime - https://github.com/anthropic-experimental/sandbox-runtime/blob/1bafa66a2c3ebc52569fc0c1a868e85e778f66a0/src/sandbox/macos-sandbox-utils.ts#L200 -(allow file-ioctl file-read-metadata file-read-data file-write-data (literal "/dev/dtracehelper")) -(allow file-ioctl file-read-metadata file-read-data file-write-data - (require-all - (literal "/dev/null") - (vnode-type CHARACTER-DEVICE) - ) -) - -;; Gemini-specific permissions -(allow pseudo-tty) - -;; Allow mach lookups for essential services -(allow mach-lookup - (global-name "com.apple.sysmond") ; For process listing - (global-name "com.apple.FSEvents") ; For Node.js file watching - (global-name "com.apple.SystemConfiguration.DNSConfiguration") ; For DNS resolution in Lix -) - -;; Allow file attribute operations needed for file creation -;; (allow file-write-setugid) -;; (allow file-write-mode) -;; (allow file-write-owner) -;; (allow file-write-times) -;; (allow file-write-flags) - -(allow mach-lookup - (global-name "com.apple.audio.systemsoundserver") - (global-name "com.apple.distributed_notifications@Uv3") - (global-name "com.apple.FontObjectsServer") - (global-name "com.apple.fonts") - (global-name "com.apple.logd") - (global-name "com.apple.lsd.mapdb") - (global-name "com.apple.PowerManagement.control") - (global-name "com.apple.system.logger") - (global-name "com.apple.system.notification_center") - (global-name "com.apple.trustd.agent") - (global-name "com.apple.system.opendirectoryd.libinfo") - (global-name "com.apple.system.opendirectoryd.membership") - (global-name "com.apple.bsd.dirhelper") - (global-name "com.apple.securityd.xpc") - (global-name "com.apple.coreservices.launchservicesd") -) - -;; The following is required to get Claude API Key from macOS Keychain (if logged in via /login) - -;; Specifically allow login keychain -(allow file-read* - (literal (string-append (param "HOME_DIR") "/Library/Keychains/login.keychain-db")) -) - -;; Critical: Allow communication with securityd (keychain daemon) -(allow mach-lookup - (global-name "com.apple.SecurityServer") - (global-name "com.apple.securityd") - (global-name "com.apple.securityd.xpc") -) - -;; Java/Scala development permissions - -;; Java-specific services -(allow mach-lookup - (global-name "com.apple.diagnosticd") - (global-name "com.apple.SystemConfiguration.configd") -) - -;; Java-specific sysctl reads -(allow sysctl-read - (sysctl-name "security.mac.lockdown_mode_state") - (sysctl-name "kern.bootargs") - (sysctl-name "kern.osvariant_status") - (sysctl-name "kern.argmax") - (sysctl-name "hw.ephemeral_storage") - (sysctl-name "hw.optional.armv8_crc32") - (sysctl-name "hw.optional.arm.FEAT_LSE") - (sysctl-name "hw.optional.armv8_1_atomics") - (sysctl-name "hw.optional.arm.FEAT_SHA512") - (sysctl-name "hw.optional.armv8_2_sha512") - (sysctl-name "hw.optional.arm.FEAT_SHA3") - (sysctl-name "hw.optional.armv8_2_sha3") - (sysctl-name "net.routetable.0.0.3.0") -) - -;; Java needs to read dtracehelper -(allow file-read-data (literal "/dev/dtracehelper")) - -;: ;; Java needs IPC shared memory for notification center -;; (allow ipc-posix-shm-read-data -;; (ipc-posix-name "apple.shm.notification_center") -;; ) - -;; Java needs system sockets (domain:32 is AF_NDRV for network device raw access) -(allow system-socket) - -;; Java needs to read metadata on certain directories -(allow file-read-metadata - (literal "/dev") - (literal "/private") - (literal "/Library") - (literal (string-append (param "HOME_DIR") "/Library")) - (literal (string-append (param "HOME_DIR") "/Library/Caches")) -) - -(allow file-read-data - (literal "/dev") -) - -;; Java needs to read various preference files -(allow file-read* - (literal "/Library/Preferences/Logging/com.apple.diagnosticd.filter.plist") - (literal "/Library/Preferences/.GlobalPreferences.plist") - (literal "/Library/Preferences/com.apple.networkd.plist") - (literal (string-append (param "HOME_DIR") "/Library/Preferences/.GlobalPreferences.plist")) - (literal (string-append (param "HOME_DIR") "/Library/Preferences/.GlobalPreferences_m.plist")) - (regex (string-append "^" (param "HOME_DIR") "/Library/Preferences/ByHost/\\.GlobalPreferences\\..*\\.plist$")) -) - -;; read/write ~/.sbt, coursier and bloop caches -(allow file-read* file-write* - (subpath (string-append (param "HOME_DIR") "/.sbt")) - (subpath (string-append (param "HOME_DIR") "/.ivy2")) - (subpath (string-append (param "HOME_DIR") "/.m2")) - (subpath (string-append (param "HOME_DIR") "/.jgit")) - (subpath (string-append (param "HOME_DIR") "/.config/jgit")) - (subpath (string-append (param "HOME_DIR") "/Library/Caches/Coursier")) - (subpath (string-append (param "HOME_DIR") "/Library/Caches/ScalaCli")) -) - -;; read [~]/Library/Java -(allow file-read* - (subpath (string-append (param "HOME_DIR") "/Library/Java")) - (subpath "/Library/Java") -) - -;; Generated allow-read rules for: /Users/joe/src -;; for some reason claude-code needs list access to all parent directories of TARGET_DIR -;; - it doesn't need access to read the contents of directories, only the directories -;; themselves. Otherwise it will set PATH to "" and disable colored output -(allow file-read* (literal "/Users")) -(allow file-read* (literal "/Users/joe")) -(allow file-read* (literal "/Users/joe/src")) diff --git a/hostnix/mojo/flake.nix b/hostnix/mojo/flake.nix index 9c6ca1c..319dfad 100644 --- a/hostnix/mojo/flake.nix +++ b/hostnix/mojo/flake.nix @@ -8,79 +8,44 @@ nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = inputs@{ self, nix-darwin, nixpkgs, nixpkgs-unstable }: - let - configuration = { pkgs, ... }: { - environment.systemPackages = let - # TODO is there a better way to do this? - pkgsUnstable = import nixpkgs-unstable { - localSystem = pkgs.stdenv.system; - config.allowUnfree = true; + outputs = + inputs@{ + self, + nix-darwin, + nixpkgs, + nixpkgs-unstable, + }: + let + configuration = + { pkgs, pkgsUnstable, ... }: + { + nix.settings.experimental-features = "nix-command flakes"; + + # Set Git commit hash for darwin-version. + system.configurationRevision = self.rev or self.dirtyRev or null; + + # Used for backwards compatibility, please read the changelog before changing. + # $ darwin-rebuild changelog + system.stateVersion = 6; + + # The platform the configuration will be used on. + nixpkgs.hostPlatform = "aarch64-darwin"; }; - in - [ - pkgs.direnv - pkgs.nix-direnv - (pkgs.writeShellScriptBin "claude" '' - exec /usr/bin/sandbox-exec -f ${self}/claude.sb \ - -D TARGET_DIR=$HOME/src \ - -D TMP_DIR=/tmp \ - -D HOME_DIR=$HOME \ - -D CACHE_DIR=$HOME/.cache \ - ${pkgsUnstable.claude-code}/bin/claude \ - --allow-dangerously-skip-permissions "$@" - '') - pkgsUnstable.jujutsu - ]; - - # $ chsh -s /run/current-system/sw/bin/bash - environment.shells = [ pkgs.bashInteractive ]; - programs.bash.completion.enable = true; - - nix.settings.experimental-features = "nix-command flakes"; - - system.primaryUser = "joe"; - - # Set Git commit hash for darwin-version. - system.configurationRevision = self.rev or self.dirtyRev or null; - - # Used for backwards compatibility, please read the changelog before changing. - # $ darwin-rebuild changelog - system.stateVersion = 6; - - # The platform the configuration will be used on. - nixpkgs.hostPlatform = "aarch64-darwin"; - - nixpkgs.config.allowUnfree = true; - }; - - apps = { pkgs, ... }: { - homebrew = { - enable = true; - # Enable after nix-darwin 25.11 - # enableBashIntegration = true; - caskArgs.require_sha = true; - onActivation.cleanup = "uninstall"; - brews = [ "mas" ]; - casks = [ - # Swap Command/Option, simulate fn, default fn1-12, reverse scroll. - "karabiner-elements" - # Scroll by lines and universal back/forward. - "linearmouse" - ]; - masApps = { - "Ghostery Privacy Ad Blocker" = 6504861501; - "Kagi for Safari" = 1622835804; - "KeePassium (KeePass passwords)" = 1435127111; - Xcode = 497799835; + in + { + darwinConfigurations.mojo = nix-darwin.lib.darwinSystem { + specialArgs = { + pkgsUnstable = import nixpkgs-unstable { + system = "aarch64-darwin"; + config.allowUnfree = true; + }; }; + modules = [ + configuration + ./modules/apps.nix + ./modules/system.nix + ]; }; }; - in - { - darwinConfigurations.mojo = nix-darwin.lib.darwinSystem { - modules = [ configuration apps ]; - }; - }; } diff --git a/hostnix/mojo/modules/apps.nix b/hostnix/mojo/modules/apps.nix new file mode 100644 index 0000000..1562893 --- /dev/null +++ b/hostnix/mojo/modules/apps.nix @@ -0,0 +1,26 @@ +{ pkgs, pkgsUnstable, ... }: +{ + environment.systemPackages = [ + pkgs.direnv + pkgs.nix-direnv + + (pkgsUnstable.callPackage ../packages/claude-code { }) + pkgsUnstable.jujutsu + ]; + + homebrew = { + brews = [ "mas" ]; + + casks = [ + "karabiner-elements" # modifiers, fn, reverse scroll + "linearmouse" # scroll by lines, universal back/forward + ]; + + masApps = { + "Ghostery Privacy Ad Blocker" = 6504861501; + "Kagi for Safari" = 1622835804; + "KeePassium (KeePass passwords)" = 1435127111; + Xcode = 497799835; + }; + }; +} diff --git a/hostnix/mojo/modules/system.nix b/hostnix/mojo/modules/system.nix new file mode 100644 index 0000000..362a01b --- /dev/null +++ b/hostnix/mojo/modules/system.nix @@ -0,0 +1,23 @@ +{ + pkgs, + pkgsUnstable, + self, + ... +}: +{ + nixpkgs.config.allowUnfree = true; + + homebrew = { + enable = true; + # Enable after nix-darwin 25.11 + # enableBashIntegration = true; + caskArgs.require_sha = true; + onActivation.cleanup = "uninstall"; + }; + + system.primaryUser = "joe"; + + # $ chsh -s /run/current-system/sw/bin/bash + environment.shells = [ pkgs.bashInteractive ]; + programs.bash.completion.enable = true; +} diff --git a/hostnix/mojo/packages/claude-code/claude.sb b/hostnix/mojo/packages/claude-code/claude.sb new file mode 100644 index 0000000..72797de --- /dev/null +++ b/hostnix/mojo/packages/claude-code/claude.sb @@ -0,0 +1,296 @@ +(version 1) + +;; Based on Para Sandboxing Profile - Standard - https://github.com/2mawi2/para/blob/218259b6e260be43334f308a74108f31920f7ca4/src/core/sandbox/profiles/standard.sb +;; Forbids reading HOME_DIR except for cwd (TARGET_DIR) +;; Forbids writing other than to cwd (TARGET_DIR) +;; All network is allowed + +;; Deny everything by default +(deny default) + +;; Allow network access (required for Claude API) +(allow network*) + +;; Deny reading files anywhere on host (allow rules override this below) +(deny file-read*) + +(deny file-read* + (subpath "/") + (subpath "/Users") + (subpath (param "HOME_DIR")) + (subpath (string-append (param "HOME_DIR") "/.ssh")) +) + +;; allow directories required to launch claude-code +(allow file-read* + (subpath "/usr") + (subpath "/bin") + (subpath "/opt") + (subpath "/var") + (subpath "/private/var") + (subpath "/etc") + (subpath "/private/etc") + (subpath "/System") + (subpath "/nix") + ) + +;; necessary for nix-darwin's `/run/current-system/sw/bin` +(allow file-read-metadata + (subpath "/run")) + +;; === IMPORTANT === MODIFY this section to include ALL directories leading to claude workdir === +;; for some reason claude-code needs list access to all parent directories of TARGET_DIR +;; - it doesn't need access to read the contents of directories, only the directories +;; themselves. Otherwise it will set PATH to "" and disable colored output +(allow file-read* + (literal "/") + ) + +(allow file-read* + ;; Git configuration (for commits) + (subpath (string-append (param "HOME_DIR") "/.config/git")) + (subpath (string-append (param "HOME_DIR") "/.config/jj")) + (literal (string-append (param "HOME_DIR") "/.gitconfig")) + ;; Nix configuration + (subpath (string-append (param "HOME_DIR") "/.config/nix")) + (subpath (string-append (param "HOME_DIR") "/.local/share/nix")) + ;; Nix profile binaries (symlinks to /nix/store) + (subpath (string-append (param "HOME_DIR") "/.nix-profile")) + (subpath (string-append (param "HOME_DIR") "/.local/state/nix")) + ;; gh CLI + (subpath (string-append (param "HOME_DIR") "/.config/gh")) +) + +;; Allow process execution and forking (children inherit policy) +(allow process-exec) +(allow process-fork) +;; Essential permissions - based on Chrome sandbox policy +;; Process permissions - from https://github.com/anthropic-experimental/sandbox-runtime/blob/1bafa66a2c3ebc52569fc0c1a868e85e778f66a0/src/sandbox/macos-sandbox-utils.ts#L200 +(allow process-info* (target same-sandbox)) +;; Allow signals to all children +(allow signal (target same-sandbox)) +(allow mach-priv-task-port (target same-sandbox)) + +;; User preferences - from https://github.com/anthropic-experimental/sandbox-runtime/blob/1bafa66a2c3ebc52569fc0c1a868e85e778f66a0/src/sandbox/macos-sandbox-utils.ts#L200 +;; (allow user-preference-read) ;; doesn't seem to be required by claude-code + +;; Allow read access to system information +;; From Chromium's sandbox policy for macOS +(allow sysctl-read + (sysctl-name "hw.activecpu") + (sysctl-name "hw.busfrequency_compat") + (sysctl-name "hw.byteorder") + (sysctl-name "hw.cacheconfig") + (sysctl-name "hw.cachelinesize_compat") + (sysctl-name "hw.cpufamily") + (sysctl-name "hw.cpufrequency_compat") + (sysctl-name "hw.cputype") + (sysctl-name "hw.l1dcachesize_compat") + (sysctl-name "hw.l1icachesize_compat") + (sysctl-name "hw.l2cachesize_compat") + (sysctl-name "hw.l3cachesize_compat") + (sysctl-name "hw.logicalcpu_max") + (sysctl-name "hw.machine") + (sysctl-name "hw.memsize") + (sysctl-name "hw.ncpu") + ;; Needed for Lix + (sysctl-name "hw.pagesize") + (sysctl-name "hw.pagesize_compat") + (sysctl-name "hw.physicalcpu_max") + (sysctl-name "hw.tbfrequency_compat") + (sysctl-name "kern.hostname") + (sysctl-name "kern.maxfilesperproc") + (sysctl-name "kern.osproductversion") + (sysctl-name "kern.osrelease") + (sysctl-name "kern.ostype") + (sysctl-name "kern.osversion") + (sysctl-name "kern.secure_kernel") + (sysctl-name "kern.version") +) + +;; Allow file writes to specific paths only +;; Note: file-write* does NOT include file-write-create, so we need both +(allow file-read* file-write* file-write-create file-read-metadata file-ioctl + ;; Project directory - primary workspace + (subpath (param "TARGET_DIR")) + + ;; Temporary directories + (subpath (param "TMP_DIR")) + (subpath "/tmp") + (subpath "/private/tmp") + (subpath "/var/folders") ; macOS temp directory root + (subpath "/private/var/folders") ; Real path (var is symlink to private/var) + + ;; below is from `para`'s' sandbox.sb, but these rules don't work because sandbox-exec uses GLOB 'regexes' + ;; (regex #"^/var/folders/[^/]+/[^/]+/[^/]+/.*") ; macOS temp dirs and subdirs + ;; (regex #"^/private/var/folders/[^/]+/[^/]+/[^/]+/.*") ; Real path version + ;; (regex #"^/var/folders/.*") ; Allow all subdirectories under /var/folders for broader compatibility + ;; (regex #"^/private/var/folders/.*") ; Real path version + + ;; Cache directory + (subpath (param "CACHE_DIR")) + (subpath (string-append (param "HOME_DIR") "/.cache")) + + ;; Claude configuration + (subpath (string-append (param "HOME_DIR") "/.claude")) + (literal (string-append (param "HOME_DIR") "/.claude.json")) + (literal (string-append (param "HOME_DIR") "/.claude.json.backup")) + (subpath (string-append (param "HOME_DIR") "/Library/Caches/claude-cli-nodejs")) + + ;; Gemini configuration + (subpath (string-append (param "HOME_DIR") "/.gemini")) + + ;; Standard I/O devices + (literal "/dev/stdout") + (literal "/dev/stderr") + (literal "/dev/null") + (literal "/dev/zero") + (literal "/dev/tty") + (literal "/dev/ptmx") + (literal "/dev/urandom") + (literal "/dev/random") + (regex #"^/dev/tty*") + (regex #"^/dev/pty*") +) + +;; File I/O on device files - sandbox-runtime - https://github.com/anthropic-experimental/sandbox-runtime/blob/1bafa66a2c3ebc52569fc0c1a868e85e778f66a0/src/sandbox/macos-sandbox-utils.ts#L200 +(allow file-ioctl file-read-metadata file-read-data file-write-data (literal "/dev/dtracehelper")) +(allow file-ioctl file-read-metadata file-read-data file-write-data + (require-all + (literal "/dev/null") + (vnode-type CHARACTER-DEVICE) + ) +) + +;; Gemini-specific permissions +(allow pseudo-tty) + +;; Allow mach lookups for essential services +(allow mach-lookup + (global-name "com.apple.sysmond") ; For process listing + (global-name "com.apple.FSEvents") ; For Node.js file watching + (global-name "com.apple.SystemConfiguration.DNSConfiguration") ; For DNS resolution in Lix +) + +;; Allow file attribute operations needed for file creation +;; (allow file-write-setugid) +;; (allow file-write-mode) +;; (allow file-write-owner) +;; (allow file-write-times) +;; (allow file-write-flags) + +(allow mach-lookup + (global-name "com.apple.audio.systemsoundserver") + (global-name "com.apple.distributed_notifications@Uv3") + (global-name "com.apple.FontObjectsServer") + (global-name "com.apple.fonts") + (global-name "com.apple.logd") + (global-name "com.apple.lsd.mapdb") + (global-name "com.apple.PowerManagement.control") + (global-name "com.apple.system.logger") + (global-name "com.apple.system.notification_center") + (global-name "com.apple.trustd.agent") + (global-name "com.apple.system.opendirectoryd.libinfo") + (global-name "com.apple.system.opendirectoryd.membership") + (global-name "com.apple.bsd.dirhelper") + (global-name "com.apple.securityd.xpc") + (global-name "com.apple.coreservices.launchservicesd") +) + +;; The following is required to get Claude API Key from macOS Keychain (if logged in via /login) + +;; Specifically allow login keychain +(allow file-read* + (literal (string-append (param "HOME_DIR") "/Library/Keychains/login.keychain-db")) +) + +;; Critical: Allow communication with securityd (keychain daemon) +(allow mach-lookup + (global-name "com.apple.SecurityServer") + (global-name "com.apple.securityd") + (global-name "com.apple.securityd.xpc") +) + +;; Java/Scala development permissions + +;; Java-specific services +(allow mach-lookup + (global-name "com.apple.diagnosticd") + (global-name "com.apple.SystemConfiguration.configd") +) + +;; Java-specific sysctl reads +(allow sysctl-read + (sysctl-name "security.mac.lockdown_mode_state") + (sysctl-name "kern.bootargs") + (sysctl-name "kern.osvariant_status") + (sysctl-name "kern.argmax") + (sysctl-name "hw.ephemeral_storage") + (sysctl-name "hw.optional.armv8_crc32") + (sysctl-name "hw.optional.arm.FEAT_LSE") + (sysctl-name "hw.optional.armv8_1_atomics") + (sysctl-name "hw.optional.arm.FEAT_SHA512") + (sysctl-name "hw.optional.armv8_2_sha512") + (sysctl-name "hw.optional.arm.FEAT_SHA3") + (sysctl-name "hw.optional.armv8_2_sha3") + (sysctl-name "net.routetable.0.0.3.0") +) + +;; Java needs to read dtracehelper +(allow file-read-data (literal "/dev/dtracehelper")) + +;: ;; Java needs IPC shared memory for notification center +;; (allow ipc-posix-shm-read-data +;; (ipc-posix-name "apple.shm.notification_center") +;; ) + +;; Java needs system sockets (domain:32 is AF_NDRV for network device raw access) +(allow system-socket) + +;; Java needs to read metadata on certain directories +(allow file-read-metadata + (literal "/dev") + (literal "/private") + (literal "/Library") + (literal (string-append (param "HOME_DIR") "/Library")) + (literal (string-append (param "HOME_DIR") "/Library/Caches")) +) + +(allow file-read-data + (literal "/dev") +) + +;; Java needs to read various preference files +(allow file-read* + (literal "/Library/Preferences/Logging/com.apple.diagnosticd.filter.plist") + (literal "/Library/Preferences/.GlobalPreferences.plist") + (literal "/Library/Preferences/com.apple.networkd.plist") + (literal (string-append (param "HOME_DIR") "/Library/Preferences/.GlobalPreferences.plist")) + (literal (string-append (param "HOME_DIR") "/Library/Preferences/.GlobalPreferences_m.plist")) + (regex (string-append "^" (param "HOME_DIR") "/Library/Preferences/ByHost/\\.GlobalPreferences\\..*\\.plist$")) +) + +;; read/write ~/.sbt, coursier and bloop caches +(allow file-read* file-write* + (subpath (string-append (param "HOME_DIR") "/.sbt")) + (subpath (string-append (param "HOME_DIR") "/.ivy2")) + (subpath (string-append (param "HOME_DIR") "/.m2")) + (subpath (string-append (param "HOME_DIR") "/.jgit")) + (subpath (string-append (param "HOME_DIR") "/.config/jgit")) + (subpath (string-append (param "HOME_DIR") "/Library/Caches/Coursier")) + (subpath (string-append (param "HOME_DIR") "/Library/Caches/ScalaCli")) +) + +;; read [~]/Library/Java +(allow file-read* + (subpath (string-append (param "HOME_DIR") "/Library/Java")) + (subpath "/Library/Java") +) + +;; Generated allow-read rules for: /Users/joe/src +;; for some reason claude-code needs list access to all parent directories of TARGET_DIR +;; - it doesn't need access to read the contents of directories, only the directories +;; themselves. Otherwise it will set PATH to "" and disable colored output +(allow file-read* (literal "/Users")) +(allow file-read* (literal "/Users/joe")) +(allow file-read* (literal "/Users/joe/src")) diff --git a/hostnix/mojo/packages/claude-code/default.nix b/hostnix/mojo/packages/claude-code/default.nix new file mode 100644 index 0000000..d8e333a --- /dev/null +++ b/hostnix/mojo/packages/claude-code/default.nix @@ -0,0 +1,11 @@ +{ writeShellScriptBin, claude-code }: + +writeShellScriptBin "claude" '' + exec /usr/bin/sandbox-exec -f ${./claude.sb} \ + -D TARGET_DIR=$HOME/src \ + -D TMP_DIR=/tmp \ + -D HOME_DIR=$HOME \ + -D CACHE_DIR=$HOME/.cache \ + ${claude-code}/bin/claude \ + --allow-dangerously-skip-permissions "$@" +'' -- cgit v1.3.1