Trellis agents run as Claude Code CLI subprocesses. Without hardening,
implementation and release agents have unrestricted access — they could
read ~/.ssh, access the macOS Keychain, or modify arbitrary files.
The security model applies defense-in-depth across six layers:
| Layer | Mechanism | What it does |
|---|---|---|
| L1: Process isolation | nono (Seatbelt/Landlock) | Kernel-enforced filesystem, network, command restrictions — irreversible |
| L2: Credential proxy | nono proxy | Agent never sees raw API keys; localhost proxy injects auth upstream |
| L3: Tool policy | SDK can_use_tool |
Per-tool-call allow/deny with descriptive errors at the SDK layer |
| L4: Observability | SDK PostToolUse hooks | Audit log of every SDK tool invocation to pool/audit.jsonl |
| L5: Attack surface | Registry tool lists | Remove tools agents don't need (no Bash/Agent in ideation) |
| L6: Supply chain | agent-sign + Sigstore | Cryptographic attestation of agent instruction files |
# Python package (nono-py)
pip install nono-py
# nono CLI (macOS)
brew install always-further/tap/nono
# nono CLI (Linux or from source)
cargo install nonoSandbox defaults to sandbox_enabled: false so existing deployments
are unaffected. Enable per-agent in the registry or via the web UI.
Kernel-level restrictions using Seatbelt on macOS and Landlock on Linux. Irreversible once applied to a process — even if the agent is compromised, it cannot escape its filesystem/network restrictions.
- Filesystem: per-role read/write paths, everything else denied
- Network: allowlist-based host filtering via
--allow-proxy - Commands: destructive commands (sudo, osascript, launchctl) blocked
- Profile generated by
trellis/core/sandbox.pyat agent startup - Profile written to
pool/sandbox-profiles/<role>-<idea-id>.json
Agents never receive raw API keys or tokens in their environment. nono's proxy intercepts outbound connections and injects auth headers before forwarding to upstream services.
- Configure via
sandbox_proxy_credentials(e.g.["anthropic", "github"]) - Credentials loaded from system keychain or 1Password before sandbox locks
sandbox_credential_mapsmaps credential URIs to env vars
Defense-in-depth that catches violations before they hit the OS.
Implemented in trellis/core/tool_policy.py.
Benefits over kernel-only enforcement:
- Agent receives a descriptive error instead of a cryptic EPERM
- Can course-correct (e.g. try a different path)
- Catches SDK-level path traversal attempts before they reach the kernel
Bash blocklist (applies to all roles):
security find-generic-password/dump-keychain— keychain accesssudo/su— privilege escalationosascript— AppleScript (can exfiltrate via UI)launchctl— launchd manipulationrm -rf //rm -rf ~— destructive filesystem opspkill/killall— process termination
Directory scoping (per-role, enforced for Read/Write/Edit/Glob/Grep):
| Role | Read dirs | Write dirs |
|---|---|---|
| implementation | project_root, blackboard/ | workspace/{id}, blackboard/{id} |
| ideation | project_root, blackboard/ | blackboard/{id} |
| validation | project_root, blackboard/ | blackboard/{id} |
| release | project_root, blackboard/ | workspace/{id}, blackboard/{id} |
| watchers | blackboard/ | (none — MCP only) |
Additional paths can be added via sandbox_extra_read_paths and
sandbox_extra_write_paths in the agent config.
Two complementary audit trails:
SDK audit (pool/audit.jsonl):
- Implemented in
trellis/core/audit.py - Logs Read, Write, Edit, Glob, Grep, WebSearch, WebFetch tool calls
- Format: newline-delimited JSON, append-only
nono audit (nono's built-in store):
- Logs Bash commands with timing, exit codes, network events
- Query via
nono audit listandnono audit show <ID> --json - Broader coverage for OS-level activity
Tool lists in registry.yaml restrict what tools each agent can invoke.
| Role | Removed | Rationale |
|---|---|---|
| ideation | Bash |
No local execution needed — use WebSearch/WebFetch |
| ideation | Agent |
Prevents privilege escalation via subagent spawning |
| watchers | Write |
Read-only; feedback goes through register_feedback MCP tool |
Agent instruction files are cryptographically signed in CI via Sigstore (keyless OIDC signing through GitHub Actions → Fulcio CA → Rekor log). nono verifies signatures at runtime before allowing the agent to read instruction files.
Files covered:
agents/*/.claude/CLAUDE.mdtrellis/agents/*/prompt.pytrellis/agents/global-system-prompt.mdregistry.yaml
Trust policy is in trust-policy.json. Enable per-agent with
sandbox_verify_attestations: true (requires CI workflow setup).
┌─────────────────┐
│ Orchestrator │
│ (Python process) │
│ │
│ • MCP servers │
│ • TG bot token │
│ • Keychain mirror│
└───────┬─────────┘
│ spawns via SDK
┌───────▼─────────┐
│ nono sandbox │
│ (nono run ...) │
│ │
│ • fs restricted │
│ • net filtered │
│ • no raw creds │
└───────┬─────────┘
│ proxied
┌───────▼─────────┐
│ nono proxy │──→ api.anthropic.com
│ (injects auth) │──→ github.com
└─────────────────┘
The orchestrator's MCP tools (blackboard, telegram, evolution) run in-process — agent communicates with them over the SDK control protocol. This means:
- Telegram bot token stays in orchestrator memory only
- Blackboard writes are mediated by Python code, not raw filesystem
- Evolution tools access knowledge dirs through the orchestrator
| Role | Sandbox | SSH | Rollback | Proxy Creds | Allowed Hosts | Bash |
|---|---|---|---|---|---|---|
| ideation | ✗ (off) | ✗ | ✗ | anthropic | web (unrestricted) | ✗ |
| implementation | ✗ (off) | ✓ | ✓ | anthropic, github | github.com, npm | ✓ |
| validation | ✗ (off) | ✗ | ✗ | anthropic | ✗ | ✓ |
| release | ✗ (off) | ✓ | ✓ | anthropic, github | github.com, pypi | ✓ |
| watchers | ✗ (off) | ✗ | ✗ | anthropic | web (unrestricted) | ✗ |
Sandbox is off by default (sandbox_enabled: false) to avoid breaking
existing deployments. Enable it per-agent once nono is installed.
All sandbox settings are configurable per-agent:
- Web UI: Agents → Edit → Security card
- YAML: Add sandbox fields directly to
registry.yaml - Wizard: LLM suggests appropriate settings based on agent description
See agents.md for the full field reference.