Skip to content

Commit 8eea645

Browse files
authored
Merge branch 'main' into add-frontend-ci
2 parents 7bbb63b + 2e05ef7 commit 8eea645

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+6308
-553
lines changed

.github/workflows/release.yml

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -115,27 +115,15 @@ jobs:
115115
id: platform
116116
run: echo "pair=$(echo ${{ matrix.platform }} | tr '/' '-')" >> "$GITHUB_OUTPUT"
117117

118-
- name: Build and push slim
118+
- name: Build and push
119119
uses: docker/build-push-action@v6
120120
with:
121121
context: .
122-
target: slim
123122
platforms: ${{ matrix.platform }}
124123
push: true
125-
tags: ${{ env.IMAGE }}:slim-${{ steps.platform.outputs.pair }}
126-
cache-from: type=gha,scope=slim-${{ steps.platform.outputs.pair }}
127-
cache-to: type=gha,mode=max,scope=slim-${{ steps.platform.outputs.pair }}
128-
129-
- name: Build and push full
130-
uses: docker/build-push-action@v6
131-
with:
132-
context: .
133-
target: full
134-
platforms: ${{ matrix.platform }}
135-
push: true
136-
tags: ${{ env.IMAGE }}:full-${{ steps.platform.outputs.pair }}
137-
cache-from: type=gha,scope=full-${{ steps.platform.outputs.pair }}
138-
cache-to: type=gha,mode=max,scope=full-${{ steps.platform.outputs.pair }}
124+
tags: ${{ env.IMAGE }}:build-${{ steps.platform.outputs.pair }}
125+
cache-from: type=gha,scope=build-${{ steps.platform.outputs.pair }}
126+
cache-to: type=gha,mode=max,scope=build-${{ steps.platform.outputs.pair }}
139127

140128
merge-docker:
141129
needs: build-docker
@@ -155,22 +143,13 @@ jobs:
155143
username: ${{ github.actor }}
156144
password: ${{ secrets.GITHUB_TOKEN }}
157145

158-
- name: Create slim multi-arch manifest
159-
run: |
160-
docker buildx imagetools create \
161-
--tag ${{ env.IMAGE }}:${{ needs.build-docker.outputs.version }}-slim \
162-
--tag ${{ env.IMAGE }}:slim \
163-
${{ env.IMAGE }}:slim-linux-amd64 \
164-
${{ env.IMAGE }}:slim-linux-arm64
165-
166-
- name: Create full multi-arch manifest
146+
- name: Create multi-arch manifest
167147
run: |
168148
docker buildx imagetools create \
169-
--tag ${{ env.IMAGE }}:${{ needs.build-docker.outputs.version }}-full \
170-
--tag ${{ env.IMAGE }}:full \
149+
--tag ${{ env.IMAGE }}:${{ needs.build-docker.outputs.version }} \
171150
--tag ${{ env.IMAGE }}:latest \
172-
${{ env.IMAGE }}:full-linux-amd64 \
173-
${{ env.IMAGE }}:full-linux-arm64
151+
${{ env.IMAGE }}:build-linux-amd64 \
152+
${{ env.IMAGE }}:build-linux-arm64
174153
175154
- name: Log in to Fly registry
176155
if: github.repository_owner == 'spacedriveapp'

AGENTS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ Single binary. No server dependencies. Runs on tokio. All data lives in embedded
1010

1111
**Stack:** Rust (edition 2024), tokio, Rig (v0.30.0, agentic loop framework), SQLite (sqlx), LanceDB (embedded vector + FTS), redb (embedded key-value).
1212

13+
## JavaScript Tooling (Critical)
14+
15+
- For UI work in `spacebot/interface/`, use `bun` for all JS/TS package management and scripts.
16+
- **NEVER** use `npm`, `pnpm`, or `yarn` in this repo unless the user explicitly asks for one.
17+
- Standard commands:
18+
- `bun install`
19+
- `bun run dev`
20+
- `bun run build`
21+
- `bun run test`
22+
- `bunx <tool>` (instead of `npx <tool>`)
23+
1324
## Migration Safety
1425

1526
- **NEVER edit an existing migration file in place** once it has been committed or applied in any environment.

Cargo.lock

Lines changed: 74 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ notify = "7"
6161
# Cryptography (for secrets)
6262
aes-gcm = "0.10"
6363
sha2 = "0.10"
64+
argon2 = "0.5"
6465
rand = "0.9"
6566

6667
# UUID generation
@@ -131,7 +132,7 @@ arrow-array = "57.3.0"
131132
arrow-schema = "57.3.0"
132133

