Agent Skills: Paste the PEM key (-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----)

Self-contained deploy automation — invoke directly, do not decompose. Deploys a Vibes app to Cloudflare Workers with subdomain registry. Uses KV for storage and native Web Crypto for JWT verification.

UncategorizedID: popmechanic/vibes-cli/cloudflare

Install this agent skill to your local

pnpm dlx add-skill https://github.com/popmechanic/vibes-cli/tree/HEAD/skills/cloudflare

Skill Files

Browse the full folder contents for cloudflare.

Download Skill

Loading file tree…

skills/cloudflare/SKILL.md

Skill Metadata

Name
cloudflare
Description
Self-contained deploy automation — invoke directly, do not decompose. Deploys a Vibes app to Cloudflare Workers with subdomain registry. Uses KV for storage and native Web Crypto for JWT verification.

Plan mode: If you are planning work, this entire skill is ONE plan step: "Invoke /vibes:cloudflare". Do not decompose the steps below into separate plan tasks.

  ╔═══════════════════════════════════════════════╗
  ║   ☁️  CLOUDFLARE WORKERS DEPLOY               ║
  ║   KV Registry · JWT Auth · Edge Functions     ║
  ╚═══════════════════════════════════════════════╝

Deploy to Cloudflare

Deploy your Vibes app to Cloudflare Workers with the subdomain registry.

Prerequisites

  1. Cloudflare account with Workers enabled
  2. Wrangler CLI installed (npm install -g wrangler)
  3. KV namespace created for registry storage

Quick Deploy

cd skills/cloudflare/worker
npm install
wrangler deploy

Environment Setup

Before deploying, set the required secrets:

cd skills/cloudflare/worker
npx wrangler secret put CLERK_PEM_PUBLIC_KEY
npx wrangler secret put CLERK_WEBHOOK_SECRET

Deploy Script

For deploying with static assets (index.html, bundles, assets):

node scripts/deploy-cloudflare.js --name myapp --file index.html --clerk-key "pk_test_xxx"

The --clerk-key flag auto-fetches the PEM public key from Clerk's JWKS endpoint and sets it as CLERK_PEM_PUBLIC_KEY. Without it, the Worker can't verify JWTs for authenticated endpoints like /claim.

This automatically:

  • Copies index.html to worker's public/
  • Copies bundles/*.js (fireproof-vibes-bridge.js + fireproof-clerk-bundle.js)
  • Copies assets/ directory (images, icons)
  • Runs wrangler deploy

Endpoints

| Endpoint | Method | Description | |----------|--------|-------------| | /registry.json | GET | Public registry read | | /check/:subdomain | GET | Check subdomain availability | | /claim | POST | Claim a subdomain (auth required) | | /webhook | POST | Clerk subscription webhooks | | /api/ai/chat | POST | AI proxy to OpenRouter (requires OPENROUTER_API_KEY) |

KV Storage

The registry is stored in Cloudflare KV under the key registry. Schema:

{
  "claims": { "subdomain": { "userId": "...", "claimedAt": "..." } },
  "reserved": ["admin", "api", "www"],
  "preallocated": {}
}

Important: Custom Domain Required for Subdomains

Workers.dev domains only support one subdomain level for SSL. For multi-tenant apps with subdomains (tenant.myapp.workers.dev), you MUST use a custom domain.

Won't work: tenant.myapp.username.workers.dev (SSL error) Will work: tenant.myapp.com (with custom domain)

On workers.dev, use the ?subdomain= query parameter for testing:

  • myapp.username.workers.dev → landing page
  • myapp.username.workers.dev?subdomain=tenant → tenant app
  • myapp.username.workers.dev?subdomain=admin → admin dashboard

Custom Domain Setup

  1. Add domain to Cloudflare (get nameservers from Cloudflare DNS dashboard)
  2. Point registrar nameservers to Cloudflare's assigned nameservers
  3. Delete conflicting DNS records for the apex domain (A, AAAA, CNAME)
  4. Add Custom Domain in Workers & Pages → your worker → Settings → Domains & Routes → Add → Custom Domain (apex: yourdomain.com)
  5. Add wildcard CNAME in DNS: Name: *, Target: <worker-name>.<username>.workers.dev (Proxied: ON)
  6. Add Route in Workers & Pages → your worker → Settings → Domains & Routes → Add → Route: *.yourdomain.com/*

After setup:

  • yourdomain.com → landing page
  • tenant.yourdomain.com → tenant app
  • admin.yourdomain.com → admin dashboard

Required Secrets

| Secret | Source | Purpose | |--------|--------|---------| | CLERK_PEM_PUBLIC_KEY | Clerk JWKS endpoint | JWT signature verification | | PERMITTED_ORIGINS | Your domains | JWT azp claim validation | | CLERK_WEBHOOK_SECRET | Clerk dashboard | Webhook signature verification | | OPENROUTER_API_KEY | OpenRouter dashboard | AI proxy for useAI() hook (optional) |

Setting secrets:

cd skills/cloudflare/worker
npx wrangler secret put CLERK_PEM_PUBLIC_KEY
# Paste the PEM key (-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----)

npx wrangler secret put CLERK_WEBHOOK_SECRET
# Paste the webhook signing secret from Clerk dashboard

npx wrangler secret put PERMITTED_ORIGINS
# Enter: https://yourdomain.com,https://*.yourdomain.com

Getting CLERK_PEM_PUBLIC_KEY:

  1. Find your Clerk Frontend API URL in Clerk dashboard (e.g., clerk.yourdomain.com)
  2. Fetch JWKS: curl https://clerk.yourdomain.com/.well-known/jwks.json
  3. Convert JWK to PEM using Node.js:
const crypto = require('crypto');
const jwk = { /* paste the key from jwks.json */ };
const pem = crypto.createPublicKey({ key: jwk, format: 'jwk' }).export({ type: 'spki', format: 'pem' });
console.log(pem);

