docs · v0.1.0

Documentation.

Everything you need to install troopr, sandbox an AI coding agent, and write a troopr.yaml that fits your project.

Quick start

Three commands to go from clone to first protected agent run.

# 1. clone & build (private alpha - see Installation for the GOPRIVATE setup)
$ git clone git@github.com:paanSinghCoder/troopr.git
$ cd troopr && go build -o /usr/local/bin/troopr ./cmd/troopr

# 2. sanity check
$ troopr version
0.1.0

# 3. run your agent inside the sandbox
$ cd ~/code/my-project
$ troopr run claude

That's it. Your agent runs exactly as it always has - same stdio, same environment, same arguments. The only difference: reads of .env, ~/.ssh, ~/.aws/credentials and the rest of the universal denylist now return Operation not permitted. Agent flags pass through: troopr run agent --model auto.


Installation

Requirements

  • macOS 13 or newer (Apple Silicon or Intel)
  • Go 1.22+ (only required to build from source)
  • A working sandbox-exec at /usr/bin/sandbox-exec (ships with macOS)

One-time setup for the private module

The GitHub repo is currently private. Until it's public, both git clone and go install need authenticated access to github.com/paanSinghCoder/troopr.

# tell Go not to route this module through the public proxy / sumdb
$ go env -w GOPRIVATE=github.com/paanSinghCoder/*
$ go env -w GONOSUMCHECK=github.com/paanSinghCoder/*

# make Go use SSH for github.com clones
$ git config --global url."git@github.com:".insteadOf "https://github.com/"

You'll also need an SSH key loaded and attached to a GitHub account with read access to the repo. (Or a personal access token with repo scope in ~/.netrc, if you prefer HTTPS.)

Build from source

$ git clone git@github.com:paanSinghCoder/troopr.git
$ cd troopr
$ go build -o /usr/local/bin/troopr ./cmd/troopr

The output is a single static binary of about 5MB. No runtime dependencies - it's pure Go standard library plus cobra (CLI parser) and yaml.v3 (config loader).

Verify

$ troopr version
0.1.0
$ troopr check | head -5
# Resolved config
cwd:            ...
home:           ...
deny:

Your first sandbox

Before pointing troopr at a real project, run a 30-second smoke test to confirm the deny is actually working on your machine.

$ mkdir /tmp/troopr-demo && cd /tmp/troopr-demo
$ echo "SECRET=hunter2" > .env
$ echo "ok" > public.txt

# reading a normal file works
$ troopr run /bin/cat public.txt
ok

# reading .env is blocked
$ troopr run /bin/cat .env
cat: .env: Operation not permitted

If the second command prints hunter2 instead of the permission error, see Troubleshooting.


Commands

Seven subcommands. Run troopr --help for the in-CLI version.

troopr run

troopr run [--config <path>] <cmd> [args...]

Launch a command inside the sandbox. Agent flags pass through - e.g. troopr run agent --model auto.

troopr login

troopr login [--agent <binary>]

Log in to your agent. Runs outside the deny-list wrapper so auth flows work normally.

troopr init

troopr init

Write a sample troopr.yaml to the current directory. Will not overwrite an existing file.

troopr config init

troopr config init

Write a sample ~/.troopr/config.yaml with global deny/allow defaults.

troopr check

troopr check [--config <path>]

Dry run. Prints the resolved configuration and the sandbox profile (.sb) that would be generated, without executing anything.

troopr log

troopr log [-f|--follow]

Tail ~/.troopr/log.jsonl. Each line is a JSON record of a denied filesystem read or write.

troopr version

troopr version

Print the installed troopr version.

Flags accepted by run and check

FlagTypeDefaultEffect
--config path ./troopr.yaml Explicit path to the config file. If omitted, troopr looks for troopr.yaml or troopr.yml in the current directory.

Everything else on the run line is passed to the wrapped command. Use troopr run agent --model auto, not troopr run --model auto agent - troopr only parses --config.


Configuration

troopr.yaml lives at the root of your project. Generate a starter file with troopr init.

Schema

# troopr.yaml
version: 1                  # required, must be 1
deny:                       # patterns added on top of the built-in denylist
  - "**/.env*"
  - "**/*.pem"
  - "~/.ssh/**"
  - "src/internal/secrets/**"
allow:                      # carve-outs from deny
  - "**/.env.example"

Fields

FieldRequiredNotes
version yes Must be 1. Anything else fails with exit code 1.
deny no List of path globs added to the built-in universal denylist. Order doesn't matter.
allow no List of path globs that are explicitly permitted even if matched by a deny rule.

Glob syntax

TokenMatchesExample
**Any sequence of characters, including /.**/.env/foo/bar/.env
*Any sequence of characters except /.*.pemkey.pem
?A single non-/ character.file?.txtfile1.txt
~ / $HOMEExpanded to your home directory before matching.~/.aws/**/Users/you/.aws/foo

Merge precedence

