sandboxer.cr
Sandboxer
Very much in development.
A Crystal shard for running shell commands inside a platform-native sandbox, with a configurable access policy.
| Platform | Mechanism | Status |
|---|---|---|
| macOS | sandbox-exec + SBPL profiles (Seatbelt) |
✔️ Runs |
| Linux | bwrap (Bubblewrap) user namespaces |
❗️ Untested |
Usage as a shard
Add to your shard.yml:
dependencies:
sandboxer:
github: nogginly/sandboxer.cr
Then shards install.
Defining a policy
require "sandboxer"
policy = Sandboxer::Policy.build do |p|
p.read_only "/usr/share/myapp" # paths the process may read
p.read_write "/tmp/workspace" # paths the process may read and write
p.tmpfs "/tmp" # in-memory scratch space (Linux); RW grant (macOS)
p.allow_network = false # deny all network access
p.working_dir = "/tmp/workspace"
p.env["APP_ENV"] = "sandbox" # explicit env vars inside the sandbox
end
All fields are optional. Omitted fields default to the safest option: no network, no paths, no extra env vars.
Policies can also be loaded from a JSON file:
policy = Sandboxer::Policy.from_json(File.read("policy.json"))
{
"read_only_paths": ["/usr/share/myapp"],
"read_write_paths": ["/tmp/workspace"],
"tmpfs_paths": ["/tmp"],
"allow_network": false,
"working_dir": "/tmp/workspace",
"env": { "APP_ENV": "sandbox" }
}
Running a command
result = Sandboxer.run(["python3", "script.py"], policy)
if result.success?
puts result.stdout
else
STDERR.puts result.stderr
exit result.exit_code
end
Sandboxer.run selects the appropriate runner for the current platform automatically. Exit codes follow Unix conventions; signal exits are mapped to 128 + signal_number.
Inspecting the generated invocation
Both runners expose their native policy representation without executing, which is useful for logging, auditing, or iterating on a policy:
# Linux: print the bwrap flag list
runner = Sandboxer::Bwrap.new
puts runner.build_argv(["python3", "script.py"], policy).join(" ")
# macOS: print the SBPL profile
runner = Sandboxer::SandboxExec.new
puts runner.generate_profile(policy)
Checking runner availability
Sandboxer.platform_runners.each do |runner|
puts "#{runner.class}: #{runner.available? ? "available" : "not found"}"
end
CLI
Note: The CLI is not yet distributed as a release binary. If you need it today, build from source — see DEVELOPMENT.md. A release workflow is planned.
The CLI will supports the following subcommands:
# Run a command inside a sandbox
sandboxer run --policy policy.json -- command [args...]
# Print the native invocation without executing
sandboxer inspect --policy policy.json [--platform linux|macos]
# Check which sandbox runners are available on this host
sandboxer check
Development
See DEVELOPMENT.md for how to build, run the specs, and understand the internals.
Contributions, by invitation!
With apologies, at this time contributions are by invitation only and limited to people I know and see often.
These are early days for Sandboxer and I am busy with family and work.
At this time I want to work on this at a manageable pace.
sandboxer.cr
- 0
- 0
- 0
- 0
- 1
- about 2 hours ago
- June 13, 2026
Mozilla Public License 2.0
Sat, 13 Jun 2026 19:04:13 GMT