{"slug": "show-hn-sandrpod-run-the-unmodified-e2b-sdk-against-your-own-infrastructure", "title": "Show HN: SandrPod – run the unmodified E2B SDK against your own infrastructure", "summary": "SandrPod, an open-source, self-hosted control plane for AI agent code execution, launched v0.5.10, enabling developers to run unmodified E2B SDK code on their own infrastructure across 8 clouds including Aliyun and Tencent, Docker, or bare machines. The project provides a one-command Docker setup, a CLI, and native REST/Python/TS APIs, with reverse-tunnel workers requiring no inbound ports.", "body_md": "**Self-hosted execution infrastructure for AI agents.**\n\nRun agent code on any cloud, any machine — or none at all. Speak any SDK. Keep full control.\n\n*Your clouds. Your machines. Your rules.*\n\nOne command brings up the control plane **and** a Docker worker from the\npublished images — no Go toolchain, no build, no cloning the repo.\n\n```\ncurl -O https://raw.githubusercontent.com/sandrpod/sandrpod/main/docker/docker-compose.yml\ndocker compose up -d\n```\n\nThat pulls `ghcr.io/sandrpod/{server,poder,toolbox}`\n\nand starts:\nthe **control plane** on `localhost:8080`\n\nand one **Docker worker** (Poder) that\ndials back over a reverse tunnel — no inbound ports on the worker.\n\nFirst run downloads ~170 MB here (server + worker). The sandbox runtime (\n\n`toolbox`\n\n, ~530 MB) is pulled by the worker the first time you create a sandbox, so step 2 below is the slow one. After that, both steps take seconds.\n\nCheck it:\n\n```\ncurl localhost:8080/health          # {\"status\":\"ok\",...}\ncurl localhost:8080/api/v1/poders   # wait for one worker to show \"state\":\"ONLINE\"\n```\n\n`/health`\n\nanswers as soon as the control plane binds, which is before the worker\nhas finished registering. The second command is the one that tells you the stack\nis actually ready.\n\n⚠️ This dev stack runs withauthentication disabled(anonymous admin) — fine on localhost. Before exposing it, set`SANDRPOD_TOKEN`\n\n(see[docs/AUTH_AND_KEYS.md]).\n\n```\npipx install sandrpod-cli              # recommended — isolated, nothing to activate\n# or: uv tool install sandrpod-cli\n# or, inside a virtualenv: pip install sandrpod-cli\n\nsandrpod-cli --api-url http://localhost:8080 create demo --provider local\nsandrpod-cli --api-url http://localhost:8080 execute demo \"echo hello from SandrPod; python3 -c 'print(6*7)'\"\n```\n\nReal output from a cold run (no cached images) against v0.5.10:\n\n```\nSandbox:  demo\nJob ID:   job-1788150453342465216-7j5di2LP\nStatus:   provisioning\nState:    PENDING\nState:    RUNNING\nSandbox 'demo' is ready\nhello from SandrPod\n42\n```\n\n**SandrPod** is the control plane for AI agent code execution — open source and\nself-hosted. It turns your own infrastructure into a fleet of on-demand sandboxes\nthat agents create, run code in, and tear down, while you keep ownership of where\nthat code runs, what it can touch, and where the data lives.\n\nHosted sandbox services make you rent their runtime in their region. SandrPod\ninverts that: **you** bring the substrate — any cloud, plain Docker, or a bare\nmachine — and SandrPod is the thin, portable layer that schedules, tunnels, and\ngoverns execution across it. It rests on three pillars:\n\n**Run anywhere you own**— 8 clouds (incl. Aliyun & Tencent), Docker, or a machine with no Docker at all.** Speak any SDK**— a native REST/Python/TS API, LangChain/deepagents, and the** unmodified E2B SDK**as a drop-in.** Stay in control**— reverse-tunnel workers with zero inbound ports, an opt-in permission gate + decision audit, and self-hosted data that never leaves your infra.\n\nOne binary schedules sandboxes across whatever infrastructure you have:\n\n**AWS · GCP · Azure · Aliyun · Tencent · DigitalOcean · Hetzner · Oracle** —\nplus plain **Docker**, or a **bare machine with no Docker** via `sandrpod-agent`\n\n.\n\nAliyun and Tencent make China-region and data-residency deployments first-class —\nsomething hosted services don't offer. Each provider has a guide under [ docs/](/sandrpod/sandrpod/blob/main/docs)\n(\n\n[AWS](/sandrpod/sandrpod/blob/main/docs/AWS_PROVISIONING.md),\n\n[GCP](/sandrpod/sandrpod/blob/main/docs/GCP_PROVISIONING.md),\n\n[Aliyun](/sandrpod/sandrpod/blob/main/docs/ALIYUN_PROVISIONING.md),\n\n[Tencent](/sandrpod/sandrpod/blob/main/docs/TENCENT_PROVISIONING.md), …). Remote exec uses each cloud's managed run-command API where it exists (AWS SSM, Aliyun CloudAssist, Azure Run Command, Tencent TAT, Oracle Instance Agent) and an ephemeral-key SSH path elsewhere (GCP, DigitalOcean, Hetzner).\n\nSandrPod isn't tied to one client. It exposes a native REST API and Python/TS\nSDKs, a first-class **LangChain/deepagents** backend, **and** the full **E2B\nwire protocol** — so the *unmodified* `e2b`\n\n/ `e2b-code-interpreter`\n\nSDKs work\nagainst it with nothing but two env vars. Bring the ecosystem you already use;\nmigrate nothing.\n\n**Zero inbound ports.** Workers dial*out*to the control plane over a WebSocket reverse tunnel — run them behind NAT, in a private subnet, or on a laptop.**Governance, not just isolation.** Opt-in employee-PC mode adds a per-machine permission gate (path consent, command denylist, PTY consent) and a decision audit pipeline (NDJSON + central HTTP upload) — turning \"run agent code\" into \"*govern*what agents may touch on real machines.\"**Your data stays yours.** Self-hosted, Apache 2.0, no phone-home.\n\n| Hosted (E2B, Modal, …) | SandrPod |\n|\n|---|---|---|\nLicense |\nClosed | Apache 2.0, open source |\nWhere it runs |\nTheir infra / region | Your cloud, Docker, or bare metal |\nClouds |\nVendor-managed | AWS · GCP · Azure · Aliyun · Tencent · DO · Hetzner · Oracle |\nChina regions |\n✗ | ✓ Aliyun + Tencent |\nNo-Docker mode |\n— | `sandrpod-agent` : any machine becomes a sandbox |\nSDKs |\nTheir SDK | Native + LangChain + drop-in E2B |\nInbound ports on workers |\nn/a | None (reverse tunnel) |\nGovernance / audit |\n— | Opt-in permission gate + decision audit |\nData residency |\nTheir region | Your account, your region |\n\nRecorded live against a SandrPod deployment: the package is the one from PyPI,\nthe only change is which host it points at. Source: [ assets/e2b-demo.py](/sandrpod/sandrpod/blob/main/assets/e2b-demo.py).\n\nOne example of \"speak any SDK\": already have code on the E2B SDK? Point it at your\nSandrPod and it just works — `Sandbox.create`\n\n, `files.*`\n\n, `commands.*`\n\n(foreground/background/PTY), `run_code`\n\n, `watch_dir`\n\n, `get_metrics`\n\n, `pause`\n\n/`resume`\n\n:\n\n``` python\nimport os\nos.environ[\"E2B_API_KEY\"]     = \"e2b_your_key\"          # issued by your SandrPod\nos.environ[\"E2B_API_URL\"]     = \"https://sandbox.you.com\"   # control plane\nos.environ[\"E2B_SANDBOX_URL\"] = \"https://sandbox.you.com\"   # per-sandbox envd\n\nfrom e2b import Sandbox                # the real, unmodified e2b SDK\nsbx = Sandbox.create()\nsbx.files.write(\"/tmp/hi.txt\", \"hello from my own cloud\")\nprint(sbx.commands.run(\"cat /tmp/hi.txt\").stdout)   # → hello from my own cloud\n\nfrom e2b_code_interpreter import Sandbox as CI\nci = CI.create()\nprint(ci.run_code(\"import numpy as np; np.arange(6).sum()\").text)   # → 15\n```\n\nFor a zero-config drop-in (no env vars, just a domain), run the gateway behind a\nwildcard domain — `SANDRPOD_E2B_DOMAIN=sandbox.you.com`\n\n+ `*.sandbox.you.com`\n\nDNS\n\n- TLS. Full surface, wire-protocol details, and the coverage matrix (verified\nagainst the\n**real, unmodified** E2B SDK over a real container) are in.[docs/E2B_COMPAT.md](/sandrpod/sandrpod/blob/main/docs/E2B_COMPAT.md)\n\n```\nE2B SDK ──┐\nNative SDK ├─→ API Server (Control Plane, :8080)\nLangChain ─┤         ↕ WebSocket + yamux reverse tunnel\nCLI ───────┘    Poder (Worker) ──→ Toolbox (Sandbox container)\n\n                sandrpod-agent  ──→ (direct mode, any machine as a sandbox)\n```\n\n| Component | Description |\n|---|---|\nAPI Server |\nThe control plane: native REST + E2B-compatible gateway, sandbox CRUD, scheduling, tunnel proxying |\nPoder |\nWorker node. Persistent WebSocket tunnel to the control plane; manages Docker container lifecycle |\nsandrpod-agent |\nRegisters the local machine directly as a sandbox with an embedded Toolbox — no Docker required |\nsandrpod-tray |\nOptional user-session GUI for employee-PC mode: tray icon + consent prompts + local settings page |\nToolbox |\nExecution service inside each sandbox. PTY, file ops, background processes, sessions |\n\nEmployee-PC mode (opt-in): when`sandrpod-agent`\n\nruns on a real employee laptop rather than a server, enable a per-PC permission gate (path consent + command denylist + PTY consent) and a decision-audit pipeline that ships every allow/deny/warn event to a central HTTP endpoint. Both are off by default (`--permission-mode=off`\n\n) — see.[docs/PERMISSION_AND_AUDIT.md]\n\n## …or with plain `curl`\n\n(no install)\n\n```\ncurl -X POST localhost:8080/api/v1/sandboxes \\\n  -d '{\"name\":\"demo\",\"provider_type\":\"local\"}'\ncurl -X POST \"localhost:8080/api/v1/sandboxes/execute?sandbox=demo\" \\\n  -d '{\"code\":\"print(6*7)\",\"language\":\"python\"}'\npython\n# Native Python SDK\nfrom langchain_sandrpod import SandrPodClient\nsb = SandrPodClient(api_url=\"http://localhost:8080\").get_sandbox(\"demo\")\nprint(sb.execute(\"echo hello from SandrPod\").output)\n\n# …or the unmodified E2B SDK (set E2B_API_URL / E2B_SANDBOX_URL to your server)\nfrom e2b import Sandbox\nprint(Sandbox.create().commands.run(\"echo hello from SandrPod\").stdout)\n```\n\n## Other ways to run it\n\n**Production, with TLS and a real domain**— the reference deployment: PostgreSQL, a wildcard certificate, and the E2B surface on.`docker compose -f docker/docker-compose.prod.yml up -d --wait`\n\n, walked through end to end (DNS, certificate, renewal, acceptance sweep) in— or as a narrative, with the two failures it took to get there, in[docs/PRODUCTION_DEPLOYMENT.md](/sandrpod/sandrpod/blob/main/docs/PRODUCTION_DEPLOYMENT.md)[this writeup](https://blog.sandrpod.com/e2b-compatible-sandbox-api/).**PostgreSQL only**(no TLS termination):`docker compose -f docker/docker-compose.pg.yml up -d`\n\n— see[docs/MULTI_INSTANCE_DEPLOYMENT.md](/sandrpod/sandrpod/blob/main/docs/MULTI_INSTANCE_DEPLOYMENT.md)to scale out.**Build from source**(contributors):`docker compose -f docker/docker-compose.local.yml up -d --build`\n\n**From Go directly**:`go run ./cmd/server -port 8080 -db sqlite:./data/sandrpod.db`\n\n, then add a worker with the`docker run …/poder`\n\none-liner or`go run ./cmd/poder -api-url=http://localhost:8080`\n\n.**No Docker at all**— turn any machine into a sandbox:`go run ./cmd/agent -api-url=http://localhost:8080 -name=my-machine`\n\nThe default `docker-compose.yml`\n\npersists to SQLite (a `sandrpod-data`\n\nvolume), so sandboxes and tokens survive a restart.\n\n```\nsandrpod-tray serve                       # user-session tray + consent dialogs (🛡)\ngo run ./cmd/agent -api-url=http://localhost:8080 -name=my-laptop \\\n  -permission-mode=prompt \\\n  -audit-upload-url=https://your-platform/api/audit/decisions/batch\n```\n\nPermission modes: `off`\n\n(default) | `prompt`\n\n(consent dialog outside `work_dir`\n\n) | `strict`\n\n(silent deny outside `work_dir`\n\n).\nFull architecture, `permissions.json`\n\nschema, tray CLI, and audit protocol: ** docs/PERMISSION_AND_AUDIT.md**.\n\n```\npip install langchain-sandrpod\npython\nfrom langchain_sandrpod import SandrPodClient\nfrom deepagents import create_deep_agent\nfrom langchain_openai import ChatOpenAI\n\nmodel = ChatOpenAI(model=\"gpt-4o\", temperature=0)\nclient = SandrPodClient(api_url=\"http://localhost:8080\")\n\nsb = client.get_sandbox(\"my-sandbox\")\nagent = create_deep_agent(model=model, backend=sb)\nresult = agent.invoke({\"messages\": [{\"role\": \"user\", \"content\": \"Write a quicksort and run it\"}]})\n\n# Or auto-create + clean up\nwith client.sandbox(\"temp-sb\") as sb:\n    agent = create_deep_agent(model=model, backend=sb)\n    result = agent.invoke({\"messages\": [...]})\n```\n\nThe backend also exposes richer per-sandbox capabilities directly:\n\n```\nsb.run_code(\"x = 40\", context=\"ctx1\")          # stateful Jupyter-style kernel\nsb.run_code(\"x + 2\", context=\"ctx1\")[\"text\"]   # → \"42\" (x persisted)\nsb.metrics()                                    # {cpu_count, cpu_used_pct, mem_*, disk_*}\nwith sb.watch_dir(\"/workspace\") as w:           # filesystem watch\n    events = w.get_new_events()\n```\n\nSee [ pkg/sdk/python/langchain_sandrpod/examples/](/sandrpod/sandrpod/blob/main/pkg/sdk/python/langchain_sandrpod/examples) for full examples.\n\n```\npipx install sandrpod-cli    # or: uv tool install sandrpod-cli\nsandrpod-cli config set-url http://localhost:8080\n\n# --provider: local | aws | gcp | azure | aliyun | tencent | digitalocean | hetzner | oracle\nsandrpod-cli list\nsandrpod-cli create my-sandbox --provider local --image ghcr.io/sandrpod/toolbox:latest\nsandrpod-cli create gpu-box --provider gcp --region asia-east1-a --instance-type e2-medium\nsandrpod-cli execute my-sandbox \"ls /workspace\"     # one-shot (stateless)\nsandrpod-cli stream my-sandbox \"make build\"         # real-time streamed output\nsandrpod-cli run my-sandbox \"z = 10\" --context c1   # stateful kernel — z persists in context c1\nsandrpod-cli stats my-sandbox                       # live CPU / memory / disk\nsandrpod-cli fs watch my-sandbox /workspace         # print filesystem events\nsandrpod-cli shell my-sandbox                       # interactive PTY\nsandrpod-cli delete my-sandbox\n\n# Poder management\nsandrpod-cli poder list\nsandrpod-cli poder delete <poder-id>\n```\n\nSandrPod speaks **two** HTTP surfaces: its own native REST API, and — when the\nE2B gateway is enabled — the full E2B control-plane + `envd`\n\nprotocol.\n\n**Native API**\n\n| Method | Path | Description |\n|---|---|---|\n| GET | `/api/v1/sandboxes` |\nList sandboxes |\n| POST | `/api/v1/sandboxes` |\nCreate sandbox |\n| DELETE | `/api/v1/sandboxes/{name}` |\nDelete sandbox |\n| POST | `/api/v1/sandboxes/execute` |\nExecute code |\n| GET | `/api/v1/sandboxes/{name}/toolbox/*` |\nProxy to Toolbox (file upload/download, etc.) |\n| GET | `/api/v1/poders` |\nList Poder nodes |\n| DELETE | `/api/v1/poders/{id}` |\nDelete a Poder record |\n\n**E2B-compatible API** — `POST /sandboxes`\n\n, `GET /sandboxes/{id}`\n\n,\n`/sandboxes/{id}/{pause,resume,metrics,connect}`\n\n, and the `envd`\n\nFilesystem/Process connect-RPC services. See [docs/E2B_COMPAT.md](/sandrpod/sandrpod/blob/main/docs/E2B_COMPAT.md).\n\n```\n# One-shot: all platforms + sandrpod-tray (skips missing toolchains gracefully)\nmake build-all\n\n# Local build (agent/server are CGO-free; tray requires CGO + native libs)\ngo build -o server        ./cmd/server\ngo build -o poder         ./cmd/poder\ngo build -o agent         ./cmd/agent\ngo build -o sandrpod-tray ./cmd/sandrpod-tray   # CGO required\n\n# Cross-compile to dist/ (agent + server only, CGO=0)\nCGO_ENABLED=0 GOOS=linux   GOARCH=amd64 go build -ldflags=\"-s -w\" -o dist/server-linux-amd64 ./cmd/server\nCGO_ENABLED=0 GOOS=linux   GOARCH=amd64 go build -ldflags=\"-s -w\" -o dist/sandrpod-agent-linux-amd64 ./cmd/agent\nCGO_ENABLED=0 GOOS=darwin  GOARCH=arm64 go build -ldflags=\"-s -w\" -o dist/sandrpod-agent-darwin-arm64 ./cmd/agent\nCGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags=\"-s -w\" -o dist/sandrpod-agent-windows-amd64.exe ./cmd/agent\n\n# Docker images (amd64)\ndocker buildx build --platform linux/amd64 -f docker/Dockerfile.poder   -t ghcr.io/sandrpod/poder:latest   --load .\ndocker buildx build --platform linux/amd64 -f docker/Dockerfile.toolbox -t ghcr.io/sandrpod/toolbox:latest --load .\n```\n\nLonger pieces, with the commands and the failures they were measured from:\n\n[Self-hosting an E2B-compatible sandbox stack, from an empty server](https://blog.sandrpod.com/e2b-compatible-sandbox-api/)— one VM, one domain, four containers, and the unmodified E2B SDK checked function by function. 48 of 50 calls pass; the two that do not are named.[Two ways to give a deepagents agent a sandbox you own](https://blog.sandrpod.com/deepagents-sandbox-backends/)—`langchain-e2b`\n\nand`langchain-sandrpod`\n\ndown the same twelve assertions. Both pass. Testing both surfaced two real defects — both of them mine to fix — and a third finding that turned out to be a deliberate contract I had misread as a bug.\n\nMore at [blog.sandrpod.com](https://blog.sandrpod.com).\n\nDeploying this inside a company, or hitting something the docs don't cover?\n[Open an issue](https://github.com/sandrpod/sandrpod/issues), email me at\n[zhaochj@126.com](mailto:zhaochj@126.com), or find me on X as [@itscjzhao](https://x.com/itscjzhao).\n\nApache 2.0", "url": "https://wpnews.pro/news/show-hn-sandrpod-run-the-unmodified-e2b-sdk-against-your-own-infrastructure", "canonical_source": "https://github.com/sandrpod/sandrpod", "published_at": "2026-09-02 12:00:03+00:00", "updated_at": "2026-09-02 12:23:09.067141+00:00", "lang": "en", "topics": ["ai-infrastructure", "ai-agents", "developer-tools"], "entities": ["SandrPod", "E2B", "Docker", "AWS", "GCP", "Azure", "Aliyun", "Tencent"], "alternates": {"html": "https://wpnews.pro/news/show-hn-sandrpod-run-the-unmodified-e2b-sdk-against-your-own-infrastructure", "markdown": "https://wpnews.pro/news/show-hn-sandrpod-run-the-unmodified-e2b-sdk-against-your-own-infrastructure.md", "text": "https://wpnews.pro/news/show-hn-sandrpod-run-the-unmodified-e2b-sdk-against-your-own-infrastructure.txt", "jsonld": "https://wpnews.pro/news/show-hn-sandrpod-run-the-unmodified-e2b-sdk-against-your-own-infrastructure.jsonld"}}