A runtime designed for code you didn't write.#
GocciaScript is meant for running untrusted or user-provided code in a host-controlled environment. Scripts receive only the data and capabilities the host provides, run with explicit limits, and return structured results that the host can inspect. Filesystem workflows use GocciaSandboxRunner: host paths are copied into a virtual filesystem as seed baselines, sandbox writes stay in that filesystem, and changes are surfaced as explicit diffs.
AI agent
LLM emits GocciaScript via a tool call
Goccia sandbox
explicit globals · seed baselines · capability gates · limits
Structured result
JSON
Seeded files, not host mounts.#
GocciaSandboxRunner executes an entry path inside an isolated virtual filesystem. Seed paths and JSON seed config copy files into the sandbox before execution; they are import baselines, not live mounts. Source can import "fs" for sandbox filesystem operations and "goccia" for shell commands or nested execution.
./build/GocciaSandboxRunner /main.js \
--seed-config=./sandbox.seed.json \
--mode=bytecode \
--diff{
"files": [
{ "from": "./project", "to": "/" },
{ "from": "./tools", "to": "/tools" },
{ "path": "/main.js", "text": "import fs from \"fs\";\nconsole.log(fs.readdirSync('/'));" },
{ "path": "/data.bin", "base64": "AQID" }
]
}import fs from "fs";
import { $, runScript } from "goccia";
fs.mkdirSync("/out", { recursive: true });
fs.writeFileSync("/out/summary.txt", "ready\n");
const audit = runScript("/tools/audit.js", {
sandbox: true,
seed: ["/tools/audit.js", { from: "/out", to: "/input" }],
diff: true,
});
console.log(await $`cat /out/summary.txt`.text());
console.log(audit.diff);One sandboxed call vs five.#
The same agent task — “Summarize the latest transaction batch and find outliers.” — solved with a typical tool stack and again with a single GocciaScript call. This comparison assumes each tool call depends on data discovered or produced by the previous step, so the calls cannot be parallelized without changing the task shape.
o200k_base tokenization.5 tool calls ↓ 1422 in ↑ 241 out
- 1bashdiscover↓ 108 ↑ 39
ls /tmp/agent/transactions/ - 2bashload↓ 166 ↑ 42
cat /tmp/agent/transactions/transactions.current.json - 3bashsum↓ 313 ↑ 50
jq '[.[].amount] | add' /tmp/agent/transactions/transactions.current.json - 4bashaverage↓ 382 ↑ 52
jq '[.[].amount] | add / length' /tmp/agent/transactions/transactions.current.json - 5bashoutliers↓ 453 ↑ 58
jq '[.[] | select((.amount | abs) > 280)]' /tmp/agent/transactions/transactions.current.json
- · values cross 5 process boundaries — possible to lose precision or quoting
- · 5 round-trips ≈ 5× the prompt overhead and 5× the chance of a misstep
1 tool call ↓ 223 in ↑ 138 out
- 1run_codeeverything↓ 223 ↑ 138
const total = transactions.reduce((s, t) => s + t.amount, 0); const avg = total / transactions.length; const stdev = Math.sqrt( transactions.reduce((s, t) => s + (t.amount - avg) ** 2, 0) / transactions.length ); const outliers = transactions.filter((t) => Math.abs(t.amount - avg) > 2 * stdev); ({ total, avg, outliers });
- ✓ all values stay in one sandbox — no serialization between steps
- ✓ host gets a single structured JSON result back
Sandbox preview
Edit the script or globals and hit Run — the code runs in the same sandboxed runtime used by the server preview and returns a structured host result.