Security & Permissions
Penguin includes a comprehensive permission and policy engine that controls what operations the AI agent can perform. This system provides granular control over file system access, process execution, network operations, and more.
Overview
The security system operates on three core principles:
- Defense in Depth: Multiple layers of checks (mode → policy → path validation)
- Least Privilege: Agents start with minimal permissions and can be granted more
- Transparent Auditing: All permission decisions are logged for debugging and compliance
Security Modes
Penguin supports three security modes that provide different levels of access:
read_only Mode
The most restrictive mode. The agent can only read files and cannot modify the file system.
Allowed operations:
filesystem.read- Read file contentsfilesystem.list- List directory contentsmemory.*- All memory operations
Blocked operations:
- All write, delete, and execute operations
- Git operations (except read-only ones)
- Network requests
workspace Mode (Default)
A balanced mode that restricts file operations to the workspace and project directories.
Allowed operations:
- All read operations
- Write/delete within workspace and project roots
- Process execution (with approval for dangerous commands)
- Git operations within project
Blocked operations:
- File operations outside workspace/project boundaries
- System directory access (
/etc,/usr,~/.ssh, etc.) - Force push and destructive git operations (require approval)
full Mode
Minimal restrictions for trusted environments. Use with caution.
Allowed operations:
- Most operations allowed by default
- System directories still protected
Operations requiring approval:
git.force- Force pushprocess.execute- For potentially dangerous commands
Configuration
Basic Configuration
Add security settings to your config.yml:
security:
# Security mode: read_only, workspace, or full
mode: workspace
# Enable/disable permission checks entirely
enabled: true
# Additional allowed paths (beyond workspace/project)
allowed_paths:
- /path/to/shared/resources
- ~/Documents/reference
# Explicitly denied paths (overrides allowed)
denied_paths:
- ~/.ssh
- ~/.aws
- /etc
- /usr
# Operations that always require user approval
require_approval:
- filesystem.delete
- git.push
- git.force
Environment Variables
You can also control security via environment variables:
# Disable all permission checks (YOLO mode - use with caution!)
PENGUIN_YOLO=true
# Set security mode
PENGUIN_SECURITY_MODE=workspace
Runtime Configuration
Security settings can be changed at runtime via the CLI or API:
# Via CLI slash command
/config runtime set security_mode read_only
# Via API
curl -X POST http://localhost:8000/api/v1/system/config/security \
-H "Content-Type: application/json" \
-d '{"mode": "workspace", "enabled": true}'
Operation Taxonomy
The permission system uses a standardized operation taxonomy:
| Category | Operations | Description |
|---|---|---|
filesystem | read, write, delete, list, create_dir | File system operations |
process | execute, background, kill | Process management |
network | request, download, upload | Network operations |
git | read, write, commit, push, force | Git operations |
memory | read, write, delete, search | Memory system operations |
Tool to Operation Mapping
Each tool is mapped to one or more operations:
TOOL_OPERATION_MAP = {
"read_file": [Operation.FILESYSTEM_READ],
"write_file": [Operation.FILESYSTEM_WRITE],
"bash": [Operation.PROCESS_EXECUTE],
"git_push": [Operation.GIT_PUSH],
# ... etc
}
Approval Flow
When an operation returns ASK, the user must approve it before execution.
Web Interface
In the web interface, approval requests appear as interactive prompts:
{
"event": "approval_required",
"data": {
"request_id": "apr_abc123",
"tool_name": "bash",
"operation": "process.execute",
"resource": "rm -rf ./build",
"reason": "Destructive command requires approval"
}
}
API Approval
Approve or deny requests programmatically:
# Approve a request
curl -X POST http://localhost:8000/api/v1/approvals/apr_abc123/approve \
-H "Content-Type: application/json" \
-d '{"scope": "once"}'
# Deny a request
curl -X POST http://localhost:8000/api/v1/approvals/apr_abc123/deny
# Pre-approve an operation pattern
curl -X POST http://localhost:8000/api/v1/approvals/pre-approve \
-H "Content-Type: application/json" \
-d '{
"operation": "filesystem.delete",
"pattern": "./build/*",
"session_id": "sess_xyz"
}'
Approval Scopes
When approving a request, you can specify the scope:
once- Approve this single request onlysession- Approve similar operations for the current sessionpattern- Approve operations matching a resource pattern
Multi-Agent Permissions
When using multiple agents, each agent can have its own permission configuration.
Agent Permission Configuration
Define permissions in your agent personas:
# In config.yml
agents:
code-reviewer:
persona: "Code Review Expert"
permissions:
mode: read_only
operations:
- filesystem.read
- memory.read
allowed_paths:
- ./src
- ./tests
deployment-agent:
persona: "Deployment Specialist"
permissions:
mode: workspace
operations:
- filesystem.read
- filesystem.write
- process.execute
- git.push
require_approval:
- git.force
Permission Inheritance
Sub-agents inherit permissions from their parent, with refinement:
Parent (workspace mode, all operations)
└── Child (can only have equal or more restrictive permissions)
- Can reduce to read_only mode
- Can remove operations from allowed list
- Cannot add operations parent doesn't have
- Cannot access paths parent can't access
Programmatic Agent Permissions
from penguin.multi import MultiAgentCoordinator
coordinator = MultiAgentCoordinator()
# Spawn agent with restricted permissions
await coordinator.spawn_agent(
agent_id="restricted-helper",
persona="helper",
permissions={
"mode": "read_only",
"allowed_paths": ["./docs"],
"operations": ["filesystem.read", "memory.read"]
},
parent_agent_id="main-agent" # Inherits and refines parent permissions
)
Audit Logging
All permission checks are logged for debugging and compliance.
Audit Configuration
security:
audit:
enabled: true
log_file: ".penguin/permission_audit.log"
# Per-category verbosity
categories:
filesystem: all # Log everything
process: ask_and_deny # Log ASK and DENY only
network: deny_only # Log only denials
git: ask_and_deny
memory: off # No logging
# Memory buffer for API queries
max_memory_entries: 1000
# Include full context (may contain sensitive data)
include_context: false
Verbosity Levels
off- No logging for this categorydeny_only- Only log DENY resultsask_and_deny- Log ASK and DENY resultsall- Log all permission checks (ALLOW, ASK, DENY)
CLI Commands
# View current permission settings
penguin permissions list
# View recent audit log entries
penguin permissions audit -n 50
# Filter by result
penguin permissions audit --result deny
# View audit statistics
penguin permissions summary
API Endpoints
# Get recent audit entries
curl "http://localhost:8000/api/v1/security/audit?limit=100&result=deny"
# Get audit statistics
curl http://localhost:8000/api/v1/security/audit/stats
Response:
{
"total": 1250,
"by_result": {
"allow": 1180,
"ask": 45,
"deny": 25
},
"by_category": {
"filesystem": 890,
"process": 200,
"git": 160
}
}
Custom Policies
You can implement custom policies by extending the PolicyEngine base class:
from penguin.security import PolicyEngine, Operation, PermissionResult
class CustomSecurityPolicy(PolicyEngine):
"""Custom policy that restricts operations during business hours."""
def __init__(self):
super().__init__(name="business-hours-policy", priority=50)
def check_operation(
self,
operation: Operation,
resource: str,
context: dict
) -> tuple[PermissionResult, str]:
import datetime
hour = datetime.datetime.now().hour
# Restrict destructive operations outside business hours
if operation in [Operation.GIT_PUSH, Operation.FILESYSTEM_DELETE]:
if hour < 9 or hour > 17:
return (
PermissionResult.DENY,
"Destructive operations not allowed outside business hours (9 AM - 5 PM)"
)
# Defer to other policies
return (PermissionResult.ALLOW, "Within business hours")
Register your custom policy:
from penguin.security import PermissionEnforcer
enforcer = PermissionEnforcer(mode="workspace")
enforcer.add_policy(CustomSecurityPolicy())
Path Security
The permission system includes robust path validation:
Path Traversal Detection
Prevents ../ attacks and symbolic link escapes:
from penguin.security import validate_path_security
# This will raise PathTraversalError
validate_path_security(
path="../../../etc/passwd",
boundaries=["/home/user/workspace"]
)
# This will raise SymlinkEscapeError if symlink points outside boundaries
validate_path_security(
path="./link-to-secrets",
boundaries=["/home/user/workspace"],
check_symlinks=True
)
Sensitive Path Protection
These paths are always protected regardless of mode:
~/.ssh- SSH keys~/.aws- AWS credentials~/.gnupg- GPG keys/etc- System configuration/usr- System binaries- Environment files containing secrets
Best Practices
Development Environment
security:
mode: workspace
enabled: true
audit:
enabled: true
categories:
filesystem: all # Full visibility during development
Production Environment
security:
mode: read_only # or workspace with strict boundaries
enabled: true
denied_paths:
- ~/.ssh
- ~/.aws
- /etc
- /var
require_approval:
- filesystem.delete
- process.execute
- git.push
audit:
enabled: true
log_file: /var/log/penguin/permissions.log
categories:
filesystem: ask_and_deny
process: all
network: all
CI/CD Pipelines
security:
mode: workspace
enabled: true
allowed_paths:
- ./ # Current directory only
require_approval: [] # No interactive approval in CI
audit:
enabled: true
categories:
filesystem: deny_only
process: deny_only
Troubleshooting
Common Issues
Operation unexpectedly denied:
- Check audit log:
penguin permissions audit --result deny - Verify security mode:
penguin permissions list - Check path boundaries in config
Approval flow not working:
- Ensure WebSocket connection is active
- Check for
approval_requiredevents in browser console - Verify session ID matches
Agent permissions not applying:
- Check agent is registered:
GET /api/v1/agents - Verify permissions block in config
- Check parent agent permissions (inheritance rules)
Debug Logging
Enable verbose permission logging:
security:
audit:
enabled: true
categories:
filesystem: all
process: all
network: all
git: all
memory: all
include_context: true # Warning: may log sensitive data
Then check logs:
tail -f .penguin/permission_audit.log | jq .