Self-hosting
The MVP deployment uses Vercel for Next.js, a Cloudflare Worker with KV for generated image caching and private optional user backgrounds, and Turnstile for upload verification. Built-in wallpapers can run without the Worker, but custom uploads cannot.
Requirements
- Node.js 24.x and npm
- a Vercel account
- a Cloudflare account with Workers, KV, and Turnstile
- Git
No application database or content-provider key is required for the MVP. KV holds active-day lesson and cache records, normalized custom images, and minimal custom-upload lifecycle metadata.
Local development
npm.cmd install
Copy-Item .env.example .env.local
npm.cmd run dev
Open http://localhost:3000. Without cache environment variables, the wallpaper route generates and returns images directly.
Environment contract
| Variable | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_SITE_URL | production | Canonical public origin |
NEXT_PUBLIC_SHORTCUT_URL | no | Published universal iCloud Shortcut |
NEXT_PUBLIC_CF_ANALYTICS_TOKEN | no | Cookie-free aggregate web analytics |
NEXT_PUBLIC_TURNSTILE_SITE_KEY | for uploads | Public Turnstile widget key |
CACHE_WORKER_URL | for cache | Deployed Worker origin |
CACHE_WORKER_SECRET | for cache | Private lookup and upload HMAC |
CACHE_SIGNING_SECRET | for cache | Public asset URL signature |
TURNSTILE_SECRET | for uploads | Server-side Turnstile verification secret |
TURNSTILE_HOSTNAMES | for uploads | Exact comma-separated production hostnames |
CRON_SECRET | for cron | Protects the rollover route |
Generate long, independent random values for each secret. Never prefix a secret with NEXT_PUBLIC_.
Cloudflare Worker and KV
- Create a KV namespace.
- Replace the placeholder namespace ID in
worker/wrangler.jsonc. - Add
CACHE_WORKER_SECRETandCACHE_SIGNING_SECRETwithwrangler secret put. - Run
npm.cmd run worker:deploy. - Save the returned Worker URL for Vercel.
The Worker defaults to workers.dev. Restrict or move it behind a custom route
if your deployment policy requires it. No R2 subscription or database is
needed. On the Workers Free plan, KV operations fail after a free allowance is
exhausted rather than becoming usage charges.
Create a managed Turnstile widget restricted to the real WallCab hostnames.
The client action is custom_background_upload; the server rejects another
action or hostname. Use Cloudflare's documented test keys only on localhost.
Vercel
Import the GitHub repository, keep the framework preset on Next.js, select Node.js 24.x, and add the environment values above. vercel.json schedules one rollover call at 23:00 UTC. The cron is an optimization; lazy provider fetching and rendering remain the fallback.
Promote a preview only after exercising the configurator, custom upload and deletion, API redirect, source credits, and Shortcut flow.
Provider diagnostics
Every wallpaper response includes X-WallCab-Content-Mode and X-WallCab-Content-Provider. The configurator reads /api/wallpaper/status to show the same result beside its live preview.
Vercel Runtime Logs receive structured wallcab.content and wallcab.wallpaper events with the UTC date, resolved category, provider, and fallback reason. They contain no IP address or user preference identifier.
For an authenticated snapshot of every category already prepared or lazily generated today:
Invoke-RestMethod `
-Uri "https://your-domain.example/api/internal/status" `
-Headers @{ Authorization = "Bearer $env:CRON_SECRET" }
The status route is read-only and does not call an external content provider. A category appears as not_generated until the rollover manifest or a wallpaper request has prepared it.
Docker status
Docker is intentionally deferred from the MVP. A container image needs a clear persistent-cache story, health checks, and documented Sharp native dependencies before it becomes a supported deployment route. It appears on the public roadmap, but no placeholder Dockerfile is shipped today.