# Vercel Deployment ZPan supports Vercel as a first-class deploy target using Vercel Functions (Node.js runtime) + [Turso](https://turso.tech) as the database and an external S3-compatible bucket for storage. > **Edge runtime is not supported.** `@aws-sdk/client-s3` requires Node.js APIs (streams, crypto) that are unavailable in the Edge runtime. All Vercel Functions for ZPan run on the `nodejs22.x` runtime. ## Prerequisites | Tool | Purpose | |------|---------| | [Vercel account](https://vercel.com) | Hosts the application | | [Turso database](https://turso.tech) | libSQL-compatible remote database | | S3-compatible storage | File storage (Cloudflare R2 recommended — free egress) | | `vercel` CLI | Local development and linking | ## Required Secrets Set these in your fork's GitHub repository under **Settings → Secrets and variables → Actions**: | Secret | Description | |--------|-------------| | `VERCEL_TOKEN` | Vercel API token. Create at [vercel.com/account/tokens](https://vercel.com/account/tokens) | | `VERCEL_ORG_ID` | Your Vercel team/org ID. Found in `vercel link` output or team settings | | `VERCEL_PROJECT_ID` | Project ID after first `vercel link`. Found in `.vercel/project.json` | | `TURSO_DATABASE_URL` | Turso database URL, e.g. `libsql://your-db.turso.io` | | `BETTER_AUTH_URL` | Your Vercel deployment URL, e.g. `https://your-app.vercel.app` | ### Optional Secrets | Secret | Description | |--------|-------------| | `TURSO_AUTH_TOKEN` | Turso auth token. Create with `turso db tokens create your-db`. Required for remote Turso URLs; can be omitted for `file://` local databases. | | `BETTER_AUTH_SECRET` | Signing secret for auth sessions. If not provided, the workflow generates one on first deploy and persists it in your Vercel project env. Back it up from the Vercel dashboard before rotating. To bring your own: `openssl rand -base64 32`. | | `TRUSTED_ORIGINS` | Comma-separated list of additional trusted origins. | ## Quick Start (Fork + Deploy) 1. **Fork** the `saltbo/zpan` repository. 2. **Create a Turso database:** ```sh turso db create zpan-db turso db tokens create zpan-db ``` 3. **Link your Vercel project** locally (first time only): ```sh cp deploy/vercel/vercel.json vercel.json pnpm exec vercel link # Note the VERCEL_ORG_ID and VERCEL_PROJECT_ID from .vercel/project.json ``` 4. **Add the required secrets** to your fork (see table above). `BETTER_AUTH_SECRET` is optional — the workflow auto-generates one on first deploy and stores it in your Vercel project env. 5. **Push to `main`** — the `deploy-vercel.yml` workflow runs automatically and deploys to production. ## Local Development Install the Vercel CLI and run: ```sh pnpm add --global vercel cp deploy/vercel/vercel.json vercel.json # Using a local libSQL file (no Turso token needed) TURSO_DATABASE_URL=file:./zpan.db \ BETTER_AUTH_SECRET=$(openssl rand -base64 32) \ vercel dev ``` The app will be available at `http://localhost:3000`. ## Build Output `pnpm build:vercel` produces: | Path | Contents | |------|---------| | `dist/` | React SPA static assets | | `api/entry-vercel.js` | Hono API compiled as a Vercel Function (ESM) | `vercel.json` routes: - `/api/*` and `/health` → Vercel Function - All other paths → `dist/index.html` (SPA) ## Entitlement Refresh (License Cert) ZPan refreshes its entitlement certificate every 6 hours. On Vercel there is no persistent process, so you need to trigger a refresh via an external scheduler. ### Setup 1. **Generate a secret:** ```sh openssl rand -hex 32 ``` 2. **Add the env var** in your Vercel project settings (**Project → Settings → Environment Variables**): | Variable | Value | |----------|-------| | `REFRESH_CRON_SECRET` | The random string from step 1 | 3. **Schedule the call** using [Vercel Cron Jobs](https://vercel.com/docs/cron-jobs). Add a `crons` entry to your `vercel.json`: ```json { "crons": [ { "path": "/api/licensing/refresh-cron?secret=", "schedule": "0 */6 * * *" }, { "path": "/api/licensing/traffic-sync-runs?secret=", "schedule": "*/10 * * * *" } ] } ``` Replace `` with the value of `REFRESH_CRON_SECRET`. If `REFRESH_CRON_SECRET` is not set, the endpoint returns `401` for all requests. ## Pricing Notes - **Hobby (free)** — suitable for personal and non-commercial use. 100 GB-hours of function compute per month. - **Pro ($20/mo)** — required for commercial use per Vercel's [fair-use policy](https://vercel.com/docs/limits/fair-use-policy). Includes team features, higher limits, and SLA. - **Turso** — free tier includes 500 databases and 9 GB of total storage. - **Cloudflare R2** — recommended S3-compatible storage. Free egress (no bandwidth charges between Vercel and R2 if using the same region is not required — R2 has zero egress fees globally).