Skip to content

How to: Learn a Sandbox Policy

You don't have to guess which paths an agent needs. Run it once under nono learn, collect the read/readwrite list, and bake it into the Agentfile. Two commands. Source: src/commands/learn.rs.

  • ostk built with the sandbox feature. Without it, ostk learn exits with the error "ostk learn requires the sandbox feature — rebuild with: cargo build --features sandbox". Source: learn.rs:16–25.
  • The nono binary on PATH. ostk learn shells out to nono learn -- <cmd> and parses its output. If nono is missing you get "nono learn failed (is nono installed?)". Source: learn.rs:164–171.
  • An Agentfile you want to sandbox. Either a path (agents/worker.af) or a bare name the kernel can resolve (worker).
01
Start from an Agentfile with no sandbox directives

No LIMIT sandbox.read or sandbox.readwrite yet. The agent currently runs at ISOLATION none — it can touch anything. That's the starting point ostk learn improves on.

02
Observe

ostk learn agents/worker.af runs the canonical spawn command (ostk run agents/worker.af) under nono learn. nono traces every open, read, and write syscall the process makes. An optional --duration <secs> caps the observation window. Source: learn.rs:30–90, 121–134.

03
Review the discovered directives

Output is printed to stdout. Two lines per unique path: "LIMIT sandbox.read <path>" or "LIMIT sandbox.readwrite <path>". ostk learn deduplicates against existing LIMIT sandbox entries in the Agentfile so rerunning against an already-learned file only shows new paths. Source: learn.rs:78–85.

04
Apply (optional)

Rerun with --apply to append the new directives directly to the Agentfile. Without --apply the directives are printed only, so you can pipe them, diff them, or paste them by hand. Source: learn.rs:90–95.

05
Switch to ISOLATION nono

Add ISOLATION nono to the Agentfile. Now the next ostk run spawns the agent into a Landlock (Linux) or Seatbelt (macOS) sandbox restricted to exactly the paths ostk learn observed. Source: /docs/security/#capability-pins-profiles.

06
Audit confirms

ostk learn emits learn.started and learn.completed events to .ostk/journal.jsonl with the directive count. Useful when the same Agentfile is learned from multiple machines and you want to see which run produced which rule set. Source: learn.rs:48–56, 80–87.

discovery run
$ ostk learn agents/worker.af --duration 30
ostk learn: observing agents/worker.af via nono learn
ostk learn: command = ostk run agents/worker.af
(agent runs to completion or 30s timeout)
ostk learn: discovered 4 new directive(s)
LIMIT sandbox.read /usr/lib/libSystem.B.dylib
LIMIT sandbox.read /etc/ssl/cert.pem
LIMIT sandbox.readwrite /tmp/worker-scratch
LIMIT sandbox.readwrite ./src
apply + lock down
$ ostk learn agents/worker.af --apply
ostk learn: appended directives to agents/worker.af
$ # add ISOLATION nono by hand
$ echo "ISOLATION nono" >> agents/worker.af
$ ostk run agents/worker.af
(now sandboxed — any access outside the learned set returns EACCES)

Caveats

  • Observation coverage is only as good as the run. If the agent never needed /opt/homebrew/bin/node during the learn session but it does on a cold start, the learned policy will block the cold start. Run the agent through its real workload, not a trivial smoke test.
  • Non-zero exit codes are expected. nono itself passes through the observed process's exit status. ostk learn only fails if nono itself can't start. Source: learn.rs:173–183.
  • Sensitive paths still appear in the output. If the agent accidentally reads ~/.ssh/id_rsa during observation, that path shows up in the discovered directives. Review before --apply.
  • Deduplication is exact-path only. If the Agentfile already has LIMIT sandbox.read /usr and observation finds /usr/lib/libSystem.B.dylib, the specific path will still be added. Existing substring coverage is not deduced. Source: learn.rs:75–78.