Human Summary
I think this would be a nice to have for users who want to give local device access for features like iMessage and so forth to their agent. Maybe you don't want someone you trust in a group chat/channel being able to easily discover everything on your device. I'm assuming most users who share access to a family member(s) or close colleagues, these folks won't be as technical to totally understand how to prompt inject their way around these access controls. Something like https://github.com/Dicklesworthstone/acip/tree/main/integrations/clawdbot could also be added by user to add more protection.
But hopefully, something like this would act as a 1st layer of defense.
Summary
Add granular path-based access control that can deny or allow specific file paths/patterns for read, write, edit, and bash operations. Supports global rules with per-agent overrides.
Motivation
Currently Clawdbot has:
- Tool-level allow/deny — block entire tools (e.g.,
deny: ["write"])
- Sandbox root containment — prevent escaping sandbox directory
What's missing: No way to restrict access to specific paths within the allowed scope. Users cannot:
- Block
~/.ssh/** while allowing everything else
- Deny
.env files globally but allow a specific agent to access them
- Protect
~/Documents/taxes/** from all agents
Proposed Design
Config Schema
// ~/.clawdbot/clawdbot.json
{
access: {
// Self-contained rules, each with its own effect + pattern + operations
rules: [
{ deny: "~/.ssh/**", ops: ["*"] },
{ deny: "~/.gnupg/**", ops: ["*"] },
{ deny: "**/.env", ops: ["read", "write", "edit"] },
{ deny: "**/secrets/**", ops: ["*"] },
// Specific allows can override denies
{ allow: "~/.ssh/config", ops: ["read"] },
],
// How to resolve conflicts (default: "deny-wins")
mode: "deny-wins"
},
routing: {
agents: {
main: {
// Agent-specific rules (evaluated before global)
access: {
rules: [
{ deny: "~/work/client/**", ops: ["*"] }
]
}
},
"secrets-manager": {
// This agent CAN access globally-denied paths
access: {
rules: [
{ allow: "**/.env", ops: ["read", "write", "edit"] },
{ allow: "~/.gnupg/**", ops: ["read"] }
]
}
}
}
}
}
Shorthand Syntax
For common cases:
{
access: {
// Shorthand: string array = deny all ops for these patterns
deny: ["~/.ssh/**", "~/.gnupg/**", "**/.env"],
// Fine-grained rules when needed
rules: [
{ allow: "~/.ssh/config", ops: ["read"] }
]
}
}
Pattern Syntax
- Glob (default):
~/.ssh/**, *.env, **/secrets/*
- Regex (wrapped in slashes):
/\.env$/, /secrets\//
Operations
| Op |
Affected Tools |
read |
read tool |
write |
write tool |
edit |
edit tool |
bash |
bash tool (heuristic path extraction) |
* |
All of the above |
Resolution Logic (deny-wins mode)
For a given (agent, path, operation):
1. If agent rule explicitly ALLOWs → ALLOW (agent override)
2. If agent rule explicitly DENYs → DENY
3. If global rule DENYs → DENY
4. Otherwise → ALLOW
Alternative first-match mode evaluates rules top-to-bottom, first match wins.
Implementation Plan
Phase 1: Clawdbot Tool Wrapper (this PR)
Extend the existing wrapSandboxPathGuard() pattern in pi-tools.ts:
function wrapAccessGuard(
tool: AnyAgentTool,
accessRules: AccessRule[],
operation: AccessOperation
): AnyAgentTool {
return {
...tool,
execute: async (toolCallId, args, signal, onUpdate) => {
const filePath = extractPathFromArgs(args);
if (filePath) {
const result = evaluateAccess(filePath, operation, accessRules);
if (result.denied) {
return {
content: [{ type: "text", text: `Access denied: ${result.reason}` }],
isError: true,
};
}
}
return tool.execute(toolCallId, args, signal, onUpdate);
},
};
}
Phase 2: Bash Path Extraction
Extract and check paths from bash commands against deny rules:
| Pattern |
Examples |
| File commands |
cat, less, head, tail, grep, sed, rm, mv, cp, chmod |
| Redirects |
> file, >> file, < file |
| Path-like args |
Anything starting with /, ~/, ./, ../ |
Best-effort — see Limitations section for bypass vectors.
Phase 3: Future — Pi Extension (upstream)
Pi's extension API already has tool_call event with blocking:
api.on("tool_call", (event, ctx) => {
const path = event.input.path;
if (isPathDenied(path)) {
return { block: true, reason: "Access denied by policy" };
}
});
Could propose this as a Pi core feature. Clawdbot would then just inject config.
Files to Modify
| File |
Change |
src/agents/sandbox.ts |
Add AccessRule type, evaluateAccess() function |
src/config/types.ts |
Add AccessConfig to agent and global config |
src/config/zod-schema.ts |
Add Zod schema for access rules |
src/agents/pi-tools.ts |
Add wrapAccessGuard(), apply to read/write/edit tools |
src/agents/bash-tools.ts |
Add path extraction heuristics for bash commands |
docs/gateway/security.md |
Document access control feature |
docs/gateway/configuration.md |
Add config reference |
Existing Patterns to Follow
The codebase already has clean patterns for this:
Tool wrapping (pi-tools.ts:472-488):
function wrapSandboxPathGuard(tool: AnyAgentTool, root: string): AnyAgentTool {
return {
...tool,
execute: async (toolCallId, args, signal, onUpdate) => {
const filePath = record?.path;
if (typeof filePath === "string" && filePath.trim()) {
await assertSandboxPath({ filePath, cwd: root, root });
}
return tool.execute(toolCallId, args, signal, onUpdate);
},
};
}
Policy checking (pi-tools.ts:454-469):
function isToolAllowedByPolicy(name: string, policy?: SandboxToolPolicy) {
if (!policy) return true;
const deny = new Set(normalizeToolNames(policy.deny));
const allow = allowRaw.length > 0 ? new Set(allowRaw) : null;
if (deny.has(normalized)) return false;
if (allow) return allow.has(normalized);
return true;
}
Limitations & Threat Model
This is a policy boundary, not a security sandbox.
| Protects Against |
Does NOT Protect Against |
| Accidental access (model mistakes) |
Encoded/obfuscated paths (base64, xxd) |
| Clear policy signal to model |
Interpreter bypass (python -c "open(...)") |
| Audit trail of denied attempts |
Multi-step attacks (write script → execute) |
| Defense in depth |
Network exfiltration |
Bash extraction is best-effort. We parse common patterns (cat, less, grep, redirects, rm, mv, cp) but cannot catch:
- Creative commands (
dd if=..., tar, custom binaries)
- Scripting language one-liners
- Commands written to a script then executed
For true isolation: Use Docker sandbox with workspaceAccess: "none" and network restrictions. Access control complements sandboxing—it doesn't replace it.
Open Questions
-
Default blocks? Ship with ~/.ssh, ~/.gnupg, .env, secrets/ blocked by default? (with useDefaults: false escape hatch)
-
Error message verbosity — Show blocked path in error? Or keep vague?
- Verbose:
"Access denied: ~/.ssh/id_rsa blocked by policy"
- Vague:
"Access denied by policy"
References
- AWS IAM policy model (Effect + Action + Resource per statement)
- Kubernetes RBAC (verbs + resources per rule)
- Existing
SandboxToolPolicy pattern in this codebase
Human Summary
I think this would be a nice to have for users who want to give local device access for features like iMessage and so forth to their agent. Maybe you don't want someone you trust in a group chat/channel being able to easily discover everything on your device. I'm assuming most users who share access to a family member(s) or close colleagues, these folks won't be as technical to totally understand how to prompt inject their way around these access controls. Something like https://github.com/Dicklesworthstone/acip/tree/main/integrations/clawdbot could also be added by user to add more protection.
But hopefully, something like this would act as a 1st layer of defense.
Summary
Add granular path-based access control that can deny or allow specific file paths/patterns for read, write, edit, and bash operations. Supports global rules with per-agent overrides.
Motivation
Currently Clawdbot has:
deny: ["write"])What's missing: No way to restrict access to specific paths within the allowed scope. Users cannot:
~/.ssh/**while allowing everything else.envfiles globally but allow a specific agent to access them~/Documents/taxes/**from all agentsProposed Design
Config Schema
Shorthand Syntax
For common cases:
Pattern Syntax
~/.ssh/**,*.env,**/secrets/*/\.env$/,/secrets\//Operations
readreadtoolwritewritetooleditedittoolbashbashtool (heuristic path extraction)*Resolution Logic (
deny-winsmode)Alternative
first-matchmode evaluates rules top-to-bottom, first match wins.Implementation Plan
Phase 1: Clawdbot Tool Wrapper (this PR)
Extend the existing
wrapSandboxPathGuard()pattern inpi-tools.ts:Phase 2: Bash Path Extraction
Extract and check paths from bash commands against deny rules:
cat,less,head,tail,grep,sed,rm,mv,cp,chmod> file,>> file,< file/,~/,./,../Best-effort — see Limitations section for bypass vectors.
Phase 3: Future — Pi Extension (upstream)
Pi's extension API already has
tool_callevent with blocking:Could propose this as a Pi core feature. Clawdbot would then just inject config.
Files to Modify
src/agents/sandbox.tsAccessRuletype,evaluateAccess()functionsrc/config/types.tsAccessConfigto agent and global configsrc/config/zod-schema.tssrc/agents/pi-tools.tswrapAccessGuard(), apply to read/write/edit toolssrc/agents/bash-tools.tsdocs/gateway/security.mddocs/gateway/configuration.mdExisting Patterns to Follow
The codebase already has clean patterns for this:
Tool wrapping (
pi-tools.ts:472-488):Policy checking (
pi-tools.ts:454-469):Limitations & Threat Model
This is a policy boundary, not a security sandbox.
base64,xxd)python -c "open(...)")Bash extraction is best-effort. We parse common patterns (
cat,less,grep, redirects,rm,mv,cp) but cannot catch:dd if=...,tar, custom binaries)For true isolation: Use Docker sandbox with
workspaceAccess: "none"and network restrictions. Access control complements sandboxing—it doesn't replace it.Open Questions
Default blocks? Ship with
~/.ssh,~/.gnupg,.env,secrets/blocked by default? (withuseDefaults: falseescape hatch)Error message verbosity — Show blocked path in error? Or keep vague?
"Access denied: ~/.ssh/id_rsa blocked by policy""Access denied by policy"References
SandboxToolPolicypattern in this codebase