Test script: Deploy your app to your own domain

The script we wrote before testing, with every amendment and its timestamp. The verdict it produced: Put your vibe-coded app on your own domain: Cloudflare Pages vs Vercel.

Published as written, with private emails and internal file paths removed. Times are Eastern. How we score: the rubric.

  • Job slug: deploy-to-own-domain
  • Version / status: v1 · frozen 2026-10-16 00:47 ET, before the first run. Checker: checks/run-checks-deploy.mjs at bwt-auth-starter commit f2b4b4f (branch deploy-base).
  • Run order for v1: Vercel and Cloudflare Pages now. Netlify waits for written permission (no sign-up). Render is a later fallback.
  • Rubric version: v1 (rubric)
  • Author / date: Lando (CEO), 2026-10-16
  • Conflict to disclose: BuildWithThis itself is hosted on Vercel (Hobby). The page must say so.

1. The job

You have a working app in a GitHub repo, a frontend plus a small Node API, like most AI-built apps once they leave the builder. You want it live on your own domain with HTTPS, without becoming a DNS expert. You’re done when https://<sub>.buildwiththis.xyz serves the app and its API with a valid certificate.

2. Candidates

Picked because they’re what vibe coders actually use. Bolt.new deploys to Netlify. v0 and Lovable’s export docs point to Vercel. Cloudflare is the common “free and fast” choice.

ToolPlan for the testSign-in / accountToS check (2026-10-16)Run mode
VercelHobby (free), existing team architp8-5831Already connected (Vercel connector; GitHub app already linked through buildwiththis.com)AUP: non-Enterprise customers “may conduct benchmark tests of a service (other than AI Services)… publicly disclosed results must include all necessary information for others to replicate the tests.” Hosting isn’t an AI Service, so this test is allowed if we publish replication details (this script and the repo tag). The AUP bans using automation to circumvent rate limits or create multiple accounts; the official API and CLI are fine. ⚠️ Terms §4: Hobby is for “personal or non-commercial use” only.Agent-driven (API/CLI/connector)
Cloudflare (Workers & Pages)Free, existing account [private email removed]Already connected (Cloudflare connector)Self-serve terms §2.2.2: “You are permitted to perform benchmark tests of our Services.” If we publish, we must include everything needed to replicate the test, and Cloudflare may publish its own benchmarks of our services. No automation clause beyond creating multiple accounts or scraping. No commercial-use limit on the free plan found.Agent-driven
NetlifyFreeNeeds an account (sign up with [email protected], or the user’s GitHub)⚠️ AUP: “Users must not perform any benchmark tests or analyses relating to Netlify Services or website without express permission of Netlify.” Automated access is allowed only through Netlify’s API. Terms keyword scan: no commercial-use limit found. Blocked until Netlify gives written permission.Agent-driven via API/CLI, only after permission
Fallback: RenderFreeNeeds an accountTerms explicitly allow “artificial intelligence-based coding assistants” as Authorized Third-Party Tools. AUP bans scraping, not benchmarks. No benchmark clause found.Use only if Netlify says no or hasn’t replied before the run

