Skip to content

Commit 6ef7b73

Browse files
authored
Merge pull request #12 from bas3line/seed-editor-source
Add editor save path
2 parents 962bd16 + 04728ad commit 6ef7b73

4 files changed

Lines changed: 66 additions & 225 deletions

File tree

README.md

Lines changed: 2 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -1,226 +1,3 @@
1-
<div align="center">
1+
# sandbox-editor
22

3-
```text
4-
███████╗ █████╗ ███╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗
5-
██╔════╝██╔══██╗████╗ ██║██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝
6-
███████╗███████║██╔██╗ ██║██║ ██║██████╔╝██║ ██║ ╚███╔╝
7-
╚════██║██╔══██║██║╚██╗██║██║ ██║██╔══██╗██║ ██║ ██╔██╗
8-
███████║██║ ██║██║ ╚████║██████╔╝██████╔╝╚██████╔╝██╔╝ ██╗
9-
╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝
10-
```
11-
12-
### Burn the code. Keep the host.
13-
14-
**A self-hosted Rust control plane for disposable coding environments and AI agents.**
15-
16-
[Docs](https://docs.yshubham.com/v2/sandbox/overview) · [Architecture](https://docs.yshubham.com/v2/sandbox/architecture) · [Security](https://docs.yshubham.com/v2/sandbox/security) · [Server setup](https://docs.yshubham.com/v2/sandbox/server-setup) · [Client setup](https://docs.yshubham.com/v2/sandbox/client-setup) · [Custom domains](https://docs.yshubham.com/v2/sandbox/custom-domains) · [CLI](https://docs.yshubham.com/v2/sandbox/cli) · [MCP](https://docs.yshubham.com/v2/sandbox/mcp-and-skills)
17-
18-
</div>
19-
20-
<p align="center">
21-
<img src="public/ChatGPT%20Image%20Jul%2019,%202026,%2010_31_43%20PM.png" alt="Bas3line Sandbox: a safe playground for coding agents" width="100%">
22-
</p>
23-
24-
## Links
25-
26-
**Start here:** [product docs](https://docs.yshubham.com/v2/sandbox/overview) · [MCP and agent setup](https://docs.yshubham.com/v2/sandbox/mcp-and-skills) · [tools registry](https://tools.yshubham.com/)
27-
28-
**Source:** [GitHub](https://github.com/bas3line/sandbox) · [self-hosted forge mirror](https://git.yshubham.com/bas3line/sandbox) · [`sandbox-platform` agent skill](https://github.com/bas3line/rool-repo/tree/main/skills/sandbox-platform)
29-
30-
**Platform:** [yshubham.com](https://yshubham.com/) · [status](https://status.yshubham.com/) · [trace](https://trace.yshubham.com/) · [temporary objects](https://objects.yshubham.com/)
31-
32-
## One-command workstation setup
33-
34-
Install `sandbox`, `sandboxd`, `sandbox-mcp`, the Sandbox skill across supported agents, and register the MCP server with detected Codex, Claude Code, and Gemini CLIs:
35-
36-
```sh
37-
curl -fsSL https://tools.yshubham.com/sandbox/setup.sh | sh
38-
```
39-
40-
The full setup uses `npx` to install the skill across supported coding agents. For binaries only:
41-
42-
```sh
43-
curl -fsSL https://tools.yshubham.com/sandbox/install.sh | sh
44-
sandbox --help
45-
```
46-
47-
The initial registry build publishes macOS ARM64 and Linux x86-64. The release workflow covers macOS x86-64 and Linux ARM64 as additional builders become available. Release binaries bundle `libpq` and vendored OpenSSL so `sandboxd` does not depend on a machine-specific PostgreSQL client path.
48-
49-
Then point the client at your self-hosted controller:
50-
51-
```sh
52-
sandbox config set-server https://sandbox.example.com
53-
export SANDBOX_TOKEN='read-from-your-secret-store'
54-
sandbox doctor
55-
```
56-
57-
The CLI saves only the non-secret controller URL. `SANDBOX_URL` or `--server` can override it for one environment or command.
58-
59-
See [server setup](https://docs.yshubham.com/v2/sandbox/server-setup), [client setup](https://docs.yshubham.com/v2/sandbox/client-setup), [custom public domains](https://docs.yshubham.com/v2/sandbox/custom-domains), [MCP setup](https://docs.yshubham.com/v2/sandbox/mcp-and-skills), and the registry-hosted [`sandbox-platform` skill](https://github.com/bas3line/rool-repo/tree/main/skills/sandbox-platform).
60-
61-
The canonical hosted reference is [Sandbox documentation](https://docs.yshubham.com/v2/sandbox/overview); [MCP and agent setup](https://docs.yshubham.com/v2/sandbox/mcp-and-skills) lives alongside it. The registry remains the source for install scripts and release assets.
62-
63-
The MCP guide includes native setup for Codex, Claude Code, Gemini CLI, OpenCode, VS Code/Copilot, and Goose, plus drop-in templates for Cursor, Claude Desktop, Windsurf, Cline, Roo Code, Gemini Code Assist, and generic MCP clients. Agents without native MCP support use the same `sandbox` CLI and installed skill.
64-
65-
## Why I am open-sourcing this
66-
67-
Sandbox started as private infrastructure I built to give coding agents fast, disposable machines without handing them the host. I kept running into the same problem: every team building with agents eventually needs remote execution, lifecycle management, resource limits, and a clean way to destroy everything afterward.
68-
69-
I am open-sourcing it so other engineers can use the foundation, adapt it to their own infrastructure, and help push self-hosted agent sandboxes forward. It is opinionated, practical, and still evolving—but it is real software, built to be run, forked, broken, improved, and shared.
70-
71-
Sandbox gives every human, CI job, and coding agent a typed remote execution boundary. One daemon, `sandboxd`, runs as a controller, worker, or both. The tiny `sandbox` CLI and `sandbox-mcp` bridge use the same authenticated API. PostgreSQL provides durable state; NATS is optional; Redis is deliberately not required. The default control plane stays lean.
72-
73-
```sh
74-
sandbox create --tenant platform --image ubuntu:24.04 \
75-
--cpu-millis 2000 --memory-mib 4096 --ttl 3600 \
76-
--network restricted --untrusted-repo --generated-code
77-
78-
sandbox exec 019f... -- cargo test --workspace
79-
sandbox http 3000
80-
sandbox tunnel create 019f... --port 3000
81-
sandbox agent run codex --tenant platform
82-
sandbox delete 019f...
83-
```
84-
85-
`sandbox http PORT` shares a service running on your current machine at a temporary `https://local-….tunnel.yshubham.com` URL through the hosted Sandbox relay. No third-party quick-tunnel domain or helper binary is involved. HTTP and WebSocket traffic—including Vite HMR—is carried over one outbound WebSocket and rewritten to the loopback origin, so development servers never need to allow a random public `Host`. Keep the command running and press Ctrl-C to revoke the exact-host route. Self-hosters can set `SANDBOX_HTTP_RELAY`; services inside a managed sandbox continue to use `sandbox tunnel create SANDBOX_ID --port PORT`.
86-
87-
## What is already real
88-
89-
| Capability | Status |
90-
|---|---|
91-
| Controller/worker scheduling, leases, heartbeats, TTL cleanup | Implemented |
92-
| AEGIS risk scoring and placement | Implemented and unit tested |
93-
| Hardened Docker worker | Implemented; intended for dedicated single-tenant worker hosts |
94-
| External runtime protocol for Firecracker, Kata, gVisor, or private VMMs | Implemented adapter contract; bring a driver |
95-
| PostgreSQL through Diesel + in-memory development store | Implemented |
96-
| NATS lifecycle events + zero-service in-memory bus | Implemented |
97-
| CLI lifecycle, agent profiles, JSON output, bounded exec | Implemented |
98-
| MCP 2025-11-25 stdio server with structured tool results | Implemented |
99-
| Wildcard HTTP/WebSocket tunnels with per-sandbox edge networks | Implemented; custom HTTPS domains, direct edge, and Cloudflare ingress documented |
100-
| Local HTTP/WebSocket sharing on the deployment wildcard | Implemented; ephemeral exact-host routes, bounded sessions/bodies, automatic revocation |
101-
| Codex, Claude Code, OpenCode, Pi image builder | Implemented with pinned versions |
102-
| Aider and Goose profiles | Implemented using their official images |
103-
| OIDC/SAML, tenant RBAC, secret broker, interactive PTY, raw TCP tunnels | Design boundary; not implemented in v0.1 |
104-
105-
This repository is an engineering foundation, not a magic claim that Docker equals a hardened multi-tenant VM. Read the [security model](https://docs.yshubham.com/v2/sandbox/security) before exposing it to hostile tenants.
106-
107-
## AEGIS: the custom isolation algorithm
108-
109-
AEGIS—**Adaptive Execution Guard and Isolation Scheduler**—does two jobs in one deterministic pass:
110-
111-
1. Scores workload risk from data sensitivity, network access, repository trust, generated-code execution, secret use, host mounts, exposure, privilege, and lifetime.
112-
2. Chooses the minimum isolation tier, hard-filters unsafe nodes, then ranks survivors using dominant-resource headroom, fragmentation, host pressure, image warmth, region locality, and bin-packing efficiency.
113-
114-
A high-risk request cannot downgrade itself to a container. If no compatible microVM worker exists, the request fails with `no_capacity`; it never silently weakens isolation. See [the AEGIS scheduler reference](https://docs.yshubham.com/v2/sandbox/aegis).
115-
116-
## The shape
117-
118-
```mermaid
119-
flowchart LR
120-
A["Humans / Codex / Claude / OpenCode / CI"] --> C["sandbox CLI"]
121-
A --> M["sandbox-mcp"]
122-
C --> P["sandboxd controller"]
123-
M --> P
124-
P --> G["Auth + policy + AEGIS"]
125-
G --> D[("PostgreSQL")]
126-
G -. events .-> N["NATS (optional)"]
127-
G --> W1["sandboxd worker: Docker"]
128-
G --> W2["sandboxd worker: external driver"]
129-
W1 --> X["Hardened containers"]
130-
W1 --> T["Private tunnel networks + HTTP edge"]
131-
W2 --> V["Firecracker / Kata / gVisor / private VMM"]
132-
```
133-
134-
`sandboxd` is one deployable executable. Split controller and worker processes for production blast-radius control; use `--role all` only for development or a dedicated single-node installation.
135-
136-
## Five-minute developer launch
137-
138-
Requirements: Docker with Compose and two random tokens of at least 32 characters.
139-
140-
```sh
141-
export SANDBOX_API_TOKEN="$(openssl rand -hex 32)"
142-
export SANDBOX_NODE_TOKEN="$(openssl rand -hex 32)"
143-
docker compose -f deploy/compose/compose.yaml up --build
144-
```
145-
146-
In another terminal:
147-
148-
```sh
149-
export SANDBOX_URL=http://127.0.0.1:8080
150-
export SANDBOX_TOKEN="$SANDBOX_API_TOKEN"
151-
cargo run --package sandbox-cli -- doctor
152-
cargo run --package sandbox-cli -- create --tenant dev --image ubuntu:24.04 --ttl 900
153-
```
154-
155-
The Compose stack intentionally sets the microVM threshold above the score range so it can run on a normal Docker laptop. That is a developer convenience, not the production policy. Production keeps the default threshold of `55` and supplies compatible workers.
156-
157-
To publish sandbox services, configure wildcard DNS and enable an edge profile. Direct Traefik, Caddy, proxied Cloudflare with Origin CA and Full (strict), an outbound-only Cloudflare Tunnel overlay, and a clearly marked HTTP compatibility mode are documented in [the tunnel guide](https://docs.yshubham.com/v2/platform/tunnels). Follow the [custom-domain setup](https://docs.yshubham.com/v2/sandbox/custom-domains) for the complete path. The outbound Cloudflare option keeps the origin off public ingress; real deployment domains, connector tokens, addresses, and certificate material belong in the environment or secret store, never the repository.
158-
159-
## Coding agents
160-
161-
Pinned image builders are included for:
162-
163-
```sh
164-
./scripts/build-agent-image.sh codex
165-
./scripts/build-agent-image.sh claude
166-
./scripts/build-agent-image.sh opencode
167-
./scripts/build-agent-image.sh pi
168-
sandbox agent list
169-
```
170-
171-
Agent credentials do not belong in command arguments, labels, or plaintext API fields. Connect the external runtime driver to Vault, AWS Secrets Manager, GCP Secret Manager, or your existing workload-identity broker. See [the coding-agent guide](https://docs.yshubham.com/v2/sandbox/coding-agents).
172-
173-
## Repository map
174-
175-
```text
176-
cmd/
177-
sandbox/ # operator + agent CLI
178-
sandboxd/ # controller/worker/all-in-one daemon
179-
sandbox-mcp/ # stdio MCP bridge
180-
crates/
181-
core/ # domain model, API contracts, configuration
182-
aegis/ # risk and placement algorithm
183-
storage/ # Diesel/PostgreSQL + memory store
184-
runtime/ # Docker + external runtime adapters
185-
events/ # memory + NATS event bus
186-
client/ # typed HTTP client
187-
config/ # reviewed configuration examples
188-
deploy/ # Compose and systemd packaging
189-
docs/ # architecture, security, API, operations
190-
images/agents/ # reproducible coding-agent image inputs
191-
scripts/ # release installer and image tooling
192-
skills/ # Codex-compatible Sandbox skill
193-
```
194-
195-
## Non-negotiable design rules
196-
197-
- Policy is enforced by the server and runtime, never by an agent prompt.
198-
- Commands cross the API as argv, not interpolated shell text.
199-
- Privileged sandboxes are rejected by the public API.
200-
- Output, request bodies, runtime duration, PIDs, CPU, RAM, and TTL are bounded.
201-
- Tokens are separated for operators and workers and compared in constant time.
202-
- Docker socket workers belong on dedicated hosts. Strong multi-tenancy uses a VMM-grade external driver.
203-
- Every release archive gets a SHA-256 checksum and signed SLSA provenance; the installer fails closed on checksum mismatch.
204-
205-
## Build
206-
207-
```sh
208-
cargo fmt --all -- --check
209-
cargo clippy --workspace --all-targets -- -D warnings
210-
cargo test --workspace
211-
cargo build --profile dist --workspace
212-
```
213-
214-
After deploying to a test controller, exercise the complete CLI lifecycle and public tunnel path:
215-
216-
```sh
217-
SANDBOX_URL=https://sandbox.example.com \
218-
SANDBOX_TOKEN='read-from-your-secret-store' \
219-
./scripts/smoke-test-cli.sh
220-
```
221-
222-
Rust is pinned to 1.97.1. Direct dependencies were resolved to current stable releases on 2026-07-19 and are locked in `Cargo.lock`.
223-
224-
## License
225-
226-
Apache-2.0. Fork it, harden it for your infrastructure, write a runtime driver, and make every untrusted build somebody else's kernel problem.
3+
Internal editor source. Public bug reports live in `sandbox-main`.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "sandbox-editor",
3+
"version": "2.4.1",
4+
"type": "module",
5+
"main": "src/editor.ts"
6+
}

src/editor.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {saveDocument} from './save.js';
2+
3+
export interface Document {
4+
name: string;
5+
contents: string;
6+
}
7+
8+
/** Persist the open document. Called from the Cmd+S handler. */
9+
export function onSave(doc: Document, write: (p: string, d: string) => void) {
10+
return saveDocument({name: doc.name, contents: doc.contents, write});
11+
}

src/save.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* File saving. Names are sanitised before they reach the filesystem so that
3+
* control characters and path separators cannot escape the workspace.
4+
*/
5+
6+
const PATH_SEPARATORS = new Set(['/', '\\']);
7+
8+
/**
9+
* Strip anything the filesystem would object to, walking the name one
10+
* character at a time.
11+
*/
12+
export function sanitizeFilename(name: string): string {
13+
let out = '';
14+
let index = 0;
15+
16+
while (index < name.length) {
17+
const code = name.charCodeAt(index);
18+
19+
if (code > 127) {
20+
// Non-ASCII: skip the continuation bytes and carry on.
21+
continue;
22+
}
23+
24+
const char = name[index] as string;
25+
if (!PATH_SEPARATORS.has(char) && code >= 32) out += char;
26+
27+
index += 1;
28+
}
29+
30+
return out;
31+
}
32+
33+
export function saveDocument({
34+
name,
35+
contents,
36+
write,
37+
}: {
38+
name: string;
39+
contents: string;
40+
write: (path: string, data: string) => void;
41+
}): string {
42+
const safe = sanitizeFilename(name);
43+
if (safe.length === 0) throw new Error('Filename is empty after sanitising');
44+
45+
write(safe, contents);
46+
return safe;
47+
}

0 commit comments

Comments
 (0)