Marketing, Console, Docs — why ByteSpike ships three apps from one repo
ByteSpike's website is three separate applications behind three subdomains: bytespike.ai is marketing, console.bytespike.ai is the customer portal, docs.bytespike.ai is Mintlify. They share design tokens, locale data, and deploy strategy — but their release cadence, attack surface, and bundle weight diverge enough that smashing them together would have cost us more than living with the seams.
When new contributors land in the ByteSpike monorepo the most common question is some version of "why three apps?" The short answer is that they grew apart on purpose. Marketing, Console, and Docs each serve a distinct audience, ship on a distinct cadence, and carry a distinct risk profile, and the cost of forcing them into one application would have shown up immediately — in bundle weight, in deploy speed, and most painfully, in incident blast radius.
What each app actually is
- apps/marketing — Next.js 15 App Router, statically prerendered, bilingual via next-intl. The pages you see at bytespike.ai: pricing, /dosia, /enterprise, /blog, /docs hub. Goal: convert visitors. Almost no client interactivity beyond a small auth-status fetch in the nav.
- apps/console — Next.js 15 with server actions and authenticated routes, mounted at console.bytespike.ai. Wallet, API keys, usage, audit log, member management, billing. Goal: serve signed-in users. Hits the gateway on every page; can't be SSG'd.
- apps/docs — Mintlify, completely separate stack. mint.json + MDX, hosted under docs.bytespike.ai. Goal: API reference + concept docs for integrators. Anything in the apps/docs tree renders directly without a Next.js build.
Why not one app
Three forces kept them separate. First — release cadence. Marketing copy ships several times a day; the Console's billing logic does not, and shouldn't have to. If a marketing typo blocked a billing fix, the bargaining cost would dwarf any DRY win. Second — attack surface. The Console handles JWT cookies and wallet writes; the marketing site doesn't, and shouldn't. Mounting them on the same Next.js process means any marketing-side dependency vulnerability lands inside the wallet-handling code path. Third — bundle weight. The Console's data tables, charts, and admin widgets are real. Forcing /dosia visitors to download them would tank LCP for a page that the customer hasn't earned yet.
What they share, what they don't
Shared through workspace packages:
- @bytespike/ui — brand tokens (CSS custom props), the Logo / Wordmark / Sparkle SVGs, the bs-card / bs-marquee / bs-gradient-text utilities. One source of truth for the visual language.
- @bytespike/locales — bilingual copy that crosses the seam (currency, dates, billing terms, API response labels).
Not shared — and intentionally so:
- Auth — Console uses HttpOnly bs_access_token cookies via the gateway; marketing relies on document.referrer + localStorage as a best-effort hint (see how-failures-dont-bill-actually-works's sibling post). Marketing never sees or stores credentials.
- Deploy targets — marketing + console are Next.js standalone apps behind nginx; docs is a Mintlify export hosted on Vercel. Three deploy pipelines, three deploy logs, three blast radii.
- i18n strategy — marketing routes are bilingual via next-intl URL prefix (/zh/...); console is signed-in-only and shows whatever locale the account preference says; docs uses Mintlify's per-page locale frontmatter.
“Three apps means three deploy logs, three bundle reports, three sets of regression risks — but only one of them can blow up at a time.”
What this means for contributors
Most PRs touch one app. Tokens live in @bytespike/ui — change a brand color in one place, all three apps pick it up next build. Copy that needs to read identically across pricing pages and console billing tooltips lives in @bytespike/locales. Anything else — components, routes, layout — lives in the app it ships from. The boring rule is the one we've held up best: if a piece of code is used by exactly one app, it stays in that app.
If you came in expecting a single monolithic Next.js app, the seam may feel like overhead. After six months of running this layout, the seam is the cheap part. The expensive part is the day a vendor SDK we depend on for one app has a CVE — and that day, the seam is what makes the patch a one-app deploy.