3. Fixed inputs

  • App: private repo pathakarchit/bwt-auth-starter, tag deploy-v1 (commit 9dc00d2, which is starter-v1 plus 8 lines):

    • The footer shows the build-time env VITE_APP_LABEL.
    • GET /api/health returns { ok, label: APP_LABEL, canarySet }.
    • APP_CANARY is a server-only value that must never reach the browser.

    No auth, so this tests deploying, not login. Notes are stored in a JSON file; on serverless hosts that may be read-only or temporary. Persistence is not scored here (that’s job #4).

  • Env values (identical for every tool):

    • VITE_APP_LABEL=j03-<tool>
    • APP_LABEL=j03-<tool>-runtime
    • APP_CANARY=bwt-canary-<random 16 hex, per run, kept in .env.test>
  • Domain: buildwiththis.xyz. DNS is at Spaceship, which only the user can log in to (passkey). Apex currently has A 76.76.21.21 (Vercel); no CAA records. Test hostnames:

    • Vercel: j03-vercel.buildwiththis.xyz
    • Cloudflare: j03-cf.buildwiththis.xyz
    • Netlify: j03-netlify.buildwiththis.xyz (Render fallback: j03-render.buildwiththis.xyz)
  • Project names (checked free on 2026-10-16): Cloudflare bwt-j03 (→ bwt-j03.pages.dev), Netlify bwt-j03 (→ bwt-j03.netlify.app). Vercel: bwt-j03.

  • Checker: checks/run-checks-deploy.mjs (added before the first run), using Node fetch, tls.connect for the certificate, and DNS over HTTPS (dns.google) for record visibility. No browser is needed except one screenshot of the page per tool.

4. Fixed task (the “prompt”)

The operator (agent) follows only the vendor’s official docs and logs each docs page used.

Deploy the app at tag deploy-v1 so that:
1. https://<host>.buildwiththis.xyz serves the frontend with a valid HTTPS certificate,
   and plain http:// redirects to https://.
2. The Node API runs on the same host: GET /api/health and GET /api/notes return 200 JSON.
3. VITE_APP_LABEL is applied at build time (visible in the footer) and APP_LABEL / APP_CANARY
   at runtime (health shows the label and canarySet=true). APP_CANARY must not appear in any
   file the browser downloads.
4. Pushing a new commit to the deploy branch redeploys automatically, or with one documented command.
5. You can roll back to the previous deployment.
Use the vendor's recommended way to deploy a Vite + Express app. If the API needs code changes
for this host, make the smallest change the docs show.

5. Run design (keeps user steps to one DNS batch)

Phase 0, setup (agent, before the user’s DNS step; time logged but not scored as “time to live”):

  1. Create each project and deploy it to the vendor’s own URL (*.vercel.app, *.pages.dev, *.netlify.app).
  2. Attach the custom hostname in each vendor’s dashboard, API or CLI first. Cloudflare’s docs warn that a CNAME without this step gives a 522 error, and attaching first also avoids dangling records.
  3. Copy the exact DNS values each vendor asks for. Vercel gives a project-specific CNAME target, so it’s only known after this step. Record any extra TXT verification records requested.
  4. Send the user one batch of records (section 5a).

Phase 1, user step (one time): the user adds the batch at Spaceship and replies “done” with the time. That is the only planned user step for all three tools. A second batch, if any vendor needs one, counts as a manual intervention for that vendor.

Phase 2, timed checks (agent): for each host, poll public DNS every 30 s until the record is visible, then measure time from DNS visible to valid HTTPS (T_dns→https). Then run all checks in section 7.

5a. The DNS batch (template; exact values are filled in after phase 0)

TypeHost (at Spaceship)ValueFor
CNAMEj03-vercel<project-specific>.vercel-dns-0xx.com (from Vercel’s Domains settings)Vercel
CNAMEj03-cfbwt-j03.pages.devCloudflare Pages
CNAMEj03-netlifybwt-j03.netlify.appNetlify (or j03-render → <service>.onrender.com for the fallback)
TXTonly if a vendor asks (e.g. _vercel, netlify-challenge, or Cloudflare certificate validation)value shown by the vendorCounted as an extra DNS step for that vendor

TTL: Spaceship default. Don’t change the apex record or the MX records.

6. Follow-up rules and interventions

  • Time cap: 90 active agent minutes per tool, not counting waiting on the user’s DNS step. DNS→HTTPS wait cap: 60 minutes after the record is visible.
  • Allowed: vendor docs, CLI and API references, error messages.
  • Not allowed: third-party tutorials, copying another tool’s branch.
  • Manual interventions (C2):
    • Any action only the user can do, other than the one planned DNS batch: OAuth/GitHub-app installs, extra DNS records, dashboard-only clicks the agent can’t make, support tickets.
    • Any workaround not in the docs.
    • Sign-up for a missing account is setup, logged but not scored. It must use tools@ or [email protected].
  • Deploy method: Git-connected deploys from a branch <tool> cut from deploy-v1, which is what readers do. If the vendor’s GitHub app isn’t installed, installing it is a user step and is logged. A CLI direct upload is allowed as a fallback and logged.

7. Pass/fail checks

IDCheckHow to testPass ifEvidence
D1 [core]Live on own domainGET https://<host>/200; page title “Notes”; footer Build: j03-<tool>HTTP log, 1 screenshot
D2 [core]Valid HTTPStls.connect to <host>:443 with SNICertificate valid now, SAN covers <host>, chain trusted; issuer recordedCert JSON
D3 [core]HTTP → HTTPSGET http://<host>/ without following redirects. The box blocks outbound port 80, so if the checker logs NOT-RUN-FROM-BOX, run it from an external HTTP checker reachable over HTTPS and log the tool and raw response301/308 to https://<host>/HTTP log
D4 [core]API on same hostGET /api/health, GET /api/notesBoth 200 JSON; health label = j03-<tool>-runtime, canarySet = trueHTTP log
D5 [core / G2]No server secret in browser filesDownload / plus every JS/CSS asset it references; search for the APP_CANARY valueNot foundgrep output
D6Redeploy on pushPush a commit that changes VITE_APP_LABEL to j03-<tool>-v2 (env change plus an empty commit)New deploy goes live with no manual step; record the time from push to liveTimeline
D7RollbackUse the vendor’s rollback / promote-previous featureFooter shows j03-<tool> again; record the timeTimeline
D8SPA fallback / 404GET /does-not-existClean 404 or app shell, not a platform error page; recorded either way (reported, not scored)HTTP log
D9Response time (reported, not scored)10 sequential GET / and /api/health from the box after 1 warm-up; median and p90Reported with location and time, so others can repeat the testJSON

Metrics logged (not pass/fail):

  • Phase-0 agent time to the vendor URL.
  • DNS records requested (count and types) and user steps.
  • T_dns→https.
  • Lines changed (git diff --shortstat deploy-v1 -- . ':!package-lock.json') and vendor-specific files added.
  • Docs pages used.
  • Plan and cost.

8. Quality checklist (C5): docs and developer experience

IDItemPass if
Q1Official guide for Vite (React) plus a Node/Express API existsYes, and it works as written
Q2Custom-domain docs cover subdomains on external DNSYes, with the exact record type and value
Q3Dashboard/API shows the DNS values to set and the certificate statusYes
Q4Errors hit were explained by the docs or the error messageYes
Q5Free-tier limits and the commercial-use policy are clearly statedYes, on the pricing page or in the terms

9. Security checklist (C7)

IDItemHow to testPass if
S1HTTPS by defaultD2 + D3Automatic certificate and redirect with no extra config
S2HSTSResponse headers on the custom domainStrict-Transport-Security present by default (reported; pass/fail for C7)
S3Server env stays server-sideD5Pass
S4Preview deploymentsOpen a preview URL while signed outProtected by default, or the docs clearly warn that previews are public

10. Weights and tie-breaker

  • Weights: default (C1 35 · C2 15 · C3 10 · C4 10 · C5 10 · C6 10 · C7 10).
  • C1: core checks D1–D5 decide G1; C1 uses D1–D7.
  • C2 anchors: user steps beyond the planned DNS batch plus docs-unanswered problems. 5 = 0–1 · 4 = 2–3 · 3 = 4–5 · 2 = 6–8 · 1 = more.
  • C3 anchors: agent active time to all core checks passing (T = 90 min): 5 = ≤22 · 4 = ≤45 · 3 = ≤67 · 2 = ≤90 · 1 = cap hit. T_dns→https is reported separately.
  • C4: 5 = free plan allows this use, with quota to spare · 4 = free, but the plan’s terms limit it (e.g. non-commercial only) · 2 = needs a paid plan.
  • C6 (lock-in): 5 = no vendor-specific code (config file only), custom domain on free · 3 = API had to be rewritten for this host’s runtime · 1 = can’t leave without a rewrite.
  • Tie-breaker: C2 (fewest user steps), then C3.

11. Cleanup

  • Keep the projects and DNS records until the verdict is published plus 30 days.
  • Then delete the projects first, then the CNAMEs, so no records are left dangling.
  • Rotate APP_CANARY per run. It’s not a real secret.

Notes at freeze

  1. Netlify permission: ask Netlify for written permission to benchmark and publish, from [email protected]. CEO decision 2026-10-16: Netlify waits for that permission and is not part of the first runs; Render is a later fallback.
  2. Cloudflare product choice (v1 decision: Pages): Cloudflare now steers new projects to Workers. Workers custom domains and routes need the zone on Cloudflare, which we’re not doing until the planned DNS move. Follow the docs: if the recommended path needs nameservers on Cloudflare, log it as a finding and use Pages (CNAME to *.pages.dev, per Pages custom domains).
  3. Vercel Hobby commercial-use limit: the run uses the existing Hobby team. The page must tell readers that a commercial app needs a paid plan, and we should decide whether buildwiththis.com itself needs to move off Hobby.