Sandbox
Last updated
HakoRun wraps the runtime in an OS-native sandbox on every run: Bubblewrap on Linux, seatbelt on macOS.
Linux: systemd-run + bwrap
internal/sandbox/command_linux.go builds systemd-run --scope --user --quiet --slice=hakorun.slice -- bwrap ... -- <runtime> /wrapper.<ext>.
| Aspect | Setting |
|---|---|
| Namespaces | --unshare-all, --unshare-net (no network) |
| Privileges | --cap-drop ALL, --new-session, --die-with-parent |
| Filesystem | Read-only /usr, /lib, /lib64, and the wrapper; tmpfs /tmp and /home/sandbox; fresh /proc and /dev |
| Working directory | /tmp |
| Environment | HOME=/home/sandbox, PATH=/usr/local/bin:/usr/bin:/bin, TMPDIR=/tmp, LANG=C.UTF-8; unsets LD_PRELOAD, LD_LIBRARY_PATH |
| TypeScript extra | Mounts the project root read-only and points NODE_PATH at its node_modules |
Because only /usr is mounted, the global tsx must live under /usr (npm's default /usr/local prefix works); bwrap fails to start on systems without /lib64.
macOS: sandbox-exec
The command is sandbox-exec -p <profile> <runtime> <wrapper>, with the profile from seatbeltProfile():
(version 1)
(deny default)
(allow process-exec)
(allow process-fork)
(allow sysctl-read)
(allow mach-lookup)
(allow signal)
(allow ipc-posix*)
(allow file-read*)
(allow file-write* (subpath "<HOME>"))
(allow network*)
Platform Comparison
| Aspect | Linux | macOS |
|---|---|---|
| Network | Disabled | Allowed |
| Writes | tmpfs only | $HOME only |
| Reads | Mounted paths only | Everywhere |
| Resource caps | hakorun.slice |
None |
| Environment | Reset | Inherits the server's |
The macOS boundary is much wider and suits development; serve production traffic from Linux.