Built and verified end to end against the real staging Cloudflare account and the preview backend. The big apper PR (#14839) is now split into a merge-ready 10-PR stack + 1 standalone (the dispatcher itself split into sub-PRs 3a–3e) — #14839 stays open as the integration/staging reference (everything was live-verified there); merging happens via the stack. Nothing is merged to production yet.
Hosting infra: models, routing store, CF client Bottom of the stack, base: main — pure infra, zero routes registered, merging is a prod no-op. on hold · model redesign Live on staging apper #15396 ↗ +
AppDeployment + AppProductionAlias models (app-scoped, IDOR-safe), host/URL config with POC-mode pending:// sentinels, KV routing-store writes (host:<hostname> → {script, deployment_id, app_id}), and the CF WfP client extensions (assets-upload-session, KV put/delete, Cloudflare-for-SaaS hostname CRUD + zone lookup). 57 unit tests green, ruff clean. Live-verified on the #14839 staging preview. ON HOLD: the backend layers are being redesigned — AppDeployment becomes AppVersion, likely reusing the existing AppCheckpoint model; Pavel is driving the apper-side design (general preview environments). This PR stays open as reference input to that design.
Deployments control-plane API create → finalize → promote / rollback / list / logs under /api/apps/{id}/deployments. on hold · model redesign Live on staging apper #15397 ↗ +
The full deploy lifecycle on an AppAdminRouter: create reserves the dep-<id> script + opens the Cloudflare assets-upload-session; finalize PUTs the namespaced script (secrets + app-scoped BASE44_SERVICE_TOKEN bindings) and writes the immutable-host route; promote/rollback flip the AppProductionAlias pointer. CLI drives it via the existing apps:deploy workspace-key scope; all mutations audit-logged. ON HOLD: the backend layers are being redesigned — AppDeployment becomes AppVersion, likely reusing the existing AppCheckpoint model; Pavel is driving the apper-side design (general preview environments). This PR stays open as reference input to that design.
Dispatcher core: KV host routing + dispatch Bottom of the dispatcher sub-stack: resolves host:<hostname> from KV, dispatches to the user worker. 3a · mergeable now (based on main) Live on staging apper #15765 ↗ +
The old dispatcher monolith #15398 (3/7) was closed and split into sub-PRs 3a–3e for reviewability — the sub-stack's tip is byte-identical to the monolith, so reviewers see granularity, not new behavior. This slice is the self-contained worker skeleton under workers/base44-apps-dispatcher/ (no backend files change): KV host routing + dispatch into the namespace. The sub-stack was re-founded on main; the dispatcher couples only to the KV RouteRecord contract, so it merges independently of the apper model redesign.
Dispatcher: platform-surface reverse proxy Reverse-proxies /api/apps/* + /ws-user-apps/* same-origin — WebSocket upgrades pass through untouched. 3b · stacks on 3a · independent of backend redesign Live on staging apper #15766 ↗ +
The platform surface rides the app's own origin (no CORS, white-label custom domains), and WebSocket upgrades pass through untouched — the wss://crm.b44apps.dev fix rides here.
Dispatcher: shared SSR cache The dumb SSR cache — inspects nothing on the request, honors only the response's Cache-Control. 3c · stacks on 3b · independent of backend redesign Live on staging apper #15767 ↗ +
Deliberately dumb: cacheability is the app's concern (private / no-store / Set-Cookie opt out). This is the cache behind the proven MISS 1265 ms → HIT 83–96 ms numbers.
Dispatcher: private-app auth gate Edge-gates private apps without ever shadowing an app's own /login. 3d · stacks on 3c · independent of backend redesign Live on staging apper #15768 ↗ +
Private apps are gated at the edge with a redirect to the platform origin's hosted login — the dispatcher never claims /login, so apps keep owning their own login pages.
Dispatcher: rate limiting + kill switch Tier-driven rate limiting pre-dispatch + the kill switch — tip of the dispatcher sub-stack. 3e · stacks on 3d · independent of backend redesign Live on staging apper #15769 ↗ +
Rate limits enforced on the cache-MISS path right before dispatch (the billable event), tier-driven from the KV routing record, plus the kill switch. Tip of the sub-stack — byte-identical to the old #15398 monolith.
Custom domains + OAuth domain validation Cloudflare-for-SaaS hostname connect/list/disconnect + app-scoped OAuth callback acceptance. blocked behind model redesign Live on staging apper #15399 ↗ +
AppCustomDomain model + /api/apps/{id}/domains (connect creates the CF custom hostname with DV cert issuance and routes it at current production). The AppDomainValidator extension is purely additive and deliberately app-scoped — an app's own dep- host, production host, or connected custom domains only, so one tenant can never capture another app's callback token. Inert until full-stack hosts exist, so classic OAuth validation is byte-for-byte unaffected. 75 unit tests green.
Slug renames re-point production routing repoint_production_alias hooks the existing update_slug endpoint — smallest slice in the stack. blocked behind model redesign Live on staging apper #15400 ↗ +
Writes the KV route for the new <slug>. production host and deletes the old one when an app is renamed; no-op when the host is unchanged or the app has no production deployment. Runs after the slug is persisted, so a Cloudflare failure logs instead of failing the rename. Live-verified on the #14839 preview: renames moved the routing record back and forth with zero manual steps.
This tracker app (yes, this very page) are-we-fullstack-yet moved to its own repo (base44-dev/awfy) — no longer part of the apper stack. own repo · out of the stack Live on staging base44-dev/awfy ↗ +
Originally split out as stack PR apper #15401, then moved wholesale to the dedicated base44-dev/awfy repo (the apper PR was closed and the code removed from the monorepo). It documents the initiative (this page replaced the session artifact as the single source of truth) and dogfoods the very deploy pipeline it tracks — the page you're reading ships through the full-stack CLI flow to are-we-fullstack-yet.b44apps.dev.
Probe API — TEMPORARY, staging-only Prod-blocked debugging surface, remove before GA — top of the stack, goes in last (or never). 7/7 · merges last apper #15402 ↗ +
/api/fullstack-probe/* — the hardened scaffolding used to empirically validate every WfP assumption from a PR preview. Fails closed in production: disabled unless FULLSTACK_PROBE_ALLOWED_EMAILS is set (it never is in prod) and every endpoint requires an allowlisted platform user. Kept as its own clearly-marked PR so the eventual removal is a clean revert — everything greppable via FULLSTACK_PROBE.
Agent model ids validated at push time Stale model ids now 400 at push with the supported list, instead of 404ing at first chat. standalone · mergeable now Live on staging apper #15403 ↗ +
Extracted from #14839 but not fullstack-specific, so it rides solo off main. validate_runtime_model_strict rejects anything that isn't a preset or a wire string the runtime actually serves; agent create/replace/update return a 400 naming the agent and listing supported ids. Persisted configs keep the lenient validator on purpose so legacy data still loads. 100 unit tests green.
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 7) — builds green, secrets out of client bundles. Live on staging apps-examples #15 ↗ +
Base44 Estates (RR7), Base44 CRM (TanStack Start), Meridian (Astro 7) — 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 #15399 (4/7) ↗ +
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). The validator change now merges via stack PR 4/7.
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.
Astro 7 + hybrid SSG live on Meridian astro 7.0.6 + adapter 14.1.1 deployed; /about + /brewing prerendered, SSR catalog cached. Live on staging proof: prerendered /about ↗ +
Meridian upgraded astro 6.4.8 → 7.0.6 with @astrojs/cloudflare 14.1.1 and redeployed live: /about and /brewing are prerendered and served as static assets (real 404 status included) while the catalog stays SSR + edge-cached — each page wears its mode. One breaking change handled (compressHTML pinned to the v6 behaviour). Full verdict + the platform gaps it surfaced live in the Research tab.
Dispatcher WebSocket pass-through Fixed — socket.io handshake verified live through wss://crm.b44apps.dev. Live on staging apper #15766 (3b/7) ↗ +
The dispatcher's Set-Cookie rewrap turned 101 upgrades into 500s, forcing realtime onto a cross-origin WS to the platform origin. The dispatcher fix (originally apper commit 09abfcf6 on #14839) is deployed on staging, was verified live through wss://crm.b44apps.dev, and now merges via stack PR 3b/7. Production hardening (keepalive invariant, transport forcing, rate limits, soak/billing, Durable Objects) is tracked in Up next.
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.