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

# Quickstart: Express and other JavaScript servers

> A fetch handler you can mount in any Node-compatible runtime. vendo init scaffolds the adapter for Express; elsewhere you mount vendo.handler yourself.

Vendo's handler is framework-agnostic. It takes a standard `Request` and returns
a standard `Response`, so it runs in Express, Fastify, Hono, Bun, or any
Node-compatible runtime. The umbrella requires Node 20 or later.

## Install and scaffold

Install the umbrella and run `vendo init` from your app root:

```bash theme={null}
npm install @vendoai/vendo
npx vendo init
```

Init always writes the `.vendo` contract (tools, policy, theme, brief). It reads
`package.json` to decide what code to propose:

* **Express** (an `express` dependency): a diff-gated `vendo/server.ts`
  (`vendo/server.mjs` when the host has no `tsconfig.json`) holding a
  `createVendo()` composition and a Node HTTP-to-fetch adapter, plus a starter
  `vendo/ai.ts` model module. Two manual wiring steps remain, and init prints
  both.
* **Any other runtime** (Fastify, Hono, Bun): init detects only `next` and
  `express`, so it proposes Next-shaped files. Decline those diffs and mount
  `vendo.handler` yourself (see below). Detection checks `next` first, so an app
  with both dependencies gets the Next scaffold.

## Wire the Express adapter

Mount the exported adapter under one base path, normally `/api/vendo`:

```ts theme={null}
import express from "express";
import { mountVendo } from "./vendo/server.js";

const app = express();
app.use("/api/vendo", mountVendo());
app.listen(3000);
```

Wrap the client root in `<VendoRoot>`:

```tsx theme={null}
import { VendoRoot } from "@vendoai/vendo/react";

root.render(
  <VendoRoot>
    <App />
  </VendoRoot>,
);
```

## Composition

The generated `vendo/server.ts` calls `createVendo` once during boot:

```ts theme={null}
import { createVendo } from "@vendoai/vendo/server";
import { model } from "./ai";

const vendo = createVendo({
  model,
  principal: async () => null,
});
```

Replace `principal` with your session resolver. It receives the `Request` and
returns a `Principal`, or `null` for an anonymous session:

```ts theme={null}
principal: async (request) => {
  const user = await resolveSession(request);
  return user ? { kind: "user", subject: user.id } : null;
},
```

## Mount the handler directly

Outside Express, mount `vendo.handler` under one base path yourself. It takes a
standard `Request` and returns a standard `Response`. The Node adapter that
`vendo init` generates for Express is the pattern to copy. An adapter must:

* Pass every method (GET, POST, DELETE) through unchanged.
* Preserve streaming for `POST /threads`, never buffering the response body.
* Reconstruct the request URL from `req.originalUrl`, not `req.url`, when mounted
  with `app.use` on a base path (Express strips the mount path from `req.url`).
* Keep cookie and authorization headers available to `principal(request)` and to
  present-mode same-origin tool calls.
* Return multi-value `Set-Cookie` as a header array, not a joined string.

Set `VENDO_BASE_URL` to your trusted host origin — `http://localhost:3000` in
development, your public HTTPS origin in production. It does three jobs.
It gates present-mode credential forwarding, so Vendo passes `authorization`
and `cookie` only to same-origin routes matching this value. It marks the
anonymous-session cookie `Secure` behind a TLS-terminating proxy. And it
provides the base for extracted route tools. When it is unset, present tool
calls run without inbound auth and the composition emits one
`present-credentials-not-forwarded` audit event so the misconfiguration
does not stay silent. `vendo init` writes it into `.env.example` for you.

## Verify

Run `vendo doctor` against the running server:

```bash theme={null}
npx vendo doctor
```

For an Express host it checks that the server composes `createVendo` from
`@vendoai/vendo/server`, that the client mounts `<VendoRoot>`, and makes one live
`/status` round trip. It also runs live HTTP probes that confirm present-mode
credentials reach the host and that `actAs` (if configured) mints material
the host accepts. It reports broken until both wiring steps are done. See
the [CLI reference](/reference/cli#vendo-doctor-dir) for the probe details.

## Persistence

Pass `createStore({ url })` to use hosted Postgres for multi-process or
serverless deployments. The default is PGlite at `.vendo/data`.