133134
# Browser automation
134-
chromiumoxide = { version = "0.8", features = ["tokio-runtime"], default-features = false }
135+
chromiumoxide = { version = "0.8", features = ["tokio-runtime", "_fetcher-rustls-tokio"], default-features = false }
135136
chromiumoxide_cdp = "0.8"
136137

137138
# Templating for prompts
@@ -165,6 +166,10 @@ unimplemented = "deny"
165166
[dev-dependencies]
166167
tokio-test = "0.4"
167168

169+
# OS keystore (macOS Keychain for master key storage)
170+
[target.'cfg(target_os = "macos")'.dependencies]
171+
security-framework = "3"
172+
168173
[profile.release]
169174
lto = "thin"
170175
strip = true

Dockerfile

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,38 +43,21 @@ RUN SPACEBOT_SKIP_FRONTEND_BUILD=1 cargo build --release \
4343
&& mv /build/target/release/spacebot /usr/local/bin/spacebot \
4444
&& cargo clean -p spacebot --release --target-dir /build/target
4545

46-
# ---- Slim stage ----
47-
# Minimal runtime with just the binary. No browser.
48-
FROM debian:bookworm-slim AS slim
46+
# ---- Runtime stage ----
47+
# Minimal runtime with Chrome runtime libraries for fetcher-downloaded Chromium.
48+
# Chrome itself is downloaded on first browser tool use and cached on the volume.
49+
FROM debian:bookworm-slim
4950

