Writing a plugin
Build a custom plugin with the SandboxPlugin interface — graft typed properties onto the sandbox, run setup on create, and clean up on destroy.
The interface
A plugin is a plain object implementing SandboxPlugin, imported from sbox-sdk. Give it a name, then implement any of extend, onCreate, and onDestroy.
import type { SandboxPlugin } from "sbox-sdk";A lifecycle plugin: seed every sandbox
onCreate runs once the sandbox is built and is awaited before create() resolves — ideal for bootstrapping. This plugin writes a set of files into every sandbox:
import type { SandboxPlugin } from "sbox-sdk";
export function seedFiles(files: Record<string, string>): SandboxPlugin {
return {
name: "seed-files",
kind: "lifecycle",
onCreate: async (sandbox) => {
for (const [path, content] of Object.entries(files)) {
await sandbox.files.write(path, content);
}
},
};
}const client = createSandboxClient({
provider: e2b(),
plugins: [
seedFiles({
"/app/package.json": "{}",
"/app/.env": "NODE_ENV=development",
}),
],
});
const sandbox = await client.create(); // both files already existAn extend plugin: graft a typed property
extend returns properties merged onto the sandbox — synchronously, so they're available immediately. Type the contribution with the Ext parameter and it shows up on the sandbox type. This is exactly how the AI-provider plugins add sandbox.tools:
import type { SandboxPlugin } from "sbox-sdk";
function withMeta(meta: {
project: string;
}): SandboxPlugin<{ meta: typeof meta }> {
return {
name: "with-meta",
extend: () => ({ meta }),
};
}
const client = createSandboxClient({
provider: e2b(),
plugins: [withMeta({ project: "demo" })],
});
const sandbox = await client.create();
sandbox.meta.project; // typed as stringCleanup with onDestroy
onDestroy runs before the sandbox is torn down — flush logs, close connections, stop anything onCreate started:
function auditLog(sink: (line: string) => void): SandboxPlugin {
return {
name: "audit-log",
kind: "middleware",
onCreate: (sandbox) => sink(`created ${sandbox.id}`),
onDestroy: (sandbox) => sink(`destroyed ${sandbox.id}`),
};
}Notes
- Hooks are optional — a plugin can implement just one.
extendis synchronous;onCreate/onDestroymay be async and are awaited. kindis a hint — set it to the category your plugin fits. The client allows at most oneai-providerplugin; every other kind stacks freely.- Plugins apply everywhere — every sandbox from
create(),connect(), andfork()runs your plugin'sextend. - Per-
createoptions — the second argument toclient.create(spec, options)reaches your hooks asctx.createOptions, so a plugin can read per-sandbox configuration.