Deploy from your browser
One-click Cloudflare deploys from Quickback Start — connect your account, click Deploy, and the hosted broker provisions resources, applies migrations, and ships the Worker. No CLI, no tokens in the browser.
Build a backend in Quickback Start, get a green compile, and ship it to your own Cloudflare account with one click — databases created, migrations applied, secrets minted, Worker live on a workers.dev URL. No local tooling required.
This page covers the browser flow. For local projects, or anything the browser flow doesn't support yet, use quickback deploy — the CLI covers every resource type and is always available.
Browser deployment is new. The hosted deploy service sits behind a kill switch, and provisioning is rate-limited to 5 deploys per minute per user — enough to stop a runaway retry loop, not something a normal deploy will ever hit. quickback deploy works for everyone today.
Browser vs CLI
| Deploy from Start | quickback deploy | |
|---|---|---|
| Where you work | start.quickback.dev chat | Your terminal, local project |
| Cloudflare auth | OAuth consent (no tokens handled by you) | wrangler login / CLOUDFLARE_API_TOKEN |
| Resource coverage | The supported matrix below, fail-closed | Everything the compiler emits |
| Custom domains | Not yet — workers.dev only | Yes, via your wrangler.toml routes |
| Best for | Chat-built apps, first deploys, demos | Local development, full-featured apps, CI |
How a deploy runs
When you click Deploy this app, the pipeline runs four phases and reports each one:
- Compile — your project compiles on the hosted compiler and emits a deployment manifest: the exact set of resources, migrations, and secrets the deploy will touch. Anything the browser flow can't deploy fails here, with a named reason — never as a half-deployed app.
- Provision — each D1 database, KV namespace, and R2 bucket is created in your account, or adopted if a resource with the same name already exists. Retries and redeploys never create duplicates. Nothing is ever deleted automatically.
- Package — the project recompiles with the real resource ids folded in and is bundled into Worker modules (plus your CMS/Account SPA assets, when enabled). Every byte is hash-verified against the manifest before anything is uploaded.
- Finalize — migrations apply, the Worker uploads with its bindings, secrets are set, and the
workers.devroute is enabled. Start probes the URL and only shows Live once it actually answers.
Provision hands back a one-time approval that finalize must present, so the upload can only ever be the one that was approved — being signed in is deliberately not enough on its own. The approval is good for an hour, which no normal deploy comes close to.
A typical deploy takes one to two minutes, most of it compilation.
Naming the Worker
The card's Worker name field seeds itself from your project's name and decides what the deploy is called. It appears as soon as your project compiles green — before you connect Cloudflare — so naming the app isn't gated behind an OAuth round trip. That single name drives the Worker and every resource derived from it — <name>-auth, <name>-features, and any audit, webhook, or file-metadata databases your features pull in.
Start remembers the name per chat, so redeploying the same conversation updates the same app in place. Editing it deploys a separate copy: a new Worker, on new databases, at a new URL. The previous app keeps running untouched — nothing is renamed or deleted in your Cloudflare account, so a name you've finished with is yours to clean up in the dashboard.
Names follow Cloudflare's rule — lowercase letters, digits, hyphens, and underscores, starting with a letter or digit, up to 64 characters. The field normalizes as you type and blocks the deploy rather than letting the broker reject the name a minute in.
What deploys
Supported today, end to end:
| Resource | Notes |
|---|---|
| D1 databases | All of a project's databases — auth, features, and (when your features pull them in) audit, webhooks, and file-metadata |
| KV namespace | The KV binding for rate limiting and session overflow |
| R2 bucket | Managed file storage (managed: true). The bucket is created; the R2 API-token secrets (R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY) are not. Profile-picture and org-logo upload stay off unless those secrets are set — see Avatars |
| Realtime | The inline Broadcaster Durable Object, including its class registration |
| Email binding | The Cloudflare Email Service binding is attached (see the delivery caveat below) |
| CMS / Account UI | The bundled SPAs upload as static assets on the same Worker, with your app's real URL baked in |
Blocked, fail-closed at the compile phase — the deploy stops with the reason, and quickback deploy remains the path for these:
| Blocked | Why |
|---|---|
| Webhooks, embeddings | Need queue and Workers AI bindings the broker can't create yet |
| Vectorize | Not yet supported |
| Neon / Postgres | Browser deploys are D1-only; Neon projects deploy via the CLI |
| Separate-worker realtime | Needs a second Worker + service binding |
Custom apps: mounts | Typically need custom domains |
Custom bindings: (Durable Objects, queues, services, extra R2 buckets) | Not represented by the deploy contract yet |
| Secrets you supply (R2 API tokens, third-party API keys, etc.) | The browser flow has no way to collect them yet — a project that requires one is refused rather than deployed broken. For listing photos, store q.url() instead of enabling fileStorage, or download and wrangler secret put the three R2_* secrets |
| Hyperdrive, public R2 buckets, containers, workflows | Out of MVP scope |
Email delivery: the email binding is attached so your app boots and auth works, but actually sending mail requires Email Sending to be configured on your Cloudflare account with a verified sending domain — which a fresh workers.dev app doesn't have. Start's builder defaults new apps to email + password without email verification for exactly this reason; enable verification, OTP, or magic links after you've configured a sending domain.
Connecting your Cloudflare account
Deploying takes two distinct grants, and the card labels them differently on purpose:
- Continue with Cloudflare signs you in to Quickback. Identity only — none of the deploy permissions below.
- Grant deploy access gives Quickback permission to deploy into your Cloudflare account — a standard OAuth consent against Cloudflare's own dashboard, shown by the Quickback OAuth client. You pick the account, see exactly which permissions are requested, and can revoke at any time from Cloudflare under My Profile → Access Management → Connected Applications (or disconnect from Start).
One click covers both: if you weren't signed in yet, sign-in chains straight into the deploy consent. First-time users see two Cloudflare screens back to back; after that, Cloudflare skips consent screens it has already approved, so re-authenticating is a ribbon of silent redirects.
What's requested, and why:
| Scope | Used for |
|---|---|
openid, user-details.read | Identifying the connection (your email is stored masked, e.g. p***@example.com) |
memberships.read | The account picker — so you never copy a 32-character account id |
account-settings.read | Looking up your workers.dev subdomain to build your app's URL |
workers-scripts.write | Uploading the Worker, enabling its route, setting its secrets |
d1.write | Creating databases and applying migrations |
workers-kv-storage.write | Creating the KV namespace |
workers-r2.write | Creating file-storage buckets |
Nothing broader is requested — no zone, DNS, billing, or account-management access.
If you cancel at Cloudflare's consent screen — or Cloudflare ends the attempt for any other reason — you land back in Start with the card explaining what happened and a Try again button. Nothing is stored on a cancelled attempt, and a connection you already had is left untouched.
Connection states and recovery
The deploy card is the whole connection UI: it reports the state of your Cloudflare connection and offers exactly one thing to do about it.
| What you see | What happened | What to do |
|---|---|---|
| "Grant Quickback permission to deploy…" | Signed in, but no deploy permission stored yet | Grant deploy access |
| Masked email badge, green | Connected and healthy | Deploy |
| Badge turns amber, "· reconnect" | Grant revoked in Cloudflare, or the connection was deleted (plain expiry never surfaces — tokens refresh server-side) | Reconnect Cloudflare — one consent, same connection record |
| "…also linked to a different Quickback login" | This Cloudflare account is connected under another Quickback account too — apps deployed there are listed there, not here | Nothing, unless an app you expected is missing: sign in with the Quickback account you deployed it from |
| "This Cloudflare connection can't …" | The grant is missing a permission a deploy needs (an older connection, or a scope your OAuth client didn't allow) | Reconnect Cloudflare to grant the rest |
| "This Cloudflare login isn't a member of any account" | The identity you connected belongs to no Cloudflare account | Create or join an account in the Cloudflare dashboard, then reconnect |
| "Couldn't reach the deploy service…" | Transient failure talking to Quickback or Cloudflare | Try again |
| "Browser deploys aren't fully configured on the server yet" | A deploy-service misconfiguration, not anything on your side | Use quickback deploy for now |
| "Too many deploys in a minute" | More than 5 provisions in a minute on one account | Wait for the Retry-After window and click Deploy again |
| "This deploy's approval was no longer valid…" | Provision approves one specific upload, for an hour. A deploy that outlives its approval is refused at the finalize step — nothing was uploaded | Try again — a fresh deploy re-provisions and re-approves |
Deploy is disabled while a reconnect is pending, so a deploy never starts on a connection that can't finish it. If a connection dies during a deploy, the failure comes back as a reconnect prompt rather than a raw error — the deploy is safe to re-run after reconnecting, because provisioning adopts and migrations apply once.
Reconnecting
Reconnect re-runs the same consent and lands on the same connection record — your account picker, deploys, and admin password reset all keep working. Reconnecting is also how you widen a grant that's missing a permission.
Connecting more than one Cloudflare account
Connect a second Cloudflare login with Connect another and the card grows a login picker above the account picker. Start remembers which one you deployed with last and preselects it.
To make every Cloudflare login land on the same Quickback account (rather than each minting its own), link them under Connected accounts in the Account UI — after that, "Continue with Cloudflare" signs you in to that one account no matter which Cloudflare identity your dashboard is holding.
Disconnecting vs. revoking
Two different things, and you usually want both:
- Disconnect (in the card) makes Quickback forget the connection and its stored token. It doesn't touch anything in your Cloudflare account, and it never touches deployed apps — they're plain Workers in your account and keep running.
- Revoke (Cloudflare dashboard → My Profile → Access Management → Connected Applications, linked from the card) withdraws the grant itself. The stored ciphertext becomes useless immediately; the next thing Start tries turns into a reconnect prompt.
Where your token lives
The OAuth token never reaches your browser, the chat model, or the compiler. It is held by the deploy service and:
- encrypted at rest with AES-256-GCM under a versioned key-encryption key;
- bound to its connection record (the ciphertext is unusable if copied elsewhere);
- decrypted only inside the API calls of an active deploy, then discarded.
The grant is standing permission carried by short-lived credentials. The token that can actually touch your account expires every hour; the deploy service refreshes it server-side, so expiry never reaches you as a prompt — a leaked working token goes stale within the hour either way. What persists is the sealed refresh token, which can only mint the next short-lived token.
Revoking in Cloudflare (My Profile → Access Management → Connected Applications) kills the whole chain immediately — the stored ciphertext becomes useless, and the card turns the next failed call into a reconnect prompt. See Connection states and recovery for the full set.
Secrets on the deployed app
BETTER_AUTH_SECRETis minted by the deploy service — 256 bits of fresh entropy per app — and set on the Worker directly. It's never shown to the browser or stored in your project.- R2 presign secrets are not minted.
defineFileStorage("cloudflare-r2")requiresR2_ACCOUNT_ID,R2_ACCESS_KEY_ID, andR2_SECRET_ACCESS_KEY. The card creates the bucket; it cannot create an R2 API token (Cloudflare OAuth has no API-Tokens-Write scope). Finalize stops withuser-secret-missinguntil those exist. Store aq.url()on the listing instead, or download the project andwrangler secret putafter creating a token at R2 → Manage API Tokens. - Existing Worker secrets are always preserved. Cloudflare's upload API replaces a Worker's bindings wholesale; the deploy explicitly carries every existing secret across, so a redeploy can never silently delete a secret you set by hand.
Redeploys are safe by construction
Click Deploy as often as you like:
- Provisioning adopts — existing resources are matched by name and reused, never duplicated, never deleted.
- Migrations apply exactly once — a ledger in each database records every applied migration with a content hash. Already-applied migrations are skipped (even when a recompile renames the file), and a migration whose content changed after it ran refuses the deploy outright instead of corrupting the schema.
- Schema changes generate incremental migrations — Start carries the previous deploy's migration history forward, so editing a table produces a
0001that alters it rather than a0000that recreates it. The history is recorded only after migrations actually apply, so a deploy that fails mid-flight leaves the last known-good state in place. - Assets are content-addressed — a redeploy with unchanged CMS/Account assets uploads zero bytes.
- Realtime classes register once — Durable Object registrations follow Cloudflare's append-only chronology; a redeploy sends nothing new.
Changing your schema between browser deploys. Supported — edit a table and redeploy. Start carries the previous deploy's migration history forward, so the compiler generates an incremental 0001, 0002, … rather than rewriting the initial migration. Your data stays put.
The history is keyed to the deploy name, which is what makes renaming a clean slate: a new name provisions fresh databases and starts from a fresh 0000, while redeploying the same name evolves the existing ones.
If you rename an app and then rename it back, or deploy the same name from a Start instance that never held its history, the compiler regenerates the initial migration and the ledger refuses it:
migration "quickback/drizzle/features/0000_quickback.sql" already ran against this
database with different content. Applying it again would leave the schema in a
state neither version describes.That refusal is the safe outcome — it stops before touching your schema. Deploy under a new name for a fresh app, or download the project and use quickback deploy, which keeps quickback/drizzle/ next to your sources and owns the history locally.
After it's live
Start keeps a chip above the composer naming the app this chat deployed, with links straight into its CMS, Account UI, and API. It is read from local storage rather than the deploy card's live state, so it survives a reload — refreshing no longer loses the way back to your app.
Your app answers at https://<app-name>.<your-subdomain>.workers.dev:
- API-only apps serve a small landing page at
/(browsers) and a JSON service descriptor (everything else), linking/api/v1,/openapi.json, and/health— never a bare 404 on first visit. - Apps with the Account UI or CMS redirect
/to the login page or CMS. Both ship by default from Start, at/cms/and/account/. The CMS admits organization members (cms.access: "member") — your first deploy seeds you asownerof the first organization (anduser.role === "sysadmin") — see Owner access. /healthreports status and whether email is configured.
The deployed app is a normal Worker in your account: logs, metrics, and settings live in your Cloudflare dashboard, and you can take over management with the CLI at any time — download the project from Start and quickback deploy picks up the same resources.
That handover works because a successful deploy writes the real Cloudflare resource ids back into your quickback.config.ts:
database: {
name: "cloudflare-d1",
config: {
splitDatabases: true,
authBinding: "AUTH_DB",
featuresBinding: "DB",
authDatabaseId: "8f2c…", // written by the deploy
featuresDatabaseId: "b91e…", // written by the deploy
},
},Those ids identify the exact databases serving your app, so the download, the file tree, and any copy you hand to another tool all carry them. Without them quickback deploy can only match your resources by name — which silently creates a second, empty set if you deploy from a different Cloudflare login or rename the app.
Keep those id keys when you edit the config. Deleting them points your next deploy at fresh, empty databases rather than the ones holding your data. If they do go missing, redeploying from Start writes them back.
Not yet available
- Owner login in deployed apps ("Continue with Cloudflare" as the app's admin sign-in) — the UI half ships with the Account SPA, but the server half and its per-app OAuth client are still in progress.
- Custom domains for browser-deployed apps —
workers.devonly for now. - The blocked resources listed above.
These all work — or will work first — through quickback deploy.
Deploying
One-command deploys with quickback deploy — creates missing Cloudflare resources (D1, KV, R2), writes the real ids back into your config, applies migrations, and ships the worker.
Owner access after a browser deploy
How you become your deployed app's first admin — a seeded password, shown once, then your app's own login.