When troopr starts, it merges configuration in this order - later sources add to earlier ones:

  1. The built-in universal denylist
  2. Anything in ~/.troopr/config.yaml (from troopr config init)
  3. Anything in ./troopr.yaml or the file passed to --config

The sandbox policy is (allow default) plus deny rules for every merged pattern. allow carve-outs override matching deny entries.


Universal denylist

Fifteen patterns always blocked on every troopr run. Add more in your troopr.yaml or ~/.troopr/config.yaml under deny:.

PatternWhy it's blocked
**/.envdotenv files at any depth
**/.env.*.env.local, .env.production, etc.
**/*.pemPEM-encoded keys and certs
**/id_rsa*OpenSSH RSA keypairs
**/id_ed25519*OpenSSH Ed25519 keypairs
~/.ssh/**everything under your SSH directory
~/.aws/credentialsAWS access keys
~/.aws/configAWS profile + region config
~/.gnupg/**GPG keyrings and trust DB
~/.kube/configKubernetes cluster credentials
~/.docker/config.jsonDocker registry auth tokens
~/.netrclegacy HTTP basic auth store
**/secrets/**anything in a secrets/ directory
**/*.kdbxKeePass 2 password databases
**/*.kdbKeePass 1 password databases

If your stack stores secrets somewhere unconventional (a ~/.config/myapp/ directory, a non-standard cloud config path, a custom token cache), add it to your deny list explicitly. The universal denylist covers the common cases; it cannot anticipate yours.


Recipes

Concrete one-liners for the agents people actually use.

Claude Code

$ cd ~/code/my-project
$ troopr run claude

Cursor agent

$ troopr run agent --model auto

Codex CLI

$ troopr run codex

Aider

$ troopr run aider

Agent login (outside sandbox)

$ troopr login
$ troopr login --agent claude

An arbitrary script or binary

$ troopr run ./my-script.sh --flag value
$ troopr run node ./scripts/refactor.js

Agent flags go after the command name. troopr only parses --config; everything else is passed to sandbox-exec verbatim.


Audit log

troopr tails the macOS unified log for sandbox denials and writes each one as a JSON line to ~/.troopr/log.jsonl.

Format

{"timestamp":"2026-05-14T03:30:26Z","agent":"claude","path":"/Users/g/.aws/credentials","action":"deny"}
FieldValue
timestampRFC3339 UTC.
agentThe first argument passed after -- (typically the agent binary name).
pathThe absolute path the kernel denied access to.
actionAlways "deny" in the current version.

Watching it live

$ troopr log -f

How it's collected

troopr spawns log stream --predicate 'eventMessage CONTAINS "Sandbox:" AND eventMessage CONTAINS "deny"' in the background while your agent runs. Each matching line is parsed and appended to ~/.troopr/log.jsonl. This is best-effort - if the unified log stream lags or drops events, the JSONL will lag too. Treat the audit log as a debug aid, not a security-of-record audit trail.


Exit codes

CodeMeaning
0Success.
1Configuration error. Invalid YAML, unknown key, missing file.
2Sandbox setup error. Could not exec sandbox-exec, profile file could not be written, etc.
nExit code of the wrapped command, propagated verbatim. A signal-killed child returns 128 + signal number.

Troubleshooting

"sandbox-exec: command not found"

You're not on macOS, or your shell can't see /usr/bin. troopr only runs on macOS 13+; there is no Linux or Windows build.

Reads of .env succeed when they shouldn't

Check that your config actually loaded. Run troopr check and confirm the deny regex for .env appears in the generated sandbox profile. If you have a troopr.yaml with an explicit allow: entry that matches your file, the carve-out wins. Carve-outs are emitted after denies precisely so they can override.

"Operation not permitted" on files I want the agent to read

Add an allow: rule for the path in troopr.yaml or ~/.troopr/config.yaml. If the path is on the universal denylist, the carve-out must match it exactly.

The audit log at ~/.troopr/log.jsonl is empty

The log stream background process needs a moment to start. Very short-lived agent runs may finish before the first deny event lands. Try the command again, or watch log show --predicate 'eventMessage CONTAINS "Sandbox:"' --last 1m to confirm denials are firing at the kernel level regardless.


Known limitations

  • macOS only. Apple's sandbox-exec has no Linux or Windows equivalent.
  • sandbox-exec is deprecated by Apple. It still ships on macOS 13–15 and works reliably; a future macOS release may force a migration to a different isolation mechanism.
  • Path-based, not content-based. A secret stored at an unusual path is readable until you add the path to deny:.
  • One policy per troopr run. There is no way to apply different rules to subprocesses spawned by the agent - they inherit the parent's policy.
  • Not a security boundary against an adversarial binary. troopr is defense-in-depth against honest mistakes (the agent reaching for ~/.aws/credentials because the LLM suggested it). A program actively trying to escape can probe TCC-permitted services, kernel bugs, or paths still reachable via mach-lookup.
  • Audit log is best-effort. It tails log stream and can miss events under load.