Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libsqlite3-0 \
curl \
gh \
bubblewrap \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /usr/local/bin/spacebot /usr/local/bin/spacebot
Expand Down
33 changes: 33 additions & 0 deletions docs/content/docs/(configuration)/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ cron_timezone = "America/Los_Angeles" # optional per-agent cron timezone overri
[agents.routing]
channel = "anthropic/claude-opus-4-20250514"

# Per-agent sandbox configuration.
[agents.sandbox]
mode = "enabled" # "enabled" (default) or "disabled"
writable_paths = ["/home/user/projects/myapp"] # additional writable directories

# Per-agent cron jobs.
[[agents.cron]]
id = "daily-check"
Expand Down Expand Up @@ -516,6 +521,34 @@ When branch/worker/cron dispatch happens before readiness is satisfied, Spacebot

Agent-specific routing is set via `[agents.routing]` with the same keys as `[defaults.routing]`.

### `[agents.sandbox]`

OS-level filesystem containment for shell and exec tool subprocesses. Uses bubblewrap (Linux) or sandbox-exec (macOS) to enforce read-only access to everything outside the workspace.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `mode` | string | `"enabled"` | `"enabled"` for kernel-enforced containment, `"disabled"` for full host access |
| `writable_paths` | string[] | `[]` | Additional directories the agent can write to beyond its workspace |

When `mode = "enabled"`, shell and exec commands run inside a mount namespace where the entire filesystem is read-only except:

- The agent's workspace directory
- `/tmp` (private per invocation)
- `/dev` (standard device nodes)
- Any paths listed in `writable_paths`

The agent's data directory (databases, config) is explicitly re-mounted read-only even if it would otherwise be writable due to path overlap.

When `SPACEBOT_DEPLOYMENT=hosted`, sandbox mode is always `"enabled"` regardless of config.

If the sandbox backend isn't available (e.g. bubblewrap not installed), processes run unsandboxed with a warning at startup.

```toml
[agents.sandbox]
mode = "enabled"
writable_paths = ["/home/user/projects/myapp", "/var/data/shared"]
```

Comment on lines +524 to +551
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for SPACEBOT_DEPLOYMENT references
echo "=== Searching for SPACEBOT_DEPLOYMENT ==="
rg -n "SPACEBOT_DEPLOYMENT" --type=rs -C3

echo -e "\n=== Searching for hosted deployment references ==="
rg -n "hosted" --type=rs -C2 | head -80

echo -e "\n=== Searching for sandbox mode enforcement ==="
rg -n "sandbox.*enabled\|mode.*enabled" --type=rs -C2 | head -80

Repository: spacedriveapp/spacebot

Length of output: 305


🏁 Script executed:

#!/bin/bash
# Search for SPACEBOT_DEPLOYMENT without type restriction
echo "=== Searching for SPACEBOT_DEPLOYMENT ==="
rg -n "SPACEBOT_DEPLOYMENT" -C3 2>/dev/null | head -100

echo -e "\n=== Searching for 'hosted' deployment references ==="
rg -n "hosted" -C2 2>/dev/null | grep -E "\.rs:|sandbox|deployment" | head -100

echo -e "\n=== Finding all Rust files to check structure ==="
fd -e rs | head -20

echo -e "\n=== Searching for sandbox config loading ==="
rg -n "agents.*sandbox\|sandbox.*mode" -C2 2>/dev/null | head -100

Repository: spacedriveapp/spacebot

Length of output: 12028


Documentation claim about SPACEBOT_DEPLOYMENT=hosted sandbox enforcement is unsupported.

While SPACEBOT_DEPLOYMENT=hosted is properly detected in the codebase (used to enforce API bind settings), there is no implementation that forces sandbox mode = "enabled" when hosted is set. The codebase searches show hosted-aware logic only for API configuration, not sandbox mode.

The design doc references "platform boot script forces mode", suggesting this is external infrastructure enforcement rather than app-level logic. Either clarify in the user-facing documentation that this is platform-enforced externally (not in the app config), or remove the claim if it's not actually guaranteed on the hosted platform.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/`(configuration)/config.mdx around lines 524 - 551, The
docs incorrectly state that setting SPACEBOT_DEPLOYMENT=hosted forces
agents.sandbox.mode = "enabled" in-app; update the `[agents.sandbox]` section to
either remove that guarantee or explicitly state that sandbox enforcement is
performed by external platform infrastructure (e.g., "platform boot script")
rather than by application logic. Reference the config key `agents.sandbox`, the
env var `SPACEBOT_DEPLOYMENT=hosted`, and the design note about the platform
boot script so readers know this is a platform-level enforcement and not
enforced by any in-app function or config migration.

### `[[agents.cron]]`

| Key | Type | Default | Description |
Expand Down
14 changes: 9 additions & 5 deletions docs/content/docs/(features)/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Each worker gets its own isolated ToolServer, created at spawn time via `create_
└──────────────────────────────────────────┘
```

