# Auth and deployment

> Route auth walk on eveChannel, env vars and secrets, eve link/deploy flows, Vercel build output (.vercel/output), sandbox backend selection, prewarm constraints, and production verification.

- Repository: vercel/eve
- GitHub: https://github.com/vercel/eve
- Human docs: https://grok-wiki.com/public/docs/vercel-eve-759e1d74a10f
- Complete Markdown: https://grok-wiki.com/public/docs/vercel-eve-759e1d74a10f/llms-full.txt

## Source Files

- `docs/guides/auth-and-route-protection.md`
- `docs/guides/deployment.md`
- `packages/eve/src/cli/commands/link.ts`
- `packages/eve/src/cli/commands/deploy.ts`
- `packages/eve/src/internal/nitro/host/configure-nitro-routes.ts`
- `packages/eve/src/execution/sandbox/prewarm.ts`

---

---
title: "Auth and deployment"
description: "Route auth walk on eveChannel, env vars and secrets, eve link/deploy flows, Vercel build output (.vercel/output), sandbox backend selection, prewarm constraints, and production verification."
---

Production Eve agents run the same Nitro host and `/eve/v1` HTTP contract locally and on Vercel. `agent/channels/eve.ts` defines the inbound auth policy; `eve build` compiles `.eve/` artifacts and, when `VERCEL` is set, emits `.vercel/output`; `eve link` and `eve deploy` wire the project to Vercel and push a production deployment.

## Route auth on `eveChannel`

The default HTTP channel (`eveChannel` from `eve/channels/eve`) runs `routeAuth` on every session route before any model work starts. Authored in `agent/channels/eve.ts` (or omitted to fall back to the framework default).

| Route | Auth |
| --- | --- |
| `POST /eve/v1/session` | `routeAuth` walk |
| `POST /eve/v1/session/:sessionId` | `routeAuth` walk |
| `GET /eve/v1/session/:sessionId/stream` | `routeAuth` walk |
| `GET /eve/v1/health` | Always public (no walk) |

`GET /eve/v1/info` uses the framework default chain `[localDev(), vercelOidc()]` independently of the authored channel file.

### Ordered auth walk

`auth` accepts a single `AuthFn` or an ordered array. `routeAuth` walks entries until one accepts:

| Outcome | Behavior |
| --- | --- |
| Returns `SessionAuthContext` | Accept request; stop walk |
| Returns `null` / `undefined` | Skip to next entry |
| Throws `UnauthenticatedError` / `ForbiddenError` | Reject with structured 401 / 403 |
| Every entry skips (including `auth: []`) | Reject with 401 |

```ts title="agent/channels/eve.ts"
import { eveChannel } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";

export default eveChannel({
  auth: [localDev(), vercelOidc()],
});
```

Put application-specific providers first; entries that do not recognize the caller return `null` and fall through. To reject with a precise status instead of skipping:

```ts
import { ForbiddenError, UnauthenticatedError } from "eve/channels/auth";

throw new UnauthenticatedError({
  code: "authentication_required",
  message: "Sign in to continue.",
}); // 401

throw new ForbiddenError({ message: "Not allowed on this workspace." }); // 403
```

Custom channels built with `defineChannel` can call `routeAuth(request, auth)` to reuse the same semantics.

### Shipped verifier helpers

| Helper | Use when |
| --- | --- |
| `localDev()` | Local development on loopback hostnames (`localhost`, `*.localhost`, `127.0.0.0/8`, `::1`); also accepts `vercel dev` (`VERCEL=1` + `VERCEL_ENV=development`) |
| `vercelOidc()` | Vercel deployments; verifies bearer JWTs from Vercel OIDC |
| `none()` | Explicit anonymous access as the final entry |
| `httpBasic(...)` | Shared username/password for operators or services |
| `jwtHmac(...)` / `jwtEcdsa(...)` | Shared-secret or asymmetric JWT verification |
| `oidc(...)` | Arbitrary OIDC issuer |
| `placeholderAuth()` | Scaffold guardrail — see below |

