SecurityOpen SourceFreeActiveMachine-verified· intermediate · ~15 min setup

Shepherd: prove an agent task is retained and least-privilege before it runs

Declare an agent task's per-repo read/write grants and hold its output to one side (retained, not applied), then validate that nothing auto-applies and every write grant is explicit, before you run it.

by Shilpa Mitra· verified today· v1.0.0

Run this workflow

CI-verified, 2/2 fixtures passing.

Build this with your agent

One copy-paste hands Claude Code, Codex, or Cursor the full recipe, steps included, nothing to fetch.

Intended Use

Anyone running agents who wants a real undo button and least-privilege file access. CI validates the grant manifest: apply_mode is retained (nothing auto-applies), each repo grant is read_only or read_write, and every read_write grant is explicitly acknowledged. No run, no OS call. The syscall-jail enforcement, the actual agent run, and Shepherd's alpha runtime are fenced.

Not for

  • Production, Shepherd is early alpha and the maintainers say so; APIs may change between releases, study and pilot it in a sandbox
  • Uniform sandbox guarantees, enforcement is exercised on macOS Seatbelt while Linux Landlock is container-gated, so the syscall-level protection is not the same on every machine
  • Quoting the '5x faster than docker commit, 95% KV-cache reuse' figures, those are the authors' own benchmarks; reproduce them before you cite them

The Stack

Tested Against

arxiv.org/abs/2605.10913 + shepherd-agents/shepherd (2026-07)node@20

Side effects & data flow

Network
none, local only
Writes
./grants.json
Credentials
none required

Prerequisites

  • Shepherd installed (pip install shepherd-ai) to actually run a task
  • A supported OS (macOS Seatbelt or Linux Landlock) for kernel-level enforcement

Steps

  1. 1

    Declare the grants and validate the safety posture

    Write the task's per-repo grants and set apply_mode to retained so the output is held to one side, not applied. CI checks nothing auto-applies and that every write grant is explicitly acknowledged (least privilege: repos are read-only unless you say otherwise). Running the task and the OS-level enforcement are fenced.

    cat > grants.json <<'JSON'
    {
      "task": "refactor-auth",
      "apply_mode": "retained",
      "grants": [
        { "repo": "app", "access": "read_write", "acknowledged": true },
        { "repo": "vendor", "access": "read_only" },
        { "repo": "secrets", "access": "read_only" }
      ]
    }
    JSON
    node -e '
    const fs = require("fs");
    const c = JSON.parse(fs.readFileSync("grants.json", "utf8"));
    function bad(m) { console.error("BAD: " + m); process.exit(1); }
    if (c.apply_mode !== "retained") bad("apply_mode must be retained (nothing applies until you accept it)");
    const grants = c.grants || [];
    if (grants.length < 1) bad("declare at least one repo grant");
    let ro = 0, rw = 0;
    for (const g of grants) {
      if (!g.repo) bad("each grant needs a repo");
      if (g.access !== "read_only" && g.access !== "read_write") bad("grant for " + g.repo + " must be read_only or read_write");
      if (g.access === "read_write") {
        if (g.acknowledged !== true) bad("write grant for " + g.repo + " must be explicitly acknowledged (least privilege)");
        rw++;
      } else {
        ro++;
      }
    }
    console.log("grants OK: task retained (not auto-applied); " + grants.length + " repo(s), " + ro + " read-only, " + rw + " read-write (all acknowledged)");
    '
  2. 2

    Run it, then keep or discard (the run step, not checked by CI)

    Install Shepherd (pip install shepherd-ai), shepherd init, run the task; its output is retained, not applied. Inspect the changeset (shepherd run changeset --latest), then shepherd run select to keep it or shepherd run discard to throw it away. On a supported OS the read-only grants are enforced by the kernel. The run and enforcement are fenced.

Eval, 2 fixtures

Last passed: verified today
  • grants-okcontainstimeout 30s · max $0

    Expected: grants OK: task retained (not auto-applied); 3 repo(s), 2 read-only, 1 read-write (all acknowledged)

  • clean-exitexit_codetimeout 30s · max $0

    Expected: 0

Results

Shepherd records an agent's run as a reversible, Git-like trace: the output comes back as a reviewable proposal, not a live edit, and nothing touches your files until you accept it. Permissions live in the task's function signature (read-only or read-write per repo) and on a supported OS are enforced at the native syscall jail (macOS Seatbelt, Linux Landlock), a write to a read-only repo is refused by the kernel, not just caught at a merge gate.

Did this work for you?

Our CI checks the setup runs. You tell us if the whole thing worked. Tell us straight.

Related workflows

Liked this workflow?

Get new verified workflows in WebAfterAI, three issues a week (Tue, Thu, Sat).