`shell`, `file`, and `exec` are stateless unit structs. `set_status` is bound to a specific worker's ID so status updates route to the right place in the channel's status block. `browser` is conditionally registered based on the agent's `browser.enabled` config.
`shell` and `exec` hold a shared `Sandbox` reference that wraps commands in OS-level containment (bubblewrap on Linux, sandbox-exec on macOS). `file` validates paths against the workspace boundary. `set_status` is bound to a specific worker's ID so status updates route to the right place in the channel's status block. `browser` is conditionally registered based on the agent's `browser.enabled` config.

Workers don't get memory tools or channel tools. They can't talk to the user, can't recall memories, can't spawn branches. They execute their task and report status.

Expand Down Expand Up @@ -171,9 +171,13 @@ async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
}
```

### Protected paths
### Sandbox containment

The `file` tool rejects reads and writes to identity/memory paths (`prompts/`, `identity/`, `data/`, `SOUL.md`, `IDENTITY.md`, `USER.md`). Workers should use `memory_save` for that content, not raw file writes.
Shell and exec commands run inside an OS-level sandbox (bubblewrap on Linux, sandbox-exec on macOS). The entire filesystem is mounted read-only except the workspace, `/tmp`, and any configured `writable_paths`. The agent's data directory (databases, config files) is explicitly protected. See [Configuration](/docs/config#agentssandbox) for sandbox config options.

The `file` tool independently validates paths against the workspace boundary and rejects writes to identity files (`SOUL.md`, `IDENTITY.md`, `USER.md`). The `exec` tool blocks dangerous environment variables (`LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`, etc.) that enable library injection regardless of sandbox state.

Leak detection (via `SpacebotHook`) scans all tool output for secret patterns (API keys, tokens, PEM keys) and terminates the process if a leak is found.

### Status reporting

Expand Down Expand Up @@ -230,15 +234,15 @@ Reports the worker's current progress. The status string appears in the channel'

### shell

Runs a shell command via `sh -c` (Unix) or `cmd /C` (Windows). Captures stdout, stderr, exit code. Has a configurable timeout (default 60s).
Runs a shell command via `sh -c` (Unix) or `cmd /C` (Windows). Captures stdout, stderr, exit code. Has a configurable timeout (default 60s). Commands are wrapped in the sandbox when enabled — the filesystem is read-only except for the workspace and configured writable paths.

### file

Read, write, or list files. Protects identity/memory paths. Creates parent directories on write by default.

### exec

Runs a specific program with explicit arguments and environment variables. More precise than `shell` for running compilers, test runners, etc. Configurable timeout.
Runs a specific program with explicit arguments and environment variables. More precise than `shell` for running compilers, test runners, etc. Configurable timeout. Sandboxed like `shell`. Blocks dangerous env vars (`LD_PRELOAD`, `NODE_OPTIONS`, etc.) that enable code injection.

### browser

Expand Down
26 changes: 18 additions & 8 deletions docs/content/docs/(features)/workers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,19 @@ Worker execution logs are controlled by `worker_log_mode`:

Logs include: worker ID, channel ID, timestamp, state, task, error (if any), and the full message history with tool calls and results.

## Protected Paths
## Sandbox and Protected Paths

The `file` and `shell` tools reject operations on protected paths:
Worker shell and exec commands run inside an OS-level sandbox (bubblewrap on Linux, sandbox-exec on macOS). The entire host filesystem is mounted read-only except:

- Identity files: `SOUL.md`, `IDENTITY.md`, `USER.md`
- System paths: `/prompts/`, `/data/spacebot.db`, `/data/lancedb/`, `/data/config.redb`
- Archives and ingest directories
- The agent's workspace directory (writable)
- `/tmp` (private per invocation)
- Any paths listed in `[agents.sandbox].writable_paths`

This prevents workers from accidentally corrupting system state.
The agent's data directory (databases, config files) is explicitly re-mounted read-only. This is enforced at the kernel level — no amount of command creativity can bypass it.

The `file` tool additionally validates paths against the workspace boundary and blocks writes to identity files (`SOUL.md`, `IDENTITY.md`, `USER.md`). The `exec` tool blocks dangerous environment variables (`LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`, etc.) that enable library injection.

Sandbox mode is configurable per agent via `[agents.sandbox]`. See [Configuration](/docs/config#agentssandbox).

## Configuration

Expand All @@ -160,11 +164,17 @@ This prevents workers from accidentally corrupting system state.
max_concurrent_workers = 5 # per channel
context_window = 128000 # tokens

[routing]
[defaults.routing]
worker = "anthropic/claude-haiku-4.5-20250514"

[routing.task_overrides]
[defaults.routing.task_overrides]
coding = "anthropic/claude-sonnet-4-20250514"

# Sandbox is enabled by default. Disable for self-hosted/local setups
# that need full host filesystem access.
[agents.sandbox]
mode = "enabled"
writable_paths = []
```

## OpenCode Workers
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/(getting-started)/docker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Minimal runtime. Everything works except the browser tool.

- Base: `debian:bookworm-slim`
- Size: ~150MB
- Includes: Spacebot binary, CA certs, SQLite libs, embedded frontend
- Includes: Spacebot binary, CA certs, SQLite libs, bubblewrap (process sandbox), embedded frontend

### `spacebot:full`

Expand Down
Loading