<Warning>
`localDev()` keys off the request URL hostname, not bare `process.env.VERCEL`. Never rely on `localDev()` alone on public origins; layer a real authenticator in front.
</Warning>

### `vercelOidc()` acceptance rules

- Tokens whose `project_id` matches `VERCEL_PROJECT_ID` are always accepted (internal runtime and subagent callers need no enumeration).
- Tokens with `external_sub` authenticate as `principalType: "user"` when `project_id` matches `VERCEL_PROJECT_ID` and environment matches `VERCEL_TARGET_ENV` / `VERCEL_ENV`. Profile claims (`name`, `picture`, `email`) surface in `ctx.session.auth.current.attributes`.
- Tokens from other Vercel projects require an explicit `subjects` list (AWS IAM-style `*` wildcards). Build patterns with `vercelSubject({ teamSlug, projectName, environment? })` — `environment` defaults to `"production"`.

```ts
import { vercelOidc, vercelSubject } from "eve/channels/auth";

vercelOidc({
  subjects: [
    vercelSubject({ teamSlug: "partner", projectName: "data" }),
    vercelSubject({ teamSlug: "acme", projectName: "agent", environment: "*" }),
  ],
});
```

### Scaffold and framework defaults

`eve init` scaffolds:

```ts
import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";

export default eveChannel({
  auth: [localDev(), vercelOidc(), placeholderAuth()],
});
```

`placeholderAuth()` returns `null` outside production. In production it throws a structured 401 (`code: "eve_production_auth_not_configured"`) so browser clients see a clear message instead of an internal error. Replace it before the first production browser request.

Deleting `agent/channels/eve.ts` falls back to the framework default `[localDev(), vercelOidc()]`, which also rejects unauthenticated production browser traffic.

### Caller snapshot in runtime

Route auth populates `ctx.session.auth`:

| Field | Meaning |
| --- | --- |
| `auth.current` | Caller on the active inbound turn |
| `auth.initiator` | Caller that started the durable session (pinned across follow-ups) |

Follow-up messages update `auth.current` but leave `auth.initiator` unchanged. Both are `null` only on internal paths (subagents) that never went through an authored route.

<Info>
Route auth decides access at the HTTP boundary. There is no second per-session ownership ACL stacked on top.
</Info>

## Environment variables and secrets

Set secrets in the Vercel project environment (or `.env.local` locally). Route-auth secrets and provider keys are never serialized into `.eve/` compiled artifacts; the runtime re-materializes them from the authored channel definition at boot.

### Model credentials

<ParamField body="VERCEL_OIDC_TOKEN" type="string">
Vercel OIDC token pulled by `eve link` / `vercel env pull`. Gateway model ids authenticate through Vercel OIDC with no provider keys to manage.
</ParamField>

<ParamField body="AI_GATEWAY_API_KEY" type="string">
Direct AI Gateway API key alternative to OIDC.
</ParamField>

<ParamField body="OPENAI_API_KEY" type="string">
Example direct provider key when not using the gateway.
</ParamField>

### Route auth secrets

Reference from `agent/channels/eve.ts` auth helpers — for example `ROUTE_AUTH_BASIC_PASSWORD` for `httpBasic(...)`, or JWT/OIDC signing keys for `jwtHmac` / `jwtEcdsa` / `oidc`.

### Vercel deployment context

| Variable | Role |
| --- | --- |
| `VERCEL` | Set during hosted builds and Vercel runtime; triggers `.vercel/output` emission and Vercel sandbox backend selection |
| `VERCEL_ENV` | `production`, `preview`, or `development`; used by `placeholderAuth()` and OIDC user-token environment matching |
| `VERCEL_TARGET_ENV` | Deployment target environment for OIDC user-token matching |
| `VERCEL_PROJECT_ID` | Current project binding for `vercelOidc()` and stable sandbox template scoping |
| `VERCEL_DEPLOYMENT_ID` | Enables build-time sandbox prewarm; keys source-graph templates |
| `VERCEL_AUTOMATION_BYPASS_SECRET` | Local bypass for Vercel preview protection when driving remote deployments with `eve dev <url>` |

