Cinch documentation
Cinch runs untrusted code in secure, disposable sandboxes — one API call, and you get back the result. Your infrastructure is never touched.
Every run executes inside a hardened, gVisor-isolated container with no network access, a read-only filesystem, and strict time and memory limits. Call it from Python or JavaScript/TypeScript.
Quickstart
Install the SDK, grab an API key from your dashboard, and run code in four lines.
1. Install
pip install pangolin-sdk2. Run code
from pangolin import Sandbox
box = Sandbox(api_key="cinch_live_...")
result = box.run("print(2 + 2)")
print(result.stdout) # "4\n"
print(result.exit_code) # 0
print(result.duration_ms) # 247Verify your email and your account starts with $1.00 in free credits — about 20 hours of compute, enough to start experimenting immediately.
Authentication
Cinch authenticates with an API key that looks like cinch_live_…. Create keys in your dashboard — you can have as many as you like (name them per project or environment), and each one's exact usage and spend is tracked separately. A key is shown in full only once at creation, so store it somewhere safe. You can revoke or delete any key at any time.
from pangolin import Sandbox
# Your key authenticates every request
box = Sandbox(api_key="cinch_live_yourkey")Keep your key server-side. Never ship it in frontend code or commit it to a repo.
Running code
Call run() with a string of source code. It spins up a fresh sandbox, executes the code, captures output, and tears the sandbox down — all in one call.
result = box.run("""
import math
print(math.sqrt(144))
""")
print(result.stdout) # "12.0\n"Languages
Cinch runs both Python 3 and JavaScript (Node 20). The language option defaults to python; set it to javascript to run JS.
# Python (default)
box.run("print(2 + 2)")
# JavaScript — pass language="javascript"
box.run("console.log(2 + 2)", language="javascript")The same hardening — gVisor isolation, no network, read-only filesystem, time and memory limits — applies to every language.
The result object
Every run returns a result with the captured output and metadata about the execution.
result = box.run("print('hello')")
result.stdout # str — standard output
result.stderr # str — standard error
result.exit_code # int — process exit code (0 = success)
result.timed_out # bool — True if it hit the time limit
result.duration_ms # int — execution time in millisecondsUsing with AI agents
The most common reason to reach for Cinch: your model writes code, and you need to actually run it without trusting it on your own machine. The pattern is always the same — generate code, run it in a sandbox, feed the real output back to the model.
Generate, then execute
A complete loop with the OpenAI SDK. The model returns code; Cinch runs it; you get structured output back.
from openai import OpenAI
from pangolin import Sandbox
client = OpenAI()
box = Sandbox(api_key="cinch_live_yourkey")
# 1. Ask the model to write code
completion = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Reply with ONLY runnable Python. No markdown, no explanation."},
{"role": "user", "content": "Compute the 20th Fibonacci number and print it."},
],
)
generated_code = completion.choices[0].message.content
# 2. Run that untrusted, model-written code safely in Cinch
result = box.run(generated_code)
# 3. Feed the real output back to your agent
print(result.stdout) # "6765\n"
print(result.exit_code) # 0Let the model self-correct
Because every run returns stderr and an exit_code, you can hand execution errors straight back to the model and let it fix its own code — a tight generate → run → repair loop.
def run_with_retry(prompt, attempts=3):
"""Let the model self-correct using real execution feedback."""
messages = [
{"role": "system", "content": "Reply with ONLY runnable Python."},
{"role": "user", "content": prompt},
]
for _ in range(attempts):
code = client.chat.completions.create(
model="gpt-4o", messages=messages
).choices[0].message.content
result = box.run(code)
if result.exit_code == 0:
return result.stdout # success
# Hand the error back so the model can fix its own code
messages.append({"role": "assistant", "content": code})
messages.append({
"role": "user",
"content": f"That errored:\n{result.stderr}\nFix it.",
})
return NoneWorks with any framework that produces code as text — OpenAI, Anthropic, LangChain tool calls, or your own agent loop. Cinch only cares about the code string.
MCP server
No code at all: connect Cinch to Claude Desktop, Claude Code, or Cursor, and the model gets an execute_code tool. It writes code, runs it in a Cinch sandbox instead of on your machine, and reads the real output — one config block and you're done.
npx -y @cinch-codes/mcp, which fetches the server automatically on first launch. You just need Node 18+ and an API key.Claude Desktop (macOS / Linux)
Add to claude_desktop_config.json, then restart the app.
{
"mcpServers": {
"cinch": {
"command": "npx",
"args": ["-y", "@cinch-codes/mcp"],
"env": { "CINCH_API_KEY": "cinch_live_..." }
}
}
}Claude Desktop (Windows)
Windows can't spawn npx directly, so wrap it with cmd /c:
{
"mcpServers": {
"cinch": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@cinch-codes/mcp"],
"env": { "CINCH_API_KEY": "cinch_live_..." }
}
}
}Claude Code
claude mcp add --scope user cinch -e CINCH_API_KEY=cinch_live_... -- npx -y @cinch-codes/mcpclaude mcp add --scope user cinch -e CINCH_API_KEY=cinch_live_... -- cmd /c npx -y @cinch-codes/mcpCursor
Same JSON as Claude Desktop, in .cursor/mcp.json (use the Windows variant on Windows).
The tool calls the same /run endpoint as the SDKs, with your key — identical sandbox limits and per-millisecond billing. Every call is a fresh, stdlib-only sandbox with no network; the tool description tells the model that up front, so it writes self-contained standard-library code.
claude mcp list shows Failed to connect, you're missing the cmd /c wrapper. If npx errors with ENOENT ... AppData\Roaming\npm, that folder doesn't exist yet — run mkdir %APPDATA%\npm once and retry.Package details: @cinch-codes/mcp on npm.
Recipes
Common patterns, ready to copy. Since sandboxes have no network and no shared state, you pass data in through the code string and get results out through stdout.
Pass data into a run
Embed your data directly in the code you send.
# Pass data in by embedding it in the code string
rows = [{"name": "Ada", "score": 91}, {"name": "Linus", "score": 88}]
code = f"""
data = {rows!r}
avg = sum(r["score"] for r in data) / len(data)
print(f"average: {{avg}}")
"""
result = box.run(code)
print(result.stdout) # "average: 89.5\n"Get structured data back
Print JSON from inside the sandbox, then parse it on your side.
# Return structured data: print JSON, then parse it back out
import json
code = """
import json
result = {"ok": True, "values": [1, 4, 9, 16]}
print(json.dumps(result))
"""
run = box.run(code)
data = json.loads(run.stdout)
print(data["values"]) # [1, 4, 9, 16]Handle failures and timeouts
Check the result instead of trusting the run — errors land in stderr, and timeouts flip timed_out.
run = box.run("raise ValueError('nope')")
if run.exit_code != 0:
print("failed:", run.stderr) # traceback is in stderr
elif run.timed_out:
print("hit the 10s time limit")
else:
print(run.stdout)Raw HTTP API
Not using an SDK? Hit the endpoint directly. Send a POST to https://api.cinch.codes/run with your key as a Bearer token.
curl https://api.cinch.codes/run \
-H "Authorization: Bearer cinch_live_yourkey" \
-H "Content-Type: application/json" \
-d '{"code":"print(2 + 2)"}'Response:
{
"stdout": "4\n",
"stderr": "",
"exitCode": 0,
"timedOut": false,
"durationMs": 247
}Errors
Failed requests return a JSON body with an error field and the matching HTTP status.
| Status | Meaning |
|---|---|
| 400 | Bad request — the body must be JSON like { "code": "..." } |
| 401 | Invalid or missing API key |
| 402 | Out of credits — top up in your dashboard |
| 429 | Concurrency limit reached — max 10 simultaneous runs |
| 503 | Temporarily unavailable — retry after a moment |
Limits
Each sandbox runs under strict limits for security and fairness.
| Runtime | Python 3 & JavaScript (Node 20) |
| Time limit | ~10 seconds per run |
| Memory | 256 MB |
| Concurrency | 10 simultaneous runs per account |
| Network | None — sandboxes have no internet access |
| Filesystem | Read-only, with a small writable /tmp |
| Output | Up to 1 MB per run |
| Isolation | gVisor, non-root, all Linux capabilities dropped |
Pricing
Cinch is pay-as-you-go — no subscription, no monthly minimum. You add funds to your balance and spend them only on the compute you actually use.
- •$0.10 per compute-hour, billed by the millisecond with a 100 ms minimum per run — you pay for the exact time your code runs.
- •Each run costs its
durationMsof execution. A typical sub-second run is a tiny fraction of a cent. - •Verify your email and get $1.00 free — about 20 hours of compute, enough for tens of thousands of runs.
- •Top up any time from your dashboard — pay what you want, starting at $5. No monthly wall.
When your balance hits zero, runs are rejected with a 402 until you top up.