Deploy with --name Flag

Always use the --name flag to deploy to your app's worker:

node scripts/deploy-cloudflare.js --name myapp --file index.html

Important: The --name determines the worker URL. Without it, wrangler uses the name from wrangler.toml (vibes-registry), not your app.

AI Features

Apps using the useAI() hook call /api/ai/chat on the same origin. The worker proxies these requests to OpenRouter.

Deploy with AI enabled:

node scripts/deploy-cloudflare.js --name myapp --file index.html --clerk-key "pk_test_xxx" --ai-key "sk-or-v1-your-key"

The --ai-key flag sets the OPENROUTER_API_KEY secret on the worker after deployment. Without it, /api/ai/chat returns {"error": "AI not configured"}.

Manual setup:

npx wrangler secret put OPENROUTER_API_KEY --name myapp
# Paste your OpenRouter API key

Troubleshooting

| Problem | Cause | Fix | |---------|-------|-----| | wrangler: command not found | Wrangler not installed | npm install -g wrangler or use npx wrangler | | KV namespace errors | Namespace doesn't exist or wrong ID | Run npx wrangler kv namespace list to verify | | JWT verification fails (401) | Missing or wrong PEM key | Check CLERK_PEM_PUBLIC_KEY is set: npx wrangler secret list --name <app> | | JWT azp mismatch (403) | Origins not configured | Set PERMITTED_ORIGINS to include your domain | | 404 on subdomain URL | Workers.dev doesn't support nested subdomains | Set up a custom domain (see Custom Domain Setup above) | | /api/ai/chat returns "AI not configured" | Missing OpenRouter key | Set OPENROUTER_API_KEY secret or redeploy with --ai-key | | wrangler deploy auth error | Not logged in | Run npx wrangler login | | Stale content after redeploy | Browser cache | Hard refresh (Cmd+Shift+R) or clear cache |

What's Next?

After successful deployment, present these options:

AskUserQuestion: question: "Your app is deployed! What would you like to do next?" header: "Next steps" options: - label: "Set up custom domain" description: "Configure DNS for subdomain routing (required for multi-tenant)" - label: "Enable AI features" description: "Add OpenRouter API key for the useAI() hook" - label: "Add billing & auth" description: "Transform into SaaS with /vibes:sell, then redeploy" - label: "Open in browser" description: "Visit the deployed URL to verify everything works"