Skip to content

Add five new Tenki Sandbox integration examples - #24

Open
opencolin wants to merge 10 commits into
mainfrom
examples/orca-tenki
Open

Add five new Tenki Sandbox integration examples#24
opencolin wants to merge 10 commits into
mainfrom
examples/orca-tenki

Conversation

@opencolin

Copy link
Copy Markdown
Collaborator

This PR adds five comprehensive cookbook examples demonstrating how to integrate Tenki Sandbox microVMs with various AI coding agents and frameworks:

Summary

These examples show practical patterns for running AI agents in isolated, disposable microVMs instead of on the host machine, with each example focusing on a different agent framework or use case.

Examples Added

  1. Claude Agent SDK (examples/claude-agent-sdk/)

    • Integrates Anthropic's Claude Agent SDK with Tenki as the execution backend
    • Re-implements the SDK's built-in bash, read_file, and write_file tools to run in a sandbox instead of locally
    • Disables all built-in tools (tools: []) to ensure the agent's entire execution surface is the microVM
    • Includes agent.mjs (full agent loop) and verify.mjs (backend verification without model key)
  2. Claude Code Sandbox (examples/claude-code-sandbox/)

    • Runs Claude Code headless against a real Git repository in a disposable microVM
    • Clones a repo, installs the CLI, lets the agent edit the checkout, and reads back the diff
    • Demonstrates how to pass through model configuration (ANTHROPIC_API_KEY, etc.) to the sandbox
  3. OpenAI Codex Sandbox (examples/codex-sandbox/)

    • Similar pattern to Claude Code but for OpenAI's Codex agent
    • Shows how to bypass Codex's built-in bubblewrap sandbox when running inside a microVM (redundant nesting)
    • Includes verification that the agent's edits can be read back via git diff
  4. OpenCode Sandbox (examples/opencode-sandbox/)

    • Boots a headless OpenCode server in a microVM with a repo pre-cloned
    • Exposes the server on a public HTTPS URL via exposePort()
    • Demonstrates password-protected access and JSON API interaction
    • Shows how to use enableOpenCode: true to bake the CLI into the guest
  5. orca-tenki (examples/orca-tenki/)

    • Implements an orca ephemeral-VM recipe backed by Tenki microVMs
    • One disposable machine per agent instead of git worktrees on the host
    • Includes recipe.mjs (lifecycle management: create/suspend/resume/destroy) and ssh-proxy.mjs (WebSocket-to-stdio bridge for SSH)
    • Demonstrates SSH certificate issuance, host key pinning, and session lifecycle management

Key Implementation Details

  • All examples use top-level await using (requires Node 24+) for automatic resource cleanup
  • Each includes a verify.mjs script that tests the Tenki backend without requiring a model API key (suitable for CI)
  • Examples demonstrate proper environment variable handling, timeout configuration, and output streaming
  • The Claude Agent SDK example shows how to use in-process MCP servers to register custom tools
  • The orca example illustrates the full lifecycle of ephemeral VM management with SSH access
  • All examples include comprehensive README documentation with setup instructions and security notes

Files Added

  • 5 example directories with run.mjs, verify.mjs, README.md, and package.json
  • Additional implementation files: tenki-tools.mjs (Claude Agent SDK), ssh-proxy.mjs (orca), recipe.mjs (orca)
  • Updated main README.md to reference the new examples

https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN

claude added 7 commits August 17, 2026 03:58
Runs Claude Code headless against a real Git checkout inside a disposable
Tenki microVM, then reads back the diff the agent produced.

verify.mjs proves the Tenki-facing half with no model key: boot with
cloneRepoUrl, npm i -g the CLI, assert its version, edit a file in the
checkout, assert the diff round-trips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
Measured: Node 22.22.2 fails to parse `await using`, Node 24.19.0 accepts it.
verify.mjs uses try/finally, so CI on Node 20 is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
verify.mjs now checks `claude --help` still lists `-p, --print` and
`--dangerously-skip-permissions`, since npm installs whatever version is
latest at run time.

An ExecOptions.env value of undefined arrives in the guest as an empty
string rather than unset, which is why run.mjs filters the passthrough
list; measured, and now stated where it matters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
Anthropic's Claude Agent SDK with a Tenki microVM as its execution backend:
`tools: []` disables every built-in tool, and bash/write_file/read_file are
re-implemented against sandbox.exec, sandbox.writeFile, and sandbox.readFile,
registered through an in-process MCP server.

verify.mjs drives the tool handlers directly with no LLM and asserts a real
computed value (the 30th Fibonacci number, from Python run inside the sandbox).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
agent.mjs printed only the final answer, which proved nothing about where
the work ran. It now logs each tool_use as it happens and ends by listing
fib.py inside the sandbox — the file exists in the microVM and nowhere on
the host.

The bash tool discarded the exit status, so a command that failed after
writing to stderr read as success to the model; non-zero exits are now
prefixed with `exit N`.

