Skip to content

infra: split www/marketing out of the editor app (single-domain multi-zone) #958

Description

@softmarshmallow

Anchor issue — not urgent. Filed to capture intent + a grounded inventory so we can pick it up later without re-deriving it. General code organization / infra cleanup. The what/why/inventory below is the durable part; the how (candidate solutions) is expected to be stale by pickup time.

What

Split the www / marketing surface out of the editor Next.js app into its own independently-deployed project, while keeping everything on the single apex domain grida.co (no app.grida.co subdomain).

The editor app carries far more than its name implies — landing, the entire marketing site, the editor runtime, the workspace/dashboard, APIs, and multi-tenant site rendering all live in one build.

Current state (grounded — editor/app/)

Route groups today:

(api) (auth) (canvas) (demo) (dev) (embed) (ingest) (insiders)
(library) (preview) (site) (tenant) (theme) (tools) (untracked)
(workbench) (workspace) (www)   + desktop/

The (www) group alone is a large public surface — each of these is a marketing/product page cluster:

(ai) (brand) (contact) (database) (dotcanvas) (downloads) (figma)
(forms) (home) (pricing) (printing) (sdk) (slides) (svg) (west)  + kr (localized)

Meanwhile the product surfaces at root:

  • (workspace)/[org]/[proj]/... — the GitHub-style editor/workspace namespace
  • (site)/organizations/[organization_name]/settings/billing/... — authed org pages
  • (workbench) — the editor runtime
  • host-based multi-tenant rendering — {tenant}.grida.site / .grida.app, routed in editor/lib/tenant/middleware.ts via the app's proxy layer (isReservedAppHostname, platform-site canonicalization)
  • global SEO — editor/app/sitemap.ts at the app root

Why

  • CI / build performance — a copy edit on (www)/(pricing) currently rebuilds the entire editor (workbench, canvas, APIs). Split → two small, independent builds; marketing iterates without touching the editor pipeline. This is the primary motivation.
  • Organization — a real ownership boundary between "the website" and "the product." editor stops being a catch-all; (www)'s ~15 page clusters stop diluting the editor's build graph.
  • Independent deploy / rollback — marketing and editor ship on their own cadence; a marketing deploy can't break the editor and vice-versa.

The constraint that makes this non-trivial

Both surfaces want root-level paths with no shared prefix: grida.co/ + grida.co/pricing + grida.co/svg (www) vs grida.co/{org}/{proj} (editor). This is not a clean /docs-style prefix split.

The resolution is forced:

  • editor stays the default / fallback app{org}/{proj} is an unbounded catch-all, so it must be "everything not claimed."
  • www owns a finite, enumerated set of root paths (/, /pricing, /svg, /forms, /database, …).

⇒ the www path list is a reserved-slug list in the editor's [org] namespace (the GitHub reserved-username problem). Note we already carry this constraint inside the single app — grida.co/pricing and grida.co/{org} already can't both be free. The split doesn't add the constraint; it promotes the reserved list into an explicit routing table. Whichever solution we pick, that table becomes the source of truth and must stay in sync with the [org] slug validator.

Split boundary — starting classification (decide per-group at pickup)

Group Lean Note
(www) (+ kr) → www the core of the split; the whole marketing surface
(library) → www? public, has its own sitemap.ts; could be www or its own zone
(brand) / (downloads) / (contact) → www already under (www)
(site) stay editor authed org pages (settings/billing)
(workspace) (workbench) (canvas) stay editor product runtime
(api) (auth) (tenant) stay editor APIs, auth flow, host-based tenant rendering
(tools) (preview) (embed) decide public-ish but coupled to the canvas runtime
(insiders) (dev) (demo) (ingest) (theme) desktop/ stay editor internal / runtime-coupled

Open questions / decisions to resolve at pickup

  • SEO ownership — where does app/sitemap.ts / robots live after the split, and how do the two apps compose a single sitemap for grida.co? Canonical URLs must stay stable (no marketing-page URL changes).
  • Reserved-slug source of truth — one list feeding both the router and the [org] slug validator. Where does it live so it can't drift?
  • Auth / cookies — session cookies scoped to .grida.co are shared across zones, but www is mostly logged-out; confirm no cookie/domain surprises and that cross-zone nav uses <a> (soft nav only works within a zone).
  • Shared code — design system / www/ components / @app/ui already shared via the monorepo; confirm both apps consume them cleanly (Turborepo cache boundaries).
  • Proxy interaction — the split routing (path-based) must compose with the existing host-based tenant proxy in lib/tenant/ without one shadowing the other.
  • OG image / preview generation — which app owns marketing OG images vs editor previews.
  • Analytics / middleware — ensure editor middleware doesn't intercept www paths and vice-versa.
  • Custom domains — confirm the user-imported custom-domain flow (Vercel) is unaffected (it's tenant-side / editor).

Candidate solutions (LIGHT — likely stale by pickup; re-evaluate)

All Vercel/Next-native, all keep the apex domain:

  • Vercel Microfrontends (microfrontends.json, edge-level routing, managed) — cleanest, editor = default app, www = child owning the enumerated paths. Paid: per routed request + per-project fee.
  • Next.js Multi-Zones (DIY: rewrites() + basePath + assetPrefix, editor as default zone) — no per-request fee; we hand-maintain the routing table + asset prefixes.
  • Thin vercel.json proxy project — smallest router variant.

Re-check when we pick this up — Vercel's offering here moves fast; there may be a better/cheaper option. Two non-negotiables regardless of tool: keep www a finite enumerated list (editor stays the fallback), and cross-zone links must be <a>, not <Link>.

Non-goals

  • No app.grida.co (or any product subdomain) — stay on the apex.
  • Multi-tenant {tenant}.grida.site / .grida.app routing is untouched; it stays with the editor (host-based, lib/tenant/).
  • Not a rewrite of the marketing pages — a lift-and-shift of (www) into its own deploy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    choreeeenterprise editionenhancementNew feature or requestinfraInfrastructure, deployment, CI/CD, and platform organization

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions