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

# Vercel Deployment

> Guide for deploying Avocado Studio to Vercel.

# Deploy to Vercel

This guide covers deploying Avocado Studio to Vercel.

## Overview

The recommended deployment splits across two platforms:

* **Site + Content Studio** on Vercel (static/serverless hosting)
* **Orchestrator** on a long-running host (Render, Fly.io, Railway, or any container platform)

Two deployment phases are supported: public site only (Phase 1) or full Content Studio stack (Phase 2).

### Required now (Phase 1)

1. `apps/site` must render published content without requiring orchestrator at runtime.
2. Add production fallback so site never fails to render if orchestrator is unavailable.
3. Disable or protect `__editor=1` in production.
4. Deploy only `apps/site` as a single Vercel project.

## Current architecture (from this repo)

* `apps/site` is a Next.js app.
* `apps/orchestrator` is a Fastify API that persists session state to a local SQLite database via `better-sqlite3` (`.data/orchestrator.db` + WAL).
* `apps/editor` is a Vite app that currently hard-codes local URLs.
* The site currently reads **draft** content from `ORCHESTRATOR_URL` (`/draft/pages`, `/draft/slugs`).

## Important decision first

Pick one deployment target:

1. Public website only (recommended for launch)
2. Full Content Studio + orchestration stack on Vercel

***

## 1) Public website only (recommended)

Use this if you want a stable public site and do not need live editing in production.

### Required changes

1. Decouple `apps/site` from draft API at runtime.

* Today, `apps/site` fetches draft pages from orchestrator.
* For production, render from a published source (shared seed content, JSON, CMS, or database).
* Keep orchestrator integration optional (for dev/editor mode only).

2. Add a published content source for site rendering.

* Option A: Use `demoPublishedPages()` from `packages/shared` as initial published content.
* Option B: Load from a real storage backend (recommended long term).

3. Add production fallback behavior.

* If orchestrator is unavailable, site should still render published pages (not `Page not found`).

4. Restrict or disable editor mode in production.

* `__editor=1` should be disabled or protected.
* Avoid exposing editor bridge behavior publicly by default.

### Vercel config

Create one Vercel project for `apps/site`:

* Root Directory: `apps/site`
* Install Command: `pnpm install --frozen-lockfile`
* Build Command: `pnpm --filter @ai-site-editor/site build`
* Output: Next.js default

### Environment variables (site)

* `ORCHESTRATOR_URL` (optional in public-only mode; required only if you keep remote draft fetches)
* `NEXT_PUBLIC_EDITOR_ORIGIN` (optional; only if editor mode is enabled)

***

## 2) Full Content Studio stack on Vercel (site + orchestrator + Content Studio)

Use this only if you want production Content Studio workflows.

### Required code changes

1. Remove hard-coded local origins from `apps/editor/src/App.tsx`.

* Replace:
  * `editorOrigin = "http://localhost:4100"`
  * `siteOrigin = "http://localhost:3000"`
  * `orchestrator = "http://localhost:4200"`
* With Vite env vars (`import.meta.env.VITE_*`).

2. Remove hard-coded local defaults in `apps/site`.

* `DEFAULT_EDITOR_ORIGIN` should come from env (`NEXT_PUBLIC_EDITOR_ORIGIN`) instead of `http://localhost:4100`.
* Keep `ORCHESTRATOR_URL` required for editor-enabled production.

3. Rework orchestrator persistence.

* Current implementation persists to a local SQLite file (`better-sqlite3`).
* Vercel functions are ephemeral and have read-only filesystems outside `/tmp`; a local SQLite file is not durable across invocations.
* Move state to a network-accessible store (Postgres, Neon, Turso/libSQL, Redis/KV, etc.).

4. Rework orchestrator runtime model for Vercel.

* Current API starts a standalone server via `app.listen(...)`.
* For Vercel, expose request handlers/functions instead of long-running process assumptions.

5. Tighten CORS in orchestrator.

* Current CORS is `origin: true` (too open for production).
* Restrict to known site/Content Studio origins via env-based allowlist.

6. Review streaming endpoint behavior (`/chat/stream`).

* Ensure it fits your Vercel function execution limits and plan.
* Add fallback to non-streaming `/chat` if needed.

### Vercel projects

Set up separate projects:

1. `site` project (Next.js)

* Root: `apps/site`
* Env: `ORCHESTRATOR_URL`, `NEXT_PUBLIC_EDITOR_ORIGIN` (if used)

2. `orchestrator` project (API)

* Root: `apps/orchestrator`
* Env: `OPENAI_API_KEY`, model env vars, storage connection env vars, CORS allowlist

3. Content Studio project (optional)

* Root: `apps/editor`
* Env: `VITE_EDITOR_ORIGIN`, `VITE_SITE_ORIGIN`, `VITE_ORCHESTRATOR_URL`

***

## Minimal change list before first Vercel publish

If the goal is to publish quickly, do these first:

1. Implement published rendering path in `apps/site` that does not require orchestrator.
2. Disable/protect production Content Studio mode.
3. Deploy `apps/site` only on Vercel.

