What is Seatbelt?
Seatbelt is macOS’s mandatory access control (MAC) framework. It’s the same technology that sandboxes App Store applications and Safari. Seatbelt policies are enforced by the XNU kernel - they cannot be bypassed by userspace code.How nono Uses Seatbelt
nono generates a Seatbelt profile (a Scheme-like DSL) based on your capability flags, then callssandbox_init() to apply it.
Profile Structure
A nono-generated Seatbelt profile looks like:System Paths
nono allows read access to system paths required for running executables:| Path | Purpose |
|---|---|
/usr | System binaries and libraries |
/bin | Core utilities |
/System | macOS system files |
/Library | System-wide application support |
/Applications | Installed applications |
/private/var/db | System databases |
Library Access
macOS applications often need access to~/Library:
| Path | Access | Purpose |
|---|---|---|
~/Library | Read | Application preferences, caches |
~/Library/Caches | Read+Write | Application caches |
~/Library/Logs | Read+Write | Application logs |
Sensitive Paths
nono explicitly denies access to credential storage, even if a parent directory is allowed:Network Control
Network access is allowed by default and controlled with a simple allow/deny:Granular Filtering Limitations
While Seatbelt theoretically supports some network filtering capabilities (by port, protocol, etc.), it does not provide native per-hostname or per-domain filtering. This is a fundamental limitation of the Seatbelt framework. Implementing granular network filtering would require one of these approaches:- IP allowlists - Resolve domains to IPs before sandboxing, then use Seatbelt’s IP filtering. This is fragile (IPs change, CDNs use many IPs, DNS resolution happens outside sandbox).
- Application-layer proxy - Run a filtering proxy that applications connect through. This adds complexity, requires proxy-aware applications, and the proxy itself runs with elevated permissions.
- Packet filtering integration - Integrate with macOS’s packet filter (pf) framework. This requires root privileges and conflicts with nono’s design goal of not requiring sudo.
--net-block to disable entirely). Granular filtering support may be explored in future releases but requires careful experimentation.
Irreversibility
Oncesandbox_init() is called, the restrictions are permanent:
- There is no
sandbox_remove()orsandbox_expand()API - The process cannot modify its own sandbox
- All child processes inherit the restrictions
- The only way to escape is to exploit a kernel vulnerability
Debugging
If a command fails with permission errors:- Run with
--dry-runto see what capabilities would be granted - Run with
-vvvfor verbose logging - Check Console.app for sandbox violation logs:
- Filter by “sandbox” or your process name
- Violations show the exact path and operation blocked
