ViteHub

Sandbox quickstart

Register Sandbox, define a release-notes sandbox, call it from a route, and verify the JSON result.

This guide creates one release-notes sandbox. The route accepts markdown-style notes, runs the sandbox in an isolated provider runtime, and returns a normalized JSON result.

The provider is the only part that changes between Cloudflare and Vercel. The sandbox definition and route call stay the same.

Prompt
Set up @vitehub/sandbox in this app.

- Install @vitehub/sandbox and one provider SDK
- Register hubSandbox() for Vite or @vitehub/sandbox/nitro for Nitro
- Configure sandbox.provider as cloudflare or vercel
- Define release-notes as a discovered sandbox
- Call runSandbox('release-notes', payload) from a route
- Handle result.isErr() before reading result.value

Docs: /docs/vite/sandbox/quickstart or /docs/nitro/sandbox/quickstart

Install Sandbox

pnpm add @vitehub/sandbox

Install the provider SDK for the platform that will execute the sandbox:

pnpm add @cloudflare/sandbox

Register the integration

Register the Nitro module and choose the provider:

nitro.config.ts
import { defineNitroConfig } from 'nitro/config'

export default defineNitroConfig({
  modules: ['@vitehub/sandbox/nitro'],
  sandbox: {
    provider: 'cloudflare',
  },
})

Define the sandbox

Create a discovered Nitro sandbox file:

server/sandboxes/release-notes.ts
import { defineSandbox } from '@vitehub/sandbox'

export type ReleaseNotesPayload = {
  notes?: string
}

export type ReleaseNotesResult = {
  summary: string
  items: string[]
}

export default defineSandbox(async (payload: ReleaseNotesPayload = {}): Promise<ReleaseNotesResult> => {
  const items = (payload.notes || '')
    .split('\n')
    .map(note => note.replace(/^[-*]\s*/, '').trim())
    .filter(Boolean)

  return {
    summary: items[0] || '',
    items,
  }
})

Call the sandbox from a route

Add a Nitro route that reads the request body and executes the named sandbox:

server/api/release-notes.post.ts
import { readRequestPayload, runSandbox } from '@vitehub/sandbox'
import type { ReleaseNotesPayload } from '../sandboxes/release-notes'

export default defineEventHandler(async (event) => {
  const payload = await readRequestPayload<ReleaseNotesPayload>(event, { notes: '' }) as ReleaseNotesPayload
  const result = await runSandbox('release-notes', payload)

  if (result.isErr()) {
    throw createError({ statusCode: 500, statusMessage: result.error.message })
  }

  return { result: result.value }
})

Verify the response

Run or deploy the app with a configured provider, then send a request:

curl -X POST http://localhost:3000/api/release-notes \
  -H 'content-type: application/json' \
  -d '{"notes":"- Added weekly digest\n- Tightened signup copy"}'

The route returns the sandbox result:

{
  "result": {
    "summary": "Added weekly digest",
    "items": [
      "Added weekly digest",
      "Tightened signup copy"
    ]
  }
}

::

Copyright © 2026