Skip to content

Sandbox

Reyn's sandbox layer translates the policy a skill declares into kernel-level enforcement — without any OS code knowing which skill is running. This is a direct application of P3 (OS is the runtime engine) and P7 (OS code must not contain skill-specific strings): the skill declares what it needs; the OS selects how to enforce it.

The sandbox complements the permission model: permissions enforce declared scope at dispatch time (before the op runs); the sandbox enforces the same boundaries at the system-call level while the subprocess is running. The two layers are independent and additive.

SandboxPolicy field reference

Defined in src/reyn/sandbox/policy.py. Passed as fields on a sandboxed_exec Control IR op.

Field Type Default Meaning
network bool false Allow outbound network connections
read_paths list[str] [] Filesystem paths the subprocess may read (glob patterns and {{workspace}} template OK)
write_paths list[str] [] Filesystem paths the subprocess may write
allow_subprocess bool false Allow the subprocess to spawn child processes
env_passthrough list[str] [] Environment variable names passed through to the subprocess (all others are stripped)
timeout_seconds int 60 Wall-clock limit; process is killed on expiry

Backend selection table

get_default_backend(config) selects a backend at runtime based on platform and installed extras. The sandbox.backend config key in reyn.yaml overrides automatic selection.

Platform Condition Backend Notes
macOS < 26 SeatbeltBackend SBPL profile via sandbox-exec. Deprecated upstream — Apple removing in macOS 26.
macOS ≥ 26 (future) AppleContainerBackend Not yet implemented (Component E, deferred). Falls back to NoopBackend.
Linux kernel ≥ 5.13, sandbox-linux extra installed LandlockBackend + seccomp-BPF pip install reyn[sandbox-linux] required. ABI v4+ adds network port rules.
Linux kernel < 5.13 or sandbox-linux not installed NoopBackend Audit-only; no enforcement.
Other any NoopBackend Audit-only; no enforcement.

When NoopBackend is used, Reyn logs a one-line WARN on first invocation. Set sandbox.on_unsupported: error to hard-fail instead.

macOS 26.3+ and SeatbeltBackend: sandbox-exec remains shipped in macOS 26.3. An SBPL profile that includes (import "bsd.sb") and (allow process-exec*) is sufficient for the backend to function. See the FP-0017 post-dogfood fix landing notes (commit b477508) for details.

reyn.yaml configuration

sandbox:
  backend: auto        # auto | seatbelt | landlock | noop
  on_unsupported: warn # warn | error | ignore
  • backend: auto — let Reyn pick the best available backend for the current platform (recommended).
  • backend: noop — explicitly opt out of enforcement (useful in CI environments where you audit via events but do not need enforcement).
  • on_unsupported: error — fail skill dispatch if the configured backend is unavailable. Use in production environments where enforcement is a hard requirement.

Declaring a sandbox policy in a skill

In a skill's skill.md, add a sandbox: block to any phase that runs sandboxed_exec:

# skill.md excerpt
phases:
  - name: run_script
    instructions: |
      Run the analysis script.
    sandbox:
      read_paths:
        - "{{workspace}}/input"
      write_paths:
        - "{{workspace}}/output"
      network: false
      timeout_seconds: 120

The {{workspace}} template is expanded by the OS at runtime to the skill's workspace directory. Skill authors MUST NOT hardcode absolute paths — use {{workspace}} for all workspace-relative paths and /usr/bin, /usr/lib, etc. for system paths (those are automatically allowed on backends that need them for dylib loading).

See also

  • FP-0017 — design rationale, component history, and backend implementation details.
  • Control IR: sandboxed_exec — op schema and field reference.
  • Permission model — dispatch-time declared-scope enforcement that the sandbox complements at runtime.