Base44 · full-stack apps in production · internal tracker

ARE WE FULLSTACK YET? NOT YET.

Fullstack readiness 15%

But the POC is real: Astro, React Router 7 and TanStack Start apps deploy through the real base44 CLI and serve direct from Cloudflare on b44apps.dev — immutable deployments, promote/rollback, edge SSR caching — proven end to end on staging. Nothing is merged to production yet, though: ahead lie the merges, the custom-domain last mile (Cloudflare for SaaS), and production hardening. Hence 15%.

Updated July 7, 2026 · 11 in review · 6 in progress · 11 up next · 5 demo apps live on staging

Live demos

Every one deployed by the real CLI to staging — click them.

In review

11

Built and verified end to end against the real staging Cloudflare account and the preview backend — but every PR is still open. Nothing is merged to production.

Deployments API + apps dispatcher Full lifecycle live: create → promote ×2 → rollback → logs, KV routing + edge cache. Live on staging apper #14839 ↗

Full lifecycle proven live: create → finalize → promote ×2 → rollback (returns the previous production deployment) → per-deployment logs. Dispatcher does KV routing, reverse proxy, dumb SSR cache, pre-dispatch rate limiting, private-app edge gating, plus the domains API.

CLI: deploy / promote / rollback / logs / slug / domains The real base44 CLI builds, uploads to Cloudflare, deploys. 12/12 CI checks green. Live on staging cli #559 ↗

The real base44 CLI builds, uploads assets straight to Cloudflare, and deploys via the deployments API. base44 slug set/reset and base44 domains add/list/remove included. Fully green across all 12 CI checks (fixed knip dead exports, a Windows line-endings hash bug, and a Bun charset assertion on the way).

SDK server client createServerClient({ request, env }) — appId/serverUrl from worker env, auth from a cookie. Live on staging sdk #221 ↗

createServerClient({ request, env }) resolves appId/serverUrl from worker env and auth from a cookie. Worker-safety fixes (analytics timer guard, fetch adapter, absolute URLs, no shared-instance leak) ride the same PR; a non-HttpOnly cookie mirror bridges browser → SSR.

Three flagship example apps Estates (RR7), CRM (TanStack Start), Meridian (Astro 6) — builds green, secrets out of client bundles. Live on staging apps-examples #15 ↗

Base44 Estates (RR7), Base44 CRM (TanStack Start), Meridian (Astro 6) — all builds green with the .wrangler/deploy/config.json contract; server client + service token verified absent from client bundles. Bonus find: Astro 6 removed Astro.locals.runtime.env — use import { env } from "cloudflare:workers".

Docs: 9 pages on developers/full-stack-apps Preview verified green; the scary 404s were a transient deploy race, not broken pages. mintlify #1519 ↗

Preview verified green — the scary 404s were a transient deploy race (an auto-llms.txt commit triggered an overlapping build), not broken pages. Mintlify's link-rot crawl across all 9 cross-linked pages passed.

CLI skill documents domains for agents base44 domains add <hostname> [--wait] / list / remove — agents drive the same flow users do. skills #132 ↗

base44 domains add <hostname> [--wait] / list / remove documented so agents drive the same flow users do.

Routing is exactly the slug-from-KV design KV is the dispatcher's ONLY routing source; promote wrote the slug host by itself. Live on staging proof: real slug host ↗

The dispatcher's ONLY routing source is KV: host:<hostname> → {script, deployment_id, app_id}. Slug hosts, custom domains, and immutable dep hosts are all just keys; promote/rollback rewrite only the alias key. Turning it on caught bug #2 — routing_store wrote bare-hostname keys while the dispatcher read host:-prefixed ones. Fixed (45/45 tests green) and proven: promote wrote host:meridian-feb7cfba.b44apps.dev by itself, MISS 946 ms → HIT 124 ms, zero manual routing steps.

Slug changes carry the production URL One UI chokepoint re-points the fullstack routing record — verified live via the real CLI. Live on staging cli #559 ↗

