- 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>
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-s3requires Node.js APIs (streams, crypto) that are unavailable in the Edge runtime. All Vercel Functions for ZPan run on thenodejs22.xruntime.
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)
-
Fork the
saltbo/zpanrepository. -
Create a Turso database:
turso db create zpan-db turso db tokens create zpan-db -
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 -
Add the required secrets to your fork (see table above).
BETTER_AUTH_SECRETis optional — the workflow auto-generates one on first deploy and stores it in your Vercel project env. -
Push to
master— thedeploy-vercel.ymlworkflow 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
-
Generate a secret:
openssl rand -hex 32 -
Add the env var in your Vercel project settings (Project → Settings → Environment Variables):
Variable Value REFRESH_CRON_SECRETThe random string from step 1 -
Schedule the call using Vercel Cron Jobs. Add a
cronsentry to yourvercel.json:{ "crons": [ { "path": "/api/licensing/refresh-cron?secret=<YOUR_SECRET>", "schedule": "0 */6 * * *" } ] }Replace
<YOUR_SECRET>with the value ofREFRESH_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).