Files
zpan/docs/deploy/vercel.md
T
Jasper Van 29102e623d feat: v2.6 Z6 — 6h background entitlement refresh (#345)
- Add server/services/licensing-refresh-runner.ts: shared runner with
  5-min dedup guard, structured INFO logs, and no-op for unbound state
- Add workers/scheduled.ts + export scheduled() in workers/bootstrap.ts
  for CF Workers cron (every 6 hours)
- Add [triggers] crons = ["0 */6 * * *"] to wrangler.toml
- Add setInterval refresh on boot in server/entry-node.ts with
  "licensing.refresh.scheduler.started interval=6h" log
- Add POST /api/licensing/refresh-cron?secret=... public endpoint
  (timing-safe secret comparison) for non-CF platforms
- Extract ZPAN_CLOUD_URL_DEFAULT to shared/constants.ts, replacing
  four duplicated literals
- Document REFRESH_CRON_SECRET + scheduler setup in all 5 non-CF
  deploy guides (vercel, netlify, aws-lambda, azure-functions, cloud-run)

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-24 08:22:10 -04:00

4.7 KiB

Vercel Deployment

ZPan supports Vercel as a first-class deploy target using Vercel Functions (Node.js runtime) + Turso 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 Hosts the application
Turso database 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
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:

    turso db create zpan-db
    turso db tokens create zpan-db
    
  3. Link your Vercel project locally (first time only):

    cp deploy/vercel/vercel.json vercel.json
    npx 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 master — the deploy-vercel.yml workflow runs automatically and deploys to production.

Local Development

Install the Vercel CLI and run:

npm install -g 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

npm run 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:

    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. Add a crons entry to your vercel.json:

    {
      "crons": [
        {
          "path": "/api/licensing/refresh-cron?secret=<YOUR_SECRET>",
          "schedule": "0 */6 * * *"
        }
      ]
    }
    

    Replace <YOUR_SECRET> 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. 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).