Oshu Vault
Examples

E2B

Run Claude Code in an E2B sandbox with secrets injected via proxy

Overview

This example creates an E2B sandbox with Claude Code, using Oshu Vault to securely inject your Anthropic API key. The sandbox only sees a sealed token — the real key never leaves the proxy.

Prerequisites

  • E2B account and E2B_API_KEY set
  • Oshu Vault instance running
  • SECRETS_PROXY_API_KEY — your proxy management API key
  • ANTHROPIC_API_KEY — your real Anthropic key

Install Dependencies

npm install @e2b/code-interpreter @oshu/vault-sdk

Build the Template

E2B uses custom sandbox templates defined programmatically. Create a template definition:

// e2b-template.ts
import { Template } from "e2b";

const PROXY_BASE_URL = process.env.PROXY_BASE_URL ?? "https://pv.oshu.dev";

export const template = Template()
  .fromDockerfile(
    `FROM node:22-slim

RUN apt-get update && apt-get install -y curl ca-certificates && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL ${PROXY_BASE_URL}/v1/ca.pem \\
      -o /usr/local/share/ca-certificates/proxy-ca.crt \\
    && update-ca-certificates

# Wrap the node binary so it trusts the system CA store
RUN mv /usr/local/bin/node /usr/local/bin/node.real \\
    && echo '#!/bin/sh' > /usr/local/bin/node \\
    && echo 'exec /usr/local/bin/node.real --use-openssl-ca "$@"' >> /usr/local/bin/node \\
    && chmod +x /usr/local/bin/node

RUN npm install -g @anthropic-ai/claude-code`,
  );

Then build and push:

// build-e2b-template.ts
import { Template, defaultBuildLogger } from "e2b";
import { template } from "./e2b-template.js";

const buildInfo = await Template.build(template, {
  alias: "oshu-vault-claude",
  cpuCount: 2,
  memoryMB: 2048,
  onBuildLogs: defaultBuildLogger(),
});

console.log(`Template ready: ${buildInfo.alias}`);
npx tsx build-e2b-template.ts

Full Example

import { Sandbox } from "@e2b/code-interpreter";
import { SecretsProxyClient } from "@oshu/vault-sdk";

// --- Config ---
const PROXY_BASE_URL = "https://pv.oshu.dev";
const PROXY_HOST = "pv.oshu.dev";
const API_KEY = process.env.SECRETS_PROXY_API_KEY!;
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY!;

// 1. Create an Oshu Vault session
const client = new SecretsProxyClient({
  baseUrl: PROXY_BASE_URL,
  apiKey: API_KEY,
});

const session = await client.createSession({
  secrets: { ANTHROPIC_API_KEY },
});

const proxyUrl = `https://${session.session_id}:${session.token}@${PROXY_HOST}`;

// 2. Create sandbox from template — CA cert + Claude Code already baked in
const sandbox = await Sandbox.create("oshu-vault-claude", {
  envs: {
    ANTHROPIC_API_KEY: session.sealed_secrets["ANTHROPIC_API_KEY"],
    HTTP_PROXY: proxyUrl,
    HTTPS_PROXY: proxyUrl,
  },
});

try {
  // 3. Run claude -p — no special flags, just works
  const result = await sandbox.commands.run(
    `claude -p "Write a hello world program in Python"`,
    { timeoutMs: 120_000 },
  );

  console.log(result.stdout);
} finally {
  await client.deleteSession(session.session_id);
  await sandbox.kill();
}

Custom Proxy URL

If your Oshu Vault instance is at a different URL, pass it as a build arg:

e2b template build -n oshu-vault-claude --build-arg PROXY_BASE_URL=https://your-proxy.example.com

What's Happening

Template built with Claude Code + trusted CA

The template installs the proxy CA certificate into the system trust store and wraps the node binary with --use-openssl-ca so Node.js trusts it too. Claude Code is pre-installed. E2B caches this template.

Session created

The SDK sends your real ANTHROPIC_API_KEY to the proxy and gets back a sealed token like SEALED_7f3a9b2c....

Sandbox configured

The sandbox receives ANTHROPIC_API_KEY=SEALED_7f3a9b2c... and proxy env vars. No certificate setup needed at runtime.

Secret injected on-the-fly

When Claude Code calls the Anthropic API with x-api-key: SEALED_7f3a9b2c..., the proxy intercepts the request and replaces the sealed token with your real API key before forwarding.

The sandbox never sees your real API key. Even if the AI-generated code tries to log or exfiltrate ANTHROPIC_API_KEY, it only gets the useless sealed token.