Security
Every tool call goes through a four-layer gate. Each layer is independently configurable and has a no-op default. Pay only for what you wire up.
@before_tool hooks
│
▼
Permissions check ─── deny ──► tool result = denied
│
▼
Sandbox wrap
│
▼
Tool dispatch
│
▼
@after_tool hooks
│
▼
Audit log entryRead more
Mode.DEFAULT / ACCEPT_EDITS / BYPASS. StandardPermissions, PerUserPermissions, custom policies.Permissions and modes@agent.before_tool / @agent.after_tool decorators for inline review and logging.HooksFilesystemSandbox (path-arg validation) · SubprocessSandbox (process isolation).SandboxesHMAC-signed JSONL with per-user_id attribution. Compliance-grade.Audit log attributionThe four layers
1. Permissions
Decides yes / no / ask per tool call. Default AllowAll (no-op);
production usually wants StandardPermissions(mode=Mode.DEFAULT),
which routes destructive tools through an
approval handler.
2. Hooks
@agent.before_tool runs before the call (returning a denial
short-circuits the dispatch). @agent.after_tool runs after, with
the ToolResult. Used for inline review, redaction, custom metrics,
or whatever else doesn’t fit a standard policy.
3. Sandboxes
Wraps the tool host with isolation. FilesystemSandbox blocks path
arguments that escape declared roots (symlink-aware).
SubprocessSandbox runs each tool in a fresh child process for
crash isolation and hard timeouts.
4. Audit log
Every tool call lands in the audit log with user_id attribution.
HMAC signatures cover the entries. Tampering breaks verification.
See Audit log attribution.
Pick a starting profile
| You want… | Start here |
|---|---|
| Dev / CI / sandbox | permissions=StandardPermissions(mode=Mode.BYPASS) |
| Single-user productivity tool | mode=Mode.ACCEPT_EDITS, audit_log=FileAuditLog(...) |
| Multi-tenant SaaS | permissions=PerUserPermissions(policies=, default=), approval_handler=..., audit_log=... |
| Coding agent with FS access | wrap tools in FilesystemSandbox, mode ACCEPT_EDITS, audit on |
Defaults are fast. Agent ships with AllowAll permissions, no
hooks, NoSandbox, and audit_log=None. The agent loop detects
each layer is a no-op and skips it on the hot path. The moment you
wire a real implementation, the corresponding integration point
flips on. Same Agent class, same API, no flags.