<Note>
Do not set `VERCEL_TEAM_ID` at build time. Sandbox template keys must derive identically at build and runtime, and Vercel exposes no team variable at runtime.
</Note>

## `eve link` and `eve deploy`

### `eve link`

Interactive only — team and project pickers are the point of the command.

<Steps>
<Step title="Prerequisites">
Run from an Eve project root (`eve init` scaffold present).
</Step>
<Step title="Link">
```bash
eve link
```
Select team and project. Eve runs `vercel link`, then `vercel env pull` so a model credential lands in `.env.local`.
</Step>
<Step title="Verify">
Confirm `VERCEL_OIDC_TOKEN` or `AI_GATEWAY_API_KEY` appears in an env file. A running `eve dev` reloads env files automatically.
</Step>
</Steps>

Re-linking shows the current link and offers "Link to another project"; the new choice is authoritative.

In CI (non-TTY):

```bash
vercel link --project <name> --yes
vercel env pull
```

### `eve deploy`

Deploys to Vercel production (`vercel deploy --prod`), installing dependencies first and pulling environment variables after. Sets `VERCEL_USE_EXPERIMENTAL_FRAMEWORKS=1` for the deploy subprocess.

| Link state | TTY | Behavior |
| --- | --- | --- |
| Already linked | Any | Deploy immediately |
| Unlinked | Interactive | Walk `eve link` pickers, then deploy |
| Unlinked | Non-interactive | Exit with guidance to run `eve link` first |

```bash
eve deploy
```

On success the command prints the production URL when available.

## Build output

### `eve build`

```bash
eve build
```

Always writes Eve compiled artifacts under `.eve/`:

:::files
.eve/
├── discovery/
│   ├── agent-discovery-manifest.json
│   └── diagnostics.json
└── compile/
    ├── compiled-agent-manifest.json
    ├── compile-metadata.json
    └── module-map.mjs
:::

When `VERCEL` is set (every hosted Vercel build), `eve build` also writes the Vercel Build Output API bundle under `.vercel/output`. A plain local `eve build` skips that bundle.

### Vercel build sequence

```mermaid
flowchart TB
  subgraph build ["eve build (VERCEL set)"]
    A["compile .eve/ artifacts"] --> B["build Nitro app surface → .vercel/output"]
    B --> C["runVercelBuildPrewarm"]
    C --> D["build workflow flow surface"]
    D --> E["emit workflow functions into .vercel/output"]
    E --> F["emit agent-summary.json → .eve/"]
  end
```

Prewarm runs after the app Nitro surface is built but before workflow function bundling, so a prewarm failure aborts the build before spending time on function output that would never deploy.

`.eve/agent-summary.json` lives outside `.vercel/output` (not part of the deployable bundle).

Co-deployed Next.js apps may also write `.vercel/output/config.json` for route rewrites; see framework integration docs for `withEve` mounting.

## Sandbox backend selection

Attach a backend on the sandbox definition in `agent/sandbox/sandbox.ts` (or `agent/sandbox.ts`).

### Explicit Vercel backend

```ts title="agent/sandbox/sandbox.ts"
import { defineSandbox } from "eve/sandbox";
import { vercel } from "eve/sandbox/vercel";

export default defineSandbox({
  backend: vercel(),
});
```

### `defaultBackend()`

Omit `backend` and Eve falls back to `defaultBackend()` (`defaultSandbox`), which picks at first use:

| Priority | Backend | Condition |
| --- | --- | --- |
| 1 | `vercel()` | `process.env.VERCEL` is set |
| 2 | `docker()` | Docker daemon reachable |
| 3 | `microsandbox()` | Platform supported (Apple Silicon macOS or glibc Linux with KVM) |
| 4 | `justbash()` | Dependency-free fallback |

Selection is cached for the process lifetime. Pin a backend unconditionally with `vercel()`, `docker()`, `microsandbox()`, or `justbash()` when you need deterministic behavior.

<Warning>
If a Vercel sandbox template is not provisioned at runtime, Eve throws `SandboxTemplateNotProvisionedError` with guidance to run `eve build` or `prewarmAppSandboxes()` first.
</Warning>