Then add orchestrator/Content Studio deployment as phase 2.

## Deferred (Phase 2, later)

* Deploy `apps/orchestrator` on Vercel-compatible runtime.
* Move orchestrator state to durable external storage.
* Migrate Content Studio/site/orchestrator origins to env-only configuration.
* Lock CORS to explicit allowlist.
* Deploy `apps/editor` (if production editing is required).

***

## Suggested rollout plan

1. Phase 1: Public site only on Vercel (stable, low risk)
2. Phase 2: Externalize orchestrator state storage
3. Phase 3: Deploy orchestrator + Content Studio with locked CORS and env-driven origins

***

## Recommended Beta Split (Avoid Production Overwrite)

Use separate URLs/environments for production and Content Studio staging.

### Production site (stable)

* Vercel project/branch: `main`
* URL: public production URL (for end users)
* Env:
  * `ORCHESTRATOR_URL` unset
  * `NEXT_PUBLIC_ENABLE_EDITOR=0`

Behavior: serves committed `apps/site/lib/published-content.json` only.

### Staging site (Content Studio preview target)

* Vercel project or branch: `beta-editor` (recommended separate project URL)
* URL: staging URL
* Env:
  * `ORCHESTRATOR_URL=https://<orchestrator-host>`
  * `NEXT_PUBLIC_ENABLE_EDITOR=1`
  * `NEXT_PUBLIC_EDITOR_ORIGIN=https://<editor-host>`

Behavior: can read live draft content from orchestrator for editing.

### Content Studio app

* Vercel project root: `apps/editor`
* Env:
  * `VITE_SITE_ORIGIN=https://<staging-site-host>` (not production)
  * `VITE_ORCHESTRATOR_URL=https://<orchestrator-host>`
  * `VITE_PUBLISH_TOKEN=<same as orchestrator PUBLISH_TOKEN>` (if enabled)

### Orchestrator

* Host on Render (or equivalent long-running host)
* Env:
  * `PUBLISH_GIT_BRANCH=beta-editor`
  * `ORCHESTRATOR_CORS_ORIGINS=https://<staging-site-host>,https://<editor-host>`

Behavior: publish writes to beta branch, preventing accidental updates to production `main`.

***

## Production troubleshooting

### Block selector / overlay not working

The block selector requires a working postMessage channel between Content Studio iframe and site. Check these in order:

1. **Draft mode not enabled** — without `VITE_SITE_DRAFT_SECRET` on the Content Studio, the iframe URL uses `__editor=1` which is ignored in production. Set `VITE_SITE_DRAFT_SECRET` on the Content Studio and `DRAFT_MODE_SECRET` on site (matching values).

2. **`NEXT_PUBLIC_ENABLE_EDITOR` not set** — the site's `editorMode` is `false` in production unless `NEXT_PUBLIC_ENABLE_EDITOR=1` is set. Without it, `PreviewBridge` never mounts and blocks have no click handlers.

3. **Origin mismatch in postMessage** — `event.origin` never has a trailing slash. If `NEXT_PUBLIC_EDITOR_ORIGIN` or `VITE_SITE_ORIGIN` has a trailing slash, all messages are silently dropped. Always omit trailing slashes.

4. **Missing `/api/editor/pages`** — the Content Studio calls this on the site to seed the orchestrator with initial content. Without it, the orchestrator has no draft pages and the site shows "Draft unavailable".

5. **CORS on orchestrator** — `ORCHESTRATOR_CORS_ORIGINS` must include both the Content Studio and site origins.

### Required env vars (full stack)

| Component          | Variable                      | Notes                                         |
| ------------------ | ----------------------------- | --------------------------------------------- |
| **Orchestrator**   | `OPENAI_API_KEY`              | or `ANTHROPIC_API_KEY`                        |
|                    | `ORCHESTRATOR_PUBLIC_ORIGIN`  | its own public URL                            |
|                    | `ORCHESTRATOR_CORS_ORIGINS`   | comma-separated Content Studio + site origins |
| **Site**           | `ORCHESTRATOR_URL`            | orchestrator public URL                       |
|                    | `NEXT_PUBLIC_ENABLE_EDITOR=1` | enables editor mode                           |
|                    | `NEXT_PUBLIC_EDITOR_ORIGIN`   | Content Studio URL, **no trailing slash**     |
|                    | `DRAFT_MODE_SECRET`           | shared secret with Content Studio             |
| **Content Studio** | `VITE_SITE_ORIGIN`            | site URL, **no trailing slash**               |
|                    | `VITE_SITE_DRAFT_SECRET`      | must match site `DRAFT_MODE_SECRET`           |
|                    | `VITE_ORCHESTRATOR_URL`       | orchestrator public URL                       |

### Orchestrator on Render

The orchestrator runs via `tsx src/index.ts` (not compiled JS). `tsx` is a production dependency. The build step is `tsc --noEmit` (typecheck only). Render build command: `pnpm install --frozen-lockfile && pnpm --filter @ai-site-editor/orchestrator build`.
