Skip to content

Security: terraboops/trellis

Security

docs/security.md

Security Model

Overview

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

Requirements

# 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 nono

Sandbox defaults to sandbox_enabled: false so existing deployments are unaffected. Enable per-agent in the registry or via the web UI.

Layers

L1: Process Isolation (nono)

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.py at agent startup
  • Profile written to pool/sandbox-profiles/<role>-<idea-id>.json

L2: Credential Proxy

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_maps maps credential URIs to env vars

L3: SDK Tool Policy (can_use_tool)

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 access
  • sudo / su — privilege escalation
  • osascript — AppleScript (can exfiltrate via UI)
  • launchctl — launchd manipulation
  • rm -rf / / rm -rf ~ — destructive filesystem ops
  • pkill / 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.

L4: Observability

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 list and nono audit show <ID> --json
  • Broader coverage for OS-level activity

L5: Attack Surface Reduction

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

L6: Instruction Attestation (agent-sign)

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.md
  • trellis/agents/*/prompt.py
  • trellis/agents/global-system-prompt.md
  • registry.yaml

Trust policy is in trust-policy.json. Enable per-agent with sandbox_verify_attestations: true (requires CI workflow setup).

Credential Flow

                            ┌─────────────────┐
                            │   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

Per-Role Defaults

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.

Configuration

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.

There aren’t any published security advisories