## Build-time sandbox prewarm

During hosted Vercel builds, Eve prewarms reusable Vercel Sandbox templates so the first session avoids cold-start cost.

### When prewarm runs

Prewarm executes only when **both** `VERCEL` and `VERCEL_DEPLOYMENT_ID` are set (`shouldPrewarmVercelBuild()`). Dev runs and one-off local builds skip platform template provisioning.

### When a sandbox is skipped

`createRuntimeSandboxTemplateKey` returns `null` (no prewarm target) when the template plan is `kind: "none"`:

- No `bootstrap()` hook **and**
- No workspace seed files under `agent/sandbox/workspace/`

Sandboxes with only `onSession()` and no seeds do not get a prewarm template.

### Template keying

| Template kind | Keyed by |
| --- | --- |
| `workspace-content` (seed files only) | Skills and workspace file contents (`contentHash`) |
| `bootstrap` | Optional `revalidationKey()`, authored sandbox source hash, and seed contents |
| `source-graph` | Full source graph hash |

Each graph node (`nodeId`) produces a distinct template key so root and subagent sandboxes do not collide.

Build logs report each template as `reused cached` or `built`. Summary line: `initialized N sandbox templates (X reused, Y built)`.

<Check>
Prewarming covers template construction only. `onSession()` still runs at runtime, once per session.
</Check>

<Warning>
If build-time prewarm fails, the build fails. The same bootstrap would break at runtime.
</Warning>

## Production verification

<Steps>
<Step title="Build locally or on Vercel">
```bash
eve build
```
Confirm `.eve/discovery/diagnostics.json` has no blocking errors. On Vercel, confirm prewarm lines in the build log.
</Step>
<Step title="Deploy">
```bash
eve deploy
```
Or push to a Git-connected Vercel project.
</Step>
<Step title="Health probe">
```bash
curl https://<your-app>/eve/v1/health
```

<ResponseExample>
```json
{"ok":true,"status":"ready","workflowId":"..."}
```
</ResponseExample>
</Step>
<Step title="Create a session">
```bash
curl -X POST https://<your-app>/eve/v1/session \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer <token>' \
  -d '{"message":"Hello from production"}'
```

<ResponseExample>
```json
{"ok":true,"sessionId":"<id>","continuationToken":"eve:..."}
```
</ResponseExample>
</Step>
<Step title="Attach to the stream">
```bash
curl https://<your-app>/eve/v1/session/<sessionId>/stream \
  -H 'authorization: Bearer <token>'
```
</Step>
</Steps>

For interactive smoke tests against a remote deployment:

```bash
eve dev https://<your-app>
```

Set `VERCEL_AUTOMATION_BYPASS_SECRET` locally first when the deployment uses Vercel preview protection.

### Pre-production checklist

- [ ] `eve build` succeeds; writes `.vercel/output` when `VERCEL` is set
- [ ] Model credential and route-auth secrets set in Vercel env vars
- [ ] Sandbox backend matches environment (`vercel()` or `defaultBackend()`)
- [ ] Build-time prewarm reused or built templates without failing
- [ ] `placeholderAuth()` replaced with real policy
- [ ] `eve deploy` succeeds
- [ ] Health, session, and stream routes respond on the deployment URL

## Related pages

<CardGroup>
<Card title="Security model" href="/security-model">
Trust boundaries, secret brokering, and the pre-production security checklist.
</Card>
<Card title="Channels" href="/channels">
`defineChannel`, platform factories, and webhook verification beyond the default Eve HTTP channel.
</Card>
<Card title="Sandbox" href="/sandbox">
Workspace seeding, `bootstrap`/`onSession` lifecycle, and proxy execution for shell/file tools.
</Card>
<Card title="CLI reference" href="/cli-reference">
Full `eve link`, `eve deploy`, and `eve build` flag and artifact reference.
</Card>
<Card title="HTTP API" href="/http-api">
Request/response shapes, NDJSON stream events, and stable route inventory.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Discovery diagnostics, 401 failure modes, and build/prewarm errors.
</Card>
</CardGroup>
