Today Platform Web — Dev Docs
Workspace

Apps

The five deployable applications in apps/ — what each one does, its dev port, its deploy target, and what to read next.

At a glance

PackagePathDev portDeploy
@today/webapps/web4060+Vercel (today.ai)
@today/adminapps/admin4061Vercel (admin.today.ai)
@today/onboardingapps/onboarding5174Standalone — native WebView shell, not part of apps/web
@today/webview-bridge-docsapps/webview-bridge-docs3000Vercel (default domain)
@today/dev-docsapps/dev-docs4070Vercel (default domain, protected)

Only apps/web gets per-worktree free-port scanning today — scripts/dev-server.mjs scans 4060-4069 for the first free port, so two web worktrees auto-take different ports. apps/admin is hardcoded to 4061 (next dev --turbopack -p 4061) and conflicts on parallel worktrees; same for apps/onboarding (Vite, --port 5174). Generalizing the scanner to admin is on the list as a follow-up.

@today/web — main product

apps/web. The user-facing product. Next.js 16 with the App Router, served at today.ai in production and todayai.dev in preview/dev.

Notable concerns covered elsewhere in this manual (in upcoming chapters):

  • Three dev modes (localhost-direct / local / remote) — Workflow → Local development
  • BFF auth proxy (apps/web/src/app/api/auth/[...all]/route.ts) — Architecture → Auth interaction
  • Cookie state on the app origin — Architecture → Auth cookie state
  • OpenAPI codegen via pnpm api:generate — Architecture → API codegen
  • MSW handlers + fixtures under apps/web/src/lib/msw/ — Architecture → MSW mocks
  • Chat runtime (already documented in apps/web/docs/chat/) — Architecture → Chat runtime (high-level, links to the local docs)
  • Playwright config split (smoke / full / visual) — Toolchain → Testing → Playwright

@today/admin — internal admin

apps/admin. Internal admin / staff app. Independent Vercel project, independent preview URL, independent deploy workflow (deploy-admin.yml).

Shares most infrastructure with apps/web: same auth client, same OIDC flow, same TDX components, same testing setup. Visual regression is not wired up for admin (see TODO.md).

@today/onboarding — native WebView onboarding shell

apps/onboarding. A standalone Vite app hosted inside the native client's WKWebView / WebView. It is not mounted under apps/web: the web app implements its own /onboarding route directly in Next using @today/opal primitives. This Vite shell exists separately for the native host case, where the onboarding flow runs inside a WebView with native messaging instead of Next SSR.

The contract is in apps/onboarding/NATIVE_CONTRACT.md; the dev workflow for testing inside a WebView is in apps/onboarding/WEBVIEW_DEV_GUIDE.md. The app handles file:// / local-files:// loading paths and routes its API calls through the native bridge rather than the BFF.

Both the web /onboarding route and this Vite shell share the same @today/opal flow primitives — that's the actual coupling, not "mounted under web."

@today/webview-bridge-docs — bridge contract site

apps/webview-bridge-docs. A separate Fumadocs site that documents the WebView Bridge wire contract plus the web SDK (@today/webview-bridge). Read by native client teams (iOS, macOS, Android, Windows) and by anyone writing web code that runs inside a WebView.

This site (@today/dev-docs) does not duplicate that content. See Docs sites for the boundary.

@today/dev-docs — this site

apps/dev-docs. You are reading it. Engineering manual for the today-platform-web monorepo itself. Read by new contributors and by AI agents working in the repo.

Adding a new app

The full checklist will live in workspace/adding-package in PR-CC. The short version:

  1. Create apps/<name>/ with package.json (@today/<name>), tsconfig.json, tsconfig.app.json, tsconfig.node.json, moon.yml.
  2. Run pnpm install to register the workspace.
  3. Add the project to tsconfig.json's references at the repo root if it participates in tsgo --build.
  4. If it deploys to Vercel: create a Vercel project, add VERCEL_PROJECT_ID_<NAME> to GitHub repo variables, add .github/workflows/deploy-<name>.yml.

scripts/affected.ts discovers packages dynamically from pnpm-workspace.yaml, so you do not need to register the new app there.

On this page