Permission System
Spacebot uses defense-in-depth to contain what worker processes can do when executing arbitrary shell commands and subprocesses. The permission system operates at multiple layers: OS-level filesystem sandboxing, application-level path validation, secret leak detection, and OpenCode permission modes.Sandbox Configuration
Worker processes run inside OS-level filesystem containment. On Linux, bubblewrap creates a mount namespace where the entire filesystem is read-only except the agent’s workspace and explicitly configured writable paths. On macOS,sandbox-exec enforces equivalent restrictions via SBPL profiles.
Per-Agent Sandbox
Configure sandbox settings in your agent definition:Sandbox enforcement mode:
enabled— OS-level containment (default, recommended)disabled— No containment, full host access (use with caution)
Additional writable directories beyond the agent’s workspace. Paths must exist at startup or they will be ignored with a warning.
Default Permissions
When sandbox mode isenabled:
Read-only access
Read-only access
- Entire host filesystem is read-only by default
/devis mounted with standard device nodes/procis mounted fresh (Linux only, if supported)/tmpis a private tmpfs per invocation
Write access
Write access
- Agent workspace directory (always writable)
- Paths listed in
writable_paths(if they exist) - Private
/tmpdirectory
Protected paths (read-only override)
Protected paths (read-only override)
- Agent data directory (contains databases and state)
- Identity files (
SOUL.md,IDENTITY.md,USER.md)
Backend Detection
Spacebot automatically detects the available sandbox backend at agent startup:- Linux: Checks for
bwrap(bubblewrap) in PATH. Also probes for/procmount support. - macOS: Checks for
/usr/bin/sandbox-exec. - Other platforms: No sandbox backend available.
mode = "enabled" but no backend is detected, Spacebot logs a warning and runs processes unsandboxed. You can disable the warning by setting mode = "disabled".
Verification
Check the agent logs at startup to confirm sandbox status:Application-Level Protections
Even when the sandbox is disabled, Spacebot enforces several application-level restrictions:Workspace Isolation
File tools (read, write, list) canonicalize all paths and reject anything outside the agent’s workspace. Symlinks that escape the workspace are blocked.
Identity File Protection
Writes to identity files are blocked at the application level, even if the sandbox allows it:SOUL.mdIDENTITY.mdUSER.md
Secret Leak Detection
A hook scans every tool argument before execution and every tool result after execution for secret patterns:Detected patterns
Detected patterns
- API keys (prefixes like
sk-,Bearer, etc.) - OAuth tokens
- PEM private keys (
-----BEGIN PRIVATE KEY-----) - Base64-encoded secrets
- Hex-encoded secrets
- URL-encoded secrets
Library Injection Blocking
Theexec tool blocks dangerous environment variables that could hijack child process loading:
LD_PRELOAD(Linux)DYLD_INSERT_LIBRARIES(macOS)NODE_OPTIONS(Node.js)PYTHONPATH(Python)
SSRF Protection
The browser tool blocks requests to cloud metadata endpoints and private IP ranges:Blocked destinations
Blocked destinations
- Cloud metadata endpoints:
169.254.169.254(AWS, Azure, GCP)metadata.google.internal(GCP)
- Private IP ranges:
10.0.0.0/8172.16.0.0/12192.168.0.0/16
- Loopback and link-local:
127.0.0.0/8::1169.254.0.0/16fe80::/10
OpenCode Permissions
When using OpenCode workers, you control which tools the OpenCode agent can use via permission modes. These settings are passed to OpenCode’s config system and enforced by OpenCode itself.Permission mode for OpenCode file edit operations:
allow— Permit all file edits without promptingreject— Block all file edits (return error to LLM)ask— Prompt for approval (headless mode rejects)
Permission mode for OpenCode shell commands:
allow— Permit all shell commands without promptingreject— Block all shell commands (return error to LLM)ask— Prompt for approval (headless mode rejects)
Permission mode for OpenCode web requests:
allow— Permit all web requests without promptingreject— Block all web requests (return error to LLM)ask— Prompt for approval (headless mode rejects)
Headless Operation
Spacebot runs OpenCode in headless mode (no interactive TUI). When a permission is set toask, OpenCode will reject the action because there’s no interactive user to prompt. Use allow or reject for headless operation.
Messaging Permissions
Messaging adapters support per-platform permission filters that control which guilds, workspaces, channels, and users can interact with the bot. These are derived from yourbindings configuration and applied at message ingress.
Discord Permissions
Discord permission filters
Discord permission filters
- Guild filter: If any binding specifies
guild_id, only messages from those guilds are processed. - Channel filter: If a binding specifies
channel_ids, only messages from those channels (or threads parented to those channels) are processed. - DM allowed users: Union of
messaging.discord.dm_allowed_usersand allbindings[].dm_allowed_usersfor Discord bindings. - Mention requirement: When
require_mention = true, guild messages must @mention the bot or reply to a bot message. - Bot messages:
allow_bot_messages = trueprocesses messages from other bots (self-messages always ignored).
Slack Permissions
Slack permission filters
Slack permission filters
- Workspace filter: If any binding specifies
workspace_id, only messages from those workspaces are processed. - Channel filter: If a binding specifies
channel_ids, only messages from those channels are processed. - DM allowed users: Union of
messaging.slack.dm_allowed_usersand allbindings[].dm_allowed_usersfor Slack bindings.
Hot-Reloadable Permissions
Messaging permission filters are hot-reloadable. When you updateconfig.toml and the file watcher detects the change, Spacebot rebuilds the permission filters and swaps them in without restarting the messaging adapters. No downtime, no reconnection.
This applies to:
- Discord guild/channel filters and DM allowed users
- Slack workspace/channel filters and DM allowed users
- Bindings (routing changes take effect immediately)
Security Best Practices
Always use sandbox mode
Always use sandbox mode
Keep
sandbox.mode = "enabled" (default) unless you have a specific reason to disable it. The sandbox is the primary defense against filesystem escape.Minimize writable paths
Minimize writable paths
Only add paths to
writable_paths if the agent genuinely needs to write there. Every writable path is an additional attack surface.Use environment variables for secrets
Use environment variables for secrets
Never hardcode API keys, tokens, or passwords in
config.toml. Use env:VAR_NAME references so secrets never touch the filesystem.Restrict DM access
Restrict DM access
Only add user IDs to
dm_allowed_users for users you trust. DMs bypass guild/channel filters.Require mentions in public channels
Require mentions in public channels
Set
require_mention = true for Discord guild bindings to prevent the bot from responding to every message.Monitor worker logs
Monitor worker logs
Set
worker_log_mode = "all_combined" or "all_separate" to log all worker activity for audit purposes. Review logs periodically for suspicious activity.Disable browser.evaluate_enabled
Disable browser.evaluate_enabled
Keep
browser.evaluate_enabled = false (default) unless you specifically need JavaScript evaluation. This blocks arbitrary code execution in the browser context.Troubleshooting
Sandbox backend not detected
Symptom: Warning at startup:sandbox mode is enabled but no backend available
Solution:
- Linux: Install bubblewrap:
sudo apt install bubblewrap(Debian/Ubuntu) orsudo dnf install bubblewrap(Fedora) - macOS:
/usr/bin/sandbox-execshould exist by default. Check that it’s executable. - Other platforms: Set
sandbox.mode = "disabled"to silence the warning.
Worker writes blocked outside workspace
Symptom: File tool returns error:path outside workspace
Solution: Add the target directory to agents[].sandbox.writable_paths if the agent legitimately needs to write there.
OpenCode actions blocked
Symptom: OpenCode returns permission errors foredit, bash, or webfetch tools
Solution: Check defaults.opencode.permissions (or per-agent overrides). Change reject or ask to allow for the relevant tool.
Discord messages ignored
Symptom: Bot doesn’t respond to messages in a guild/channel Solution:- Verify
guild_idmatches the Discord guild ID (right-click guild → Copy ID with developer mode enabled) - If using
channel_ids, verify the channel ID is in the list - If
require_mention = true, verify messages @mention the bot or reply to a bot message - Check that the binding’s
agent_idmatches an existing agent
Slack messages ignored
Symptom: Bot doesn’t respond to messages in a workspace/channel Solution:- Verify
workspace_idmatches the Slack team ID - If using
channel_ids, verify the channel ID is in the list - Check that the bot is invited to the channel (public channels) or workspace (private channels)
- Verify the binding’s
agent_idmatches an existing agent