> ## Documentation Index
> Fetch the complete documentation index at: https://vendo-mintlify-24213046.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Edge runtimes

> Run Vendo on Cloudflare Workers, Bun, Deno, Hono, Fastify, or Lambda: what init generates, the three rules, and the adapter contract if you mount the handler yourself.

The handler takes a standard `Request` and returns a standard `Response`, so it
runs anywhere the Web platform does. CI bundles the server entry for a Worker
and boots it under real workerd on every change.

## What init writes

Run `npx vendo init --framework custom`. Detection lands here on its own when
the host is neither Next.js nor Express.

It generates `vendo/server.ts`: a lazy composition that takes `Request` in,
returns `Response`, and reads the environment per call.

```ts vendo/server.ts highlight={13,14,15,16,17} theme={null}
import { createAnthropic } from "@ai-sdk/anthropic";
import { cloudConnections, cloudSandbox, cloudTools, createVendo, guard, hostedStore } from "@vendoai/vendo/server";

let vendo: Vendo | null = null;

function getVendo(env: VendoEnv) {
  if (vendo === null) {
    const apiKey = env.VENDO_API_KEY;
    // The VENDO CONSOLE's origin — not your app's. Your app's public URL is VENDO_BASE_URL.
    const consoleUrl = (env.VENDO_CONSOLE_URL ?? "https://console.vendo.run").replace(/\/+$/, "");
    const cloud = { apiKey, baseUrl: consoleUrl };
    vendo = createVendo({
      models: { default: createAnthropic({ apiKey: cloud.apiKey, baseURL: `${cloud.baseUrl}/api/v1` })("vendo") },
      store: hostedStore(cloud),
      connections: cloudConnections(cloud),
      connectors: [cloudTools(cloud)],
      sandbox: cloudSandbox(cloud),
      guard: guard({ policy: {} }),
    });
  }
  return vendo;
}

export function handleVendoRequest(request: Request, env: VendoEnv) {
  return getVendo(env).handler(request);
}
```

Every Cloud seam is named here on purpose. A Worker has no ambient environment
to read, so nothing is left for the key to fill on its own.

***

## Route your runtime through it

```ts Cloudflare Workers highlight={4} theme={null}
// wrangler.toml: compatibility_flags = ["nodejs_compat"]
import { handleVendoRequest } from "./vendo/server";

export default { fetch: (request: Request, env: VendoEnv) => handleVendoRequest(request, env) };
```

```ts Bun · Deno · Hono highlight={1} theme={null}
app.all("/api/vendo/*", (c) => handleVendoRequest(c.req.raw));
```

The client side does not change. Mount `<VendoProvider>` with your base path and
put `<VendoOverlay />` inside it.

***

## The three rules

<Steps>
  <Step title="Construct lazily.">
    Workers forbids async work in module scope, and environment variables only
    exist per request there. Keep the lazy-singleton shape above.
  </Step>

  <Step title="Pass every adapter.">
    The default model ladder and the local store engines need Node, so on a
    Worker they refuse with guidance instead of half working. The generated
    wiring names all of them, which is why it runs unchanged.
  </Step>

  <Step title="Set VENDO_BASE_URL.">
    Your deployed app's full public URL, path prefix included.
    Present-credential forwarding fails closed without it.
  </Step>
</Steps>

On the edge the Cloud gateway is spelled out by hand — the stock Anthropic
provider pointed at the console:

```ts highlight={1} theme={null}
createAnthropic({ apiKey: VENDO_API_KEY, baseURL: `${VENDO_CONSOLE_URL}/api/v1` })("vendo")
```

Call `vendoModel()` on a Worker and it throws that same sentence back at you.

<Note>
  `@vendoai/vendo/sandbox/edge` type-checks against vendored `lib` bytes, so install the
  exact `typescript` its `EDGE_TYPESCRIPT_VERSION` names. The peer range is wide
  and will not nudge you there.
</Note>

***

## Mounting the handler yourself

Skip the generated adapter and mount `vendo.handler` by hand, and your adapter
has to do five things.

| Rule                                                     | Why                                                        |
| -------------------------------------------------------- | ---------------------------------------------------------- |
| Pass GET, POST, PUT, PATCH, and DELETE through unchanged | The wire routes on method                                  |
| Never buffer the body of `POST /threads`                 | That is the streaming reply                                |
| Rebuild the URL from `req.originalUrl`, not `req.url`    | Express strips the mount path from `req.url`               |
| Keep cookie and authorization headers reachable          | `principal(request)` and present-mode tool calls read them |
| Return multi-value `Set-Cookie` as an array              | A joined string loses cookies                              |

`vendo doctor` judges an unknown-framework host by its wiring, never by another
framework's file layout. A missing server half reports
[`E-WIRE-007`](/production/troubleshooting/e-wire-007), a missing client half
[`E-WIRE-008`](/production/troubleshooting/e-wire-008).
