| owner | Ops |
|---|---|
| last-verified | 2026-05-13 |
| doc-type | runbook |
This runbook deploys DashClaw as a hosted service, for example hosted.dashclaw.io, where visitors can mint trial workspaces via /connect. It is the ops companion to the hosted provisioning code already in this repo.
Who this is for: the operator standing up the hosted instance. Self-host users do not need this. They should follow QUICK-START.md.
Estimated time: about 45 minutes the first time, about 10 minutes after that.
The current repo already includes the main hosted-mode code paths:
app/api/hosted/workspaces/route.jsfor trial provisioningapp/api/hosted/workspaces/[workspaceId]/route.jsfor admin inspection and deletionapp/api/hosted/cleanup/route.jsfor trial cleanupapp/connect/HostedProvisionSection.jsxandapp/connect/HostedProvisionClient.jsxfor the hosted mint UIscripts/check-hosted-ready.mjsfor pre-deploy readiness checksscripts/smoke-hosted.mjsfor post-deploy smoke testingmigrations/2026-04-18-hosted-trial-columns.sqlfor hosted trial columns and indexvercel.jsoncron entry for/api/hosted/cleanup
What is not done by the repo alone is provisioning external operator infrastructure like Neon, Vercel, Cloudflare Turnstile, and DNS.
- GitHub repo access with push rights to
main - Vercel account
- Neon account
- Cloudflare account
- Optional custom domain
- Go to https://console.neon.tech and create a new project.
- Choose a region close to your Vercel deployment.
- Copy the pooled
DATABASE_URL.
Keep that value safe. You will paste it into Vercel.
- Go to https://dash.cloudflare.com
- Open Turnstile and add a new site.
- Use your future hosted domain, or
*.vercel.appfor initial testing. - Choose
Managedwidget mode. - Copy both keys:
NEXT_PUBLIC_TURNSTILE_SITE_KEYTURNSTILE_SECRET_KEY
Run these locally from any machine with Node installed:
node -e "console.log('CRON_SECRET=' + require('crypto').randomBytes(32).toString('hex'))"
node -e "console.log('HOSTED_CLEANUP_SECRET=' + require('crypto').randomBytes(32).toString('hex'))"
node -e "console.log('DASHCLAW_API_KEY=oc_live_' + require('crypto').randomBytes(16).toString('hex'))"
node -e "console.log('NEXTAUTH_SECRET=' + require('crypto').randomBytes(32).toString('base64url'))"Recommended too, if you do not already have one for the deployment:
node -e "console.log('ENCRYPTION_KEY=' + require('crypto').randomBytes(32).toString('base64url').slice(0,32))"Save all values.
- Go to https://vercel.com/new
- Import the DashClaw repo
- Framework preset should auto-detect as Next.js
- Leave the build command as default, because
vercel.jsonalready sets:
"buildCommand": "node scripts/auto-migrate.mjs && next build"- Add these production environment variables:
| Name | Required | Notes |
|---|---|---|
DASHCLAW_HOSTED |
yes | set to true |
DATABASE_URL |
yes | from Neon |
DASHCLAW_API_KEY |
yes | admin key for hosted workspace inspection and deletion |
ENCRYPTION_KEY |
yes | standard DashClaw production secret |
TURNSTILE_SECRET_KEY |
yes | from Cloudflare |
NEXT_PUBLIC_TURNSTILE_SITE_KEY |
recommended | needed for visible widget rendering |
HOSTED_CLEANUP_SECRET |
recommended | used by GitHub Actions cleanup |
CRON_SECRET |
recommended | used by Vercel cron auth |
HOSTED_TRIAL_DAYS |
optional | defaults to 30 |
HOSTED_TRIAL_ACTION_CAP |
optional | defaults to 10000 |
HOSTED_PROVISION_MAX_PER_IP_PER_DAY |
optional | defaults to 5 |
NEXTAUTH_URL |
yes | final deployment URL |
NEXTAUTH_SECRET |
yes | required by NextAuth |
ALLOWED_ORIGIN |
recommended | set to your hosted app origin |
Then deploy.
Run locally against the same env values you plan to use in production:
DASHCLAW_HOSTED=true DATABASE_URL=<neon-url> TURNSTILE_SECRET_KEY=<turnstile-secret> DASHCLAW_API_KEY=<admin-key> NEXT_PUBLIC_TURNSTILE_SITE_KEY=<turnstile-site> HOSTED_CLEANUP_SECRET=<cleanup> npm run hosted:check-readyExpected result:
[hosted:check-ready] status=ok
After the Vercel deployment is live:
HOSTED_SMOKE_BASE_URL=https://your-deploy.vercel.app DASHCLAW_API_KEY=<admin-key> npm run hosted:smokeExpected result:
[smoke] PASS
Open these pages:
/connect/setup
On /connect, confirm:
- the hosted trial section appears
- the Turnstile widget appears if site key is configured
- clicking the mint button returns a workspace ID, API key, and config snippet
On /setup, confirm the page renders the "Deployment truth surface" with green database, schema, and environment sections. /api/setup/status returns the same report as JSON if you need to diff it against the UI.
- Add your subdomain in Vercel, for example
hosted.dashclaw.io - Add the DNS record Vercel asks for
- Update the Turnstile allowed domain list
- Update
NEXTAUTH_URL - Redeploy
Pick one.
Already configured in vercel.json:
{ "path": "/api/hosted/cleanup", "schedule": "0 3 * * *" }This route accepts:
Authorization: Bearer $CRON_SECRET- or
x-cleanup-secret: $HOSTED_CLEANUP_SECRET - or an authenticated admin request
Add these repo secrets:
DASHCLAW_BASE_URLHOSTED_CLEANUP_SECRET
Then call:
curl -X POST "$DASHCLAW_BASE_URL/api/hosted/cleanup" -H "x-cleanup-secret: $HOSTED_CLEANUP_SECRET"Minimum recommended monitoring:
- Vercel deployment logs, especially
[HOSTED]errors - hosted trial rows in
organizationswherehosted_mode = true - daily cleanup success
- periodic
npm run hosted:smokeagainst production
If hosted provisioning breaks:
- Promote the last known-good Vercel deployment
- Restore Neon if a destructive schema change slipped through
- Reproduce locally with:
DASHCLAW_HOSTED=true npm run devBefore calling the hosted deployment ready, verify all of this:
- Neon database created
- Vercel project deployed successfully
- Hosted env vars added
- Turnstile site and secret keys configured
-
npm run hosted:check-readypasses -
npm run hosted:smokepasses -
/connecthosted trial mint works end to end -
/api/hosted/cleanupworks via chosen cron path - Custom domain and
NEXTAUTH_URLupdated if using branded host
Runbook last verified: 2026-05-13