5051
RUN apt-get update && apt-get install -y --no-install-recommends \
5152
ca-certificates \
5253
libsqlite3-0 \
5354
curl \
5455
gh \
5556
bubblewrap \
56-
&& rm -rf /var/lib/apt/lists/*
57-
58-
COPY --from=builder /usr/local/bin/spacebot /usr/local/bin/spacebot
59-
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
60-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
61-
62-
ENV SPACEBOT_DIR=/data
63-
ENV SPACEBOT_DEPLOYMENT=docker
64-
EXPOSE 19898 18789
65-
66-
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
67-
CMD curl -f http://localhost:19898/api/health || exit 1
68-
69-
ENTRYPOINT ["docker-entrypoint.sh"]
70-
CMD ["spacebot", "start", "--foreground"]
71-
72-
# ---- Full stage ----
73-
# Slim + Chromium for browser workers.
74-
FROM slim AS full
75-
76-
RUN apt-get update && apt-get install -y --no-install-recommends \
77-
chromium \
57+
openssh-server \
58+
# Chrome runtime dependencies — required whether Chrome is system-installed
59+
# or downloaded by the built-in fetcher. The fetcher provides the browser
60+
# binary; these are the shared libraries it links against.
7861
fonts-liberation \
7962
libnss3 \
8063
libatk-bridge2.0-0 \
@@ -91,5 +74,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
9174
libxtst6 \
9275
&& rm -rf /var/lib/apt/lists/*
9376

94-
ENV CHROME_PATH=/usr/bin/chromium
95-
ENV CHROME_FLAGS="--no-sandbox --disable-dev-shm-usage --disable-gpu"
77+
COPY --from=builder /usr/local/bin/spacebot /usr/local/bin/spacebot
78+
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
79+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
80+
81+
ENV SPACEBOT_DIR=/data
82+
ENV SPACEBOT_DEPLOYMENT=docker
83+
EXPOSE 19898 18789
84+
85+
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
86+
CMD curl -f http://localhost:19898/api/health || exit 1
87+
88+
ENTRYPOINT ["docker-entrypoint.sh"]
89+
CMD ["spacebot", "start", "--foreground"]

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,20 +242,35 @@ headers = { Authorization = "Bearer ${SENTRY_TOKEN}" }
242242

243243
### Security
244244

245-
Workers execute arbitrary shell commands and subprocesses on your behalf. Spacebot uses defense-in-depth to contain what those processes can do:
245+
Spacebot runs autonomous LLM processes that execute arbitrary shell commands and spawn subprocesses. Security isn't an add-on — it's a layered system designed so that no single failure exposes credentials or breaks containment.
246246

247-
- **Process sandbox** — shell and exec tools run inside OS-level filesystem containment. On Linux, [bubblewrap](https://github.com/containers/bubblewrap) creates a mount namespace where the entire filesystem is read-only except the agent's workspace and any explicitly configured writable paths. On macOS, `sandbox-exec` enforces equivalent restrictions via SBPL profiles. No amount of LLM creativity can write outside the sandbox — it's kernel-enforced, not string-filtered
248-
- **Workspace isolation** — file tools canonicalize all paths and reject anything outside the agent's workspace. Symlinks that escape the workspace are blocked
247+
#### Credential Isolation
248+
249+
Secrets are split into two categories: **system** (LLM API keys, messaging tokens — never exposed to subprocesses) and **tool** (CLI credentials like `GH_TOKEN` — injected as env vars into workers). The category is auto-assigned based on the secret name, or set explicitly.
250+
251+
- **Environment sanitization** — every subprocess starts with a clean environment (`--clearenv` on Linux, `env_clear()` everywhere else). Only safe baseline vars (`PATH`, `HOME`, `LANG`), tool-category secrets, and explicit `passthrough_env` entries are present. System secrets never enter any subprocess
252+
- **Secret store** — credentials live in a dedicated redb database, not in `config.toml`. Config references secrets by alias (`anthropic_key = "secret:ANTHROPIC_API_KEY"`), so the config file is safe to display, screenshot, or `cat`
253+
- **Encryption at rest** — optional AES-256-GCM encryption with a master key derived via Argon2id. The master key lives in the OS credential store (macOS Keychain, Linux kernel keyring) — never on disk, never in an env var, never accessible to worker subprocesses
254+
- **Keyring isolation** — on Linux, workers are spawned with a fresh empty session keyring via `pre_exec`. Even without the sandbox, workers cannot access the parent's kernel keyring where the master key lives
255+
- **Output scrubbing** — all tool secret values are redacted from worker output before it reaches channels or LLM context. A rolling buffer handles secrets split across stream chunks. Channels see `[REDACTED]`, never raw values
256+
- **Worker secret management** — workers can store credentials they obtain (API keys from account creation, OAuth tokens) via the `secret_set` tool. Stored secrets are immediately available to future workers
257+
258+
#### Process Containment
259+
260+
- **Process sandbox** — shell and exec tools run inside OS-level filesystem containment. On Linux, [bubblewrap](https://github.com/containers/bubblewrap) creates a mount namespace where the entire filesystem is read-only except the agent's workspace and configured writable paths. On macOS, `sandbox-exec` enforces equivalent restrictions via SBPL profiles. Kernel-enforced, not string-filtered
261+
- **Dynamic sandbox mode** — sandbox settings are hot-reloadable. Toggle via the dashboard or API without restarting the agent
262+
- **Workspace isolation** — file tools canonicalize all paths and reject anything outside the agent's workspace. Symlinks that escape are blocked
249263
- **Leak detection** — a hook scans every tool argument before execution and every tool result after execution for secret patterns (API keys, tokens, PEM private keys) across plaintext, URL-encoded, base64, and hex encodings. Leaked secrets in arguments skip the tool call; leaked secrets in output terminate the agent
250264
- **Library injection blocking** — the exec tool blocks dangerous environment variables (`LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`, `NODE_OPTIONS`, etc.) that could hijack child process loading
251265
- **SSRF protection** — the browser tool blocks requests to cloud metadata endpoints, private IPs, loopback, and link-local addresses
252266
- **Identity file protection** — writes to `SOUL.md`, `IDENTITY.md`, and `USER.md` are blocked at the application level
253-
- **Secret encryption**credentials stored via the secrets system are encrypted at rest with AES-256-GCM
267+
- **Durable binary storage**`tools/bin` directory on PATH survives hosted rollouts. Workers are instructed to install binaries there instead of ephemeral package manager locations
254268

255269
```toml
256270
[agents.sandbox]
257271
mode = "enabled" # "enabled" (default) or "disabled"
258272
writable_paths = ["/home/user/projects/myapp"] # additional writable dirs beyond workspace
273+
passthrough_env = ["CUSTOM_VAR"] # forward specific env vars to workers
259274
```
260275

261276
---
@@ -505,6 +520,8 @@ No server dependencies. Single binary. All data lives in embedded databases in a
505520
| [Cortex](docs/content/docs/(core)/cortex.mdx) | Memory bulletin and system observation |
506521
| [Cron Jobs](docs/content/docs/(features)/cron.mdx) | Scheduled recurring tasks |
507522
| [Routing](docs/content/docs/(core)/routing.mdx) | Model routing and fallback chains |
523+
| [Secrets](docs/content/docs/(configuration)/secrets.mdx) | Credential storage, encryption, and output scrubbing |
524+
| [Sandbox](docs/content/docs/(configuration)/sandbox.mdx) | Process containment and environment sanitization |
508525
| [Messaging](docs/content/docs/(messaging)/messaging.mdx) | Adapter architecture (Discord, Slack, Telegram, Twitch, Webchat, webhook) |
509526
| [Discord Setup](docs/content/docs/(messaging)/discord-setup.mdx) | Discord bot setup guide |
510527
| [Browser](docs/content/docs/(features)/browser.mdx) | Headless Chrome for workers |

0 commit comments

Comments
 (0)