Private, isolated sandboxes for vibe coding — powered by 0G Network.
中文版:README.zh.md
Vibe coding has two conflicting requirements:
-
Local environments aren't isolated enough. Running untrusted or experimental code locally risks polluting the development environment, leaking credentials, or causing irreversible side effects. A fully isolated remote sandbox is needed.
-
Remote servers are controlled by others. Renting a cloud VM solves isolation, but the host provider can inspect or tamper with the code and data running inside. The sandbox environment itself cannot be trusted.
0G Sandbox combines 0G Tapp (TEE-based trusted execution) with Daytona (sandbox runtime) to satisfy both requirements simultaneously:
- Isolation: each sandbox is a fully containerized Daytona workspace, isolated from the user's local machine and from other users' sandboxes.
- Confidentiality: the billing proxy and its TEE signing key run inside a hardware TEE enclave (TDX) managed by 0G Tapp. The host cannot inspect the workload or forge vouchers. Critically, the provider never sees the user's code — sandbox workloads run inside the TEE and are opaque to the infrastructure operator.
- Trustless billing: users deposit funds into a Solidity contract on 0G Network. Compute fees are settled via EIP-712 vouchers signed by the TEE key — no trusted intermediary needed.
A cloud TDX instance only secures the execution side. In a vibe coding workflow, the attack surface is larger: your prompts, context, and intermediate outputs all pass through the AI model, and cloud providers offer no confidentiality guarantee for inference.
0G Sandbox is designed to compose with 0G Compute (TEE-based AI inference), enabling a fully confidential vibe coding pipeline:
Prompt ──► 0G Compute (AI inference in TEE)
│
▼ generated code
0G Sandbox (execution in TEE)
│
▼ results
settled on 0G Network (trustless billing)
At every step — what you write, what the AI generates, what the code produces — the data is invisible to any operator, including 0G itself. This end-to-end guarantee is something a cloud provider, as a single point of trust, fundamentally cannot offer.
The fastest way to spin up an OpenClaw AI gateway inside a 0G Private Sandbox is to let Claude do the work for you.
Prerequisites: Claude Code installed.
/plugin marketplace add 0gfoundation/0g-sandbox
/plugin install 0g-private-sandbox@0g-sandbox
/reload-plugins
Then invoke the skill anytime:
/0g-private-sandbox
git clone https://github.com/0gfoundation/0g-sandbox.git
cd 0g-sandbox
claudeThen just describe what you want in plain language, for example:
"I want to use 0G private sandbox to play with OpenClaw"
Claude will walk you through the rest. When asked for configuration details, the key piece of information you need is:
| Item | Value |
|---|---|
| Testnet contract | 0xd7e0CD227e602FedBb93c36B1F5bf415398508a4 |
| RPC | https://evmrpc-testnet.0g.ai |
| Chain ID | 16602 |
Claude will handle onboarding, wallet setup, deposit, sandbox creation, and OpenClaw configuration automatically.
The TEE key is the single signing key for the entire system — it both signs EIP-712 vouchers
off-chain and sends settlement transactions on-chain. It is fetched automatically from the
tapp-daemon gRPC at startup (or from MOCK_APP_PRIVATE_KEY in dev mode).
The TEE address needs a small amount of 0G for gas to submit settlement transactions.
To find the TEE signer address:
tapp-cli -s http://<server>:50051 get-app-key --app-id 0g-sandbox
# → Ethereum Address: 0x... ← fund this address with 0G for gasSee CONTRACTS.md for architecture, deploy/upgrade/verify instructions, and contract addresses.
Copy docker/sandbox/.env.dev to .env, fill in the required values:
cp docker/sandbox/.env.dev .env
# edit .env
go run ./cmd/billing/| Variable | Default | Description |
|---|---|---|
DAYTONA_API_URL |
(required) | Daytona API endpoint (internal; never expose publicly) |
DAYTONA_ADMIN_KEY |
(required) | Daytona admin key |
SETTLEMENT_CONTRACT |
(required) | BeaconProxy address |
RPC_URL |
(required) | EVM RPC endpoint |
CHAIN_ID |
(required) | Chain ID (e.g. 16602) |
PROVIDER_ADDRESS |
(required) | Provider's Ethereum address |
REDIS_ADDR |
redis:6379 |
Redis address |
COMPUTE_PRICE_PER_SEC |
16667 |
neuron/sec fallback (used only when per-resource on-chain pricing is not set) |
CREATE_FEE |
5000000 |
neuron flat fee fallback (on-chain value takes priority after provider registration) |
VOUCHER_INTERVAL_SEC |
60 |
voucher flush interval (seconds) |
SSH_GATEWAY_HOST |
— | SSH gateway host rewritten in SSH commands (e.g. <provider-ip>); falls back to browser hostname if unset |
PROXY_DOMAIN |
— | Domain template for sandbox service-port URLs: http://<port>-<id>.<PROXY_DOMAIN>/<path>. Use <your-ip>.nip.io:4000 (nip.io) or sandbox.yourdomain.com (real domain with nginx). |
PORT |
8080 |
HTTP server port |
MOCK_TEE |
— | Set to true for local dev (uses MOCK_APP_PRIVATE_KEY instead of TDX gRPC) |
MOCK_APP_PRIVATE_KEY |
— | Hex private key used when MOCK_TEE=true |
The SSH gateway requires two ed25519 key pairs stored as base64 in .env.
Generate them once per deployment:
# Generate the gateway private key (used by the SSH gateway service)
ssh-keygen -t ed25519 -C "daytona-gateway" -f /tmp/daytona_gw -N ""
DAYTONA_SSH_GATEWAY_PRIVATE_KEY=$(base64 -w0 < /tmp/daytona_gw)
# Generate the host key (identifies the server to SSH clients)
ssh-keygen -t ed25519 -C "daytona-gateway-host" -f /tmp/daytona_host -N ""
DAYTONA_SSH_GATEWAY_HOST_KEY=$(base64 -w0 < /tmp/daytona_host)
# Print values to paste into .env
echo "DAYTONA_SSH_GATEWAY_PRIVATE_KEY=$DAYTONA_SSH_GATEWAY_PRIVATE_KEY"
echo "DAYTONA_SSH_GATEWAY_HOST_KEY=$DAYTONA_SSH_GATEWAY_HOST_KEY"
# Clean up temp files
rm -f /tmp/daytona_gw /tmp/daytona_gw.pub /tmp/daytona_host /tmp/daytona_host.pubThese values must never be committed to source control (.gitignore covers *.key/*.pem;
the base64 values live only in .env).
docker compose upThe billing server runs inside a 0G Tapp TEE enclave. Deploy via tapp-cli:
# Build the image
docker build --target sandbox -t 0g-sandbox:dev .
# Deploy (or redeploy after changes)
tapp-cli -s http://<tapp-server>:50051 stop-app --app-id 0g-sandbox
tapp-cli -s http://<tapp-server>:50051 start-app --app-id 0g-sandbox -f docker-compose.yml
# Check container status
tapp-cli -s http://<tapp-server>:50051 get-app-container-status --app-id 0g-sandbox
# Tail logs
tapp-cli -s http://<tapp-server>:50051 get-app-logs --app-id 0g-sandbox -n 100The TEE key is automatically generated and managed by the tapp-daemon. Retrieve the Ethereum address for provider registration:
tapp-cli -s http://<tapp-server>:50051 get-app-key --app-id 0g-sandbox
# → Ethereum Address: 0x... ← use as --tee-signer when registering the providerNote: if the app is redeployed and the TEE key changes, re-register the provider with the new signer address (
cmd/provider register --tee-signer <new-addr>). This incrementssignerVersion— all users must re-acknowledge before vouchers settle.
Users interact with the system via:
- On-chain: deposit funds and acknowledge the TEE signer
- HTTP API: create, list, stop, and delete sandboxes (authenticated via EIP-191 signatures)
See CLI.md for the full cmd/user reference and onboarding flow.
Minimum balance to create a sandbox:
minBalance = CREATE_FEE + COMPUTE_PRICE_PER_SEC × VOUCHER_INTERVAL_SEC
Exact values depend on the provider's on-chain registration. Check GET /info for the live
create_fee, compute_price_per_sec, and min_balance for the provider you're using.
Sandboxes are automatically stopped when the user's balance is exhausted.
The Broker is a TEE-hosted service that acts as the entry point for users. Rather than connecting directly to a provider, users connect to the Broker, which handles provider discovery, request routing, and automatic balance top-ups on their behalf.
User ──► Broker (TEE)
│
├── Provider Marketplace index provider URLs from chain events
├── Reverse Proxy route /proxy/:addr/* → provider backend (no CORS)
├── Balance Monitor poll on-chain balance every T_monitor seconds
└── Payment Layer client call deposit(user, provider, amount) when balance low
The Broker signs Payment Layer requests with its TEE key. This lets the Payment Layer verify that top-up instructions came from a legitimate, unmodified Broker — not a spoofed caller. Trust is rooted in the TEE hardware, not in operator configuration.
The Broker indexes registered providers from chain events (via cmd/broker → internal/indexer)
and exposes them at GET /api/providers. The dashboard UI uses this to let users pick a
provider without knowing any provider URLs directly.
When a sandbox starts or restarts, the billing proxy registers the session with the Broker
(POST /api/session). The Broker tracks each session's CPU/memory and computes the user's
total burn rate. When on-chain balance drops below the threshold, it calls the Payment
Layer to top up automatically.
Derived from T_react — time from alert to funds landing on-chain:
| Parameter | Formula | Automatic (T_react ≈ 60s) | Manual (T_react = 10 min) |
|---|---|---|---|
BROKER_MONITOR_INTERVAL_SEC |
VOUCHER_INTERVAL_SEC / 2 |
30s | 30s |
BROKER_THRESHOLD_INTERVALS |
T_react / T_monitor |
3 | 20 |
BROKER_TOPUP_INTERVALS |
THRESHOLD_INTERVALS × 2 |
6 | 40 |
- Threshold = burn_rate × interval ×
THRESHOLD_INTERVALS— triggers top-up request - Topup amount = burn_rate × interval ×
TOPUP_INTERVALS— target balance after refill - On-chain latency for automatic top-up ≈ 30–60s (Payment Layer queue + ~2 block confirmations at 6s/block)
Log buffer note: tapp-cli has a fixed log buffer (~50 lines). At 6s intervals, the buffer fills in ~2 min during alert state and
get-app-logsreturns stale data. KeepBROKER_MONITOR_INTERVAL_SEC ≥ 30to avoid this.
| Variable | Default | Description |
|---|---|---|
SETTLEMENT_CONTRACT |
(required) | BeaconProxy address (same as billing proxy) |
RPC_URL |
https://evmrpc-testnet.0g.ai |
EVM RPC endpoint |
CHAIN_ID |
16602 |
Chain ID |
BROKER_PORT |
8082 |
HTTP port |
BROKER_MONITOR_INTERVAL_SEC |
300 |
Balance poll interval (seconds) |
BROKER_THRESHOLD_INTERVALS |
2 |
Alert when balance < burn × interval × N |
BROKER_TOPUP_INTERVALS |
3 |
Top-up to burn × interval × N neuron |
PAYMENT_LAYER_URL |
— | Payment Layer HTTP endpoint; empty = log-only (noop) |
BROKER_DEBUG |
false |
Expose GET /api/monitor to inspect live sessions |
The Broker runs as a separate tapp app (0g-broker) alongside 0g-sandbox:
# Build (broker stage of the multi-stage Dockerfile)
docker build --target broker -t 0g-broker:latest .
docker push <registry>/0g-broker:latest
# Deploy
tapp-cli -s http://<tapp-server>:50051 stop-app --app-id 0g-broker
tapp-cli -s http://<tapp-server>:50051 start-app --app-id 0g-broker \
-f docker/broker/docker-compose.yml
# Check logs
tapp-cli -s http://<tapp-server>:50051 get-app-logs --app-id 0g-broker --service broker -n 50The billing proxy connects to the Broker via BROKER_URL=http://<broker-host>:8082.
# Build contracts (requires Docker)
make build-contracts
# Regenerate Go bindings
make abigen
# Run all tests
go test ./...
# Run Solidity tests (requires Docker)
make test-contractsSee TESTING.md for unit, integration, and E2E test details.