PATCH /api/apps/{id}/metadata/slug (the single UI chokepoint) re-points the fullstack routing record as part of the rename — new host: key written, old deleted, reset-to-auto handled. Verified live with the real CLI on Modal: renames moved the KV record back and forth. estates/crm/meridian.b44apps.dev are now true slug hosts — no manual alias records left.

Login story fixed end to end Apps own /login (email + OTP via the real SDK); OAuth callback validation is app-scoped. Live on staging apper #14839 ↗

All three demo apps now own /login with email/password sign-in + sign-up + OTP via the real SDK surface. The OAuth gateway allowlist was fixed on the preview, and AppDomainValidator now accepts hosts provably owned by the initiating app (slug host / dep-* host / registered custom domains — deliberately app-scoped so one tenant can never capture another app's callback token).

Round-2 click-testing fixes RR7 meta() replacement gotcha, CRM env threading, every mock/fallback removed. Live on staging apps-examples #15 ↗

RR7 gotcha: a leaf route's meta() REPLACES the root's, so base44:app-id tags existed only on / — now rendered in the root layout head everywhere. CRM's browser SDK defaulted to prod base44.app — runtime {appId, apiUrl} now threads from Worker env to every client call site. All mock/fallback data removed: pages render real entity data or honest empty states.

Security directives, all built Dedicated namespace · reverse proxy · private-app edge gate · rate limiting · service token. Live on staging

Dedicated base44-fullstack-staging namespace · dispatcher is a reverse proxy (no CORS, white-label) · private apps gated at the edge · rate limiting wired into the dispatcher · service token injected per the security sign-off.

In progress

6

Started, tracked, and expected to land — but not proven live yet.

Custom-domain last mile: the netanelgilad.com test Wildcard route + fallback DNS bound; two Cloudflare-for-SaaS walls remain.

Wildcard *.b44apps.dev/* Worker route created and fallback.b44apps.dev + apex DNS bound. Two Cloudflare-for-SaaS walls remain (see Up next). Once SaaS is enabled on the zone: create the */* catch-all, then base44 domains add app.netanelgilad.com --wait prints the CNAME, polls TLS to active, and the subdomain serves white-label.

Dispatcher WebSocket pass-through Set-Cookie rewrap turned 101 upgrades into 500s; fix is on the PR branch. apper #14839 ↗

The dispatcher's Set-Cookie rewrap turned 101 upgrades into 500s, so realtime rides a cross-origin WS to the platform origin for now. Pass-through fix is on the PR branch — tracked follow-up until verified live.

Astro 7 live verify on Modal Astro 7 shipped Jun 22; same adapter contract expected — needs a live run.

Astro 7 shipped Jun 22; the examples run Astro 6. Same @astrojs/cloudflare-on-Vite-plugin contract expected — needs a live Modal run to confirm.

Next.js support (vinext, best-effort) Cloudflare's Vite-based Next is the path; OpenNext needs unsupported bindings.

Cloudflare's Vite-based Next (experimental) is the path; OpenNext needs a deploy-time bundle and its cache stack uses unsupported bindings. Also the framework to watch for the 10 MB gzip / 1 s startup limits.

CLI publish output: print the fullstack production URL After a slug rename the CLI still prints the classic published-url.

After a slug rename the CLI still prints the classic published-url. Small follow-up on #559.

One-click wildcard DNS record One proxied * record makes every slug + dep-<id> host resolve automatically.

A proxied * → A 192.0.2.1 record, combined with the wildcard route already created, makes every slug + dep-<id> host resolve automatically — no per-host binds. One dashboard click.

Up next

11

The honest list — unowned platform work, classic↔fullstack divergences, and tracked risks. Why the answer up top isn't "YES."

Enable Cloudflare for SaaS on the b44apps.dev zone THE blocker: Custom Hostnames + fallback origin + SSL-edit token scope. Dashboard clicks. Gap

The catch-all */* route is refused until SaaS is enabled (error 100327), and the custom_hostnames / fallback-origin API rejects wrangler's ssl_certs:write (needs real "SSL and Certificates: Edit"). Dashboard steps: enable Custom Hostnames (free tier: 100), set fallback origin to fallback.b44apps.dev (DNS record already exists), add the SSL-edit scope to the staging token. This is THE blocker for the custom-domain test.

Prod OAuth gateway allowlist for custom domains Preview gateway fixed; prod needs the same before Google login works on customer domains. Gap

ALLOWED_DOMAIN_PATTERN was fixed on the preview gateway (oauth.velino.org); production needs the same change before Google login works on customer custom domains. Flagged as platform work.

Domain purchase flow is classic-only Purchased domains hardwire Render records — fullstack apps can't serve on them at all. Gap

get_entri_config hardcodes CNAME www → base44.onrender.com + A @ → 216.24.57.1 (Render), and the purchase webhook wires Render + DomainLink unconditionally. Nothing branches on the app being fullstack: purchased domains cannot serve fullstack apps without a fullstack-aware record set, CF hostname creation, and an apex strategy.

No cross-system hostname uniqueness The same hostname can be live in classic AND fullstack — whichever DNS points at wins. Gap

Classic checks only custom_domains; fullstack checks only AppCustomDomain for the same app (Cloudflare's per-zone uniqueness is the only backstop, surfaced as an opaque 502). The same hostname can be live in both systems — whichever target DNS points at wins while the other reports stale verified/pending state.

Billing/abuse parity missing in fullstack domains No capability gate, no 350-domain cap, no blocked terms — free tier can attach domains today. Gap

No Capability.CUSTOM_DOMAINS gate, no 350-domain/user cap, no blocked-terms list, no hostname normalization (case / www.) in add_custom_domain — free-tier users can attach domains via CLI/API today.

Lifecycle divergence: deletion orphans fullstack domains App deletion tears down classic DomainLinks but leaves CF hostnames + KV routes serving. Gap

app_cleanup.py tears down classic DomainLinks but never CF custom hostnames / KV routes / AppCustomDomain rows — orphaned hostnames keep serving after app deletion. Fullstack domains also ignore subscription cancellation/resubscribe and admin disable; no unlink/relink, no domain-level or URL redirects.

Email can't be enabled on fullstack-connected domains Custom email is keyed on a DomainLink — a fullstack domain has none, so it 404s. Gap

Custom email sending is keyed on a DomainLink (get_domain_if_valid 404s without one) — a fullstack AppCustomDomain has none, so it's impossible to enable at all today. base44 domains add prints zero email-DNS guidance, and a forced apex CNAME at providers without flattening conflicts with MX and breaks the customer's mail.

OAuth www mismatch + www/apex handling Exact-hostname callback check fails on www. for fullstack; no apex/redirect story yet. Gap

Fullstack callback validation is exact-hostname (no www.-strip like classic), so callbacks on www.<connected-host> pass for classic and fail for fullstack. Classic covers apex+www with one record + redirect; fullstack needs two CNAMEs, has no redirect between variants, and no apex guidance (CNAME flattening required). Minor: b44apps.dev is absent from the classic blocked-terms list.

Script-count cost $0.02/mo per script past 1,000 — prune old deployments by tag. Risk

$0.02/mo per script past 1,000 — prune old deployments by tag (keep last N + anything aliased).

Alias-flip latency KV is ≤60 s worst case; production wants Durable Objects or Instant Purge (<150 ms). Risk

POC uses KV (≤60 s worst case); production upgrade is Durable Objects or cache + Instant Purge (<150 ms).

Private-app edge gate is cookie-presence only POC checks the cookie exists; production must verify the JWT at the edge. Risk

POC checks the auth cookie exists; production must verify the JWT at the edge — ties into the §S service-token production path.

Click any card for the full story — links open the PR or live proof.