Also documented, from a live run: a tool handler that throws is delivered
to the agent as an is_error result and it recovers on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
Runs OpenAI Codex headless against a real Git checkout inside a disposable
Tenki microVM, then reads back the diff the agent produced. Same repo and
task as claude-code-sandbox so the two read side by side.

Codex ships its own bubblewrap sandbox; nesting it inside a microVM buys
nothing and warns about a missing bubblewrap, so the example bypasses that
layer and says why.

verify.mjs proves the Tenki-facing half with no model key: boot with
cloneRepoUrl, npm i -g the CLI, assert its version and that run.mjs's flags
still exist, edit a file in the checkout, assert the diff round-trips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
Documents enableOpenCode, a first-party Tenki feature the cookbook had no
coverage of: it bakes the OpenCode CLI into the guest before the sandbox
reports ready, so this example has no install step at all.

Runs `opencode serve` against a cloned repo and exposes it on a public
preview URL, so the same URL is both OpenCode's web UI (/app) and its JSON
API. Measured gotchas are documented: port 7681 is reserved by Tenki's own
ttyd console and cannot be exposed, --hostname 0.0.0.0 is required, and
OPENCODE_SERVER_PASSWORD must be set or the server is open to the internet.

verify.mjs asserts an unauthenticated request gets 401 before asserting the
authenticated one returns the checkout, so the example cannot regress into
publishing an unsecured agent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
@tenki-reviewer

tenki-reviewer Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review complete. No blocking issues — approved ✅; 1 nitpick below.

🧹 Nitpicks (1) — 🟢 1 low
  • 🟢 orca recipe ignores the workspace the README requires (recipe.mjs:34) — The orca-tenki README (examples/orca-tenki/README.md:83) tells users to export TENKI_WORKSPACE_ID=... before running, but recipe.mjs never reads that variable and its createAndWait call (examples/orca-tenki/recipe.mjs:34) omits workspaceId, so the sandbox is created in whatever workspace the token resolves to and the exported value is silently ignored.

This PR introduces five new example directories showing how to run coding agents (Claude Agent SDK, Claude Code, Codex, OpenCode, and Orca) inside disposable Tenki microVMs. Each includes a run.mjs launcher, a verify.mjs harness, a package.json, and a README. The Orca recipe adds an ssh-proxy.mjs WebSocket SSH bridge and a recipe.mjs that provisions ephemeral VMs and issues short-lived user certificates.

Files Change
claude-agent-sdk/* Adds an Agent SDK integration with a tenki-tools.mjs tool bridge and verify harness.
claude-code-sandbox/*, codex-sandbox/*, opencode-sandbox/* Add headless agent sandbox launchers and matching verify scripts.
orca-tenki/* Adds an ephemeral-VM recipe with a WebSocket SSH proxy and user-certificate issuance.
README.md + per-example READMEs Documents the recipes and their usage.

Note: the high-recall sweep pass did not publish its findings, so its candidates were not independently adjudicated; the issues it surfaced overlap with the verified set.

Reviewed commit: c6af8e2

tenki-reviewer[bot]
tenki-reviewer Bot previously approved these changes Aug 19, 2026
claude added 3 commits August 19, 2026 18:47
run.mjs printed a preview URL and told you to open it in a browser, then
`await using` terminated the sandbox as the scope ended — the URL 404'd
before anyone could click it. Caught by running run.mjs for the first time.

It now creates the sandbox without `await using`, holds the process open,
and disposes on SIGINT, so the URL stays reachable until Ctrl-C.
idleTimeoutMinutes: 30 reaps a sandbox that gets forgotten.

Holding the loop open needs a ref'd handle: an unsettled top-level await
makes Node exit 13 with "Detected unsettled top-level await".

Dropping `await using` also drops the Node 24 requirement for this example;
run.mjs now parses on Node 22.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
orca isolates each parallel coding agent in a git worktree on the local
machine. Its ephemeral-VM recipe hook runs a user-supplied command for
create/suspend/resume/destroy and takes back an SSH target, so a recipe can
put every agent on its own Tenki microVM instead.

Tenki carries SSH over a WebSocket rather than a TCP port, so ssh-proxy.mjs
is a 19-line ProxyCommand bridge that lets a stock ssh client in.

Measured and handled: the SSH host key is the gateway's, not the guest's
sshd, and cert.caPub signs user certs so it cannot pin it — the recipe
learns the real key once under a per-sandbox alias rather than disabling
host checking. The cert must sit at <identityFile>-cert.pub because orca's
target schema has no certificate field. resume() returns while the session
is still RESUMING, so it polls isReady before re-issuing the cert.

verify.mjs drives the recipe exactly as orca does and SSHes in using only
what create printed, then suspends, resumes, reconnects, and destroys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
The README tells users to export it, but createAndWait omitted workspaceId,
so the sandbox landed in whatever workspace the token resolved to and the
exported value was silently ignored. Flagged by review on #24.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz85xGA4zWSge1BtiDNnSN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants