The big picture
There are two apps and a set of shared packages:apps/webis a client-rendered React 19 single-page app built with Vite, TanStack Router, TanStack Query, Tailwind CSS, and Lingui for translations.apps/serveris a Hono application on Node.js. It serves the API, authentication, the MCP server, uploads, OpenAPI, and the built web app.packages/*hold everything the apps share: API business logic, authentication, database access, schemas, PDF and DOCX rendering, and UI primitives.
/api/rpc. Better Auth handles sign-in, sessions, passkeys, two-factor authentication, API keys, and the OAuth provider used by MCP clients. Drizzle talks to PostgreSQL.
How it runs
- Development.
pnpm devstarts Vite onPORT(default3000), the Hono server onSERVER_PORT(default3001), and the email template preview on port3002. Vite proxies/api,/mcp,/uploads,/.well-known, and/schema.jsonto Hono, so you always openhttp://localhost:3000. - Docker. The production image runs one Node.js process on port
3000. Hono mounts the API, auth, MCP, and static routes, then serves the built web app. - Vercel. One project deploys two services:
frontendserves the static web build from the CDN, andbackendruns the same Hono app in a Node.js Function. See Deployment checks.
apps/server/src/static/web.ts serves HTML shells with OpenGraph, canonical, and JSON-LD metadata injected.
What happens at startup
The server checks the environment, applies database migrations, and verifies the migrated schema before it initializes auth and accepts traffic. WithSTRICT_SCHEMA_CHECK=true, schema drift stops the server; otherwise it logs the drift and continues.
Workspace map
Internal packages are consumed as source through the
exports map in each package.json, which points at src files. Don’t expect a dist folder unless a package builds one explicitly.
Shared dependency versions live in the default pnpm catalog in pnpm-workspace.yaml; workspace manifests use catalog: for those ranges. Apps and packages inherit strict TypeScript settings from packages/config/tsconfig.base.json.
Turbo hashes shared Vitest configuration and setup files, tracks transitive source dependencies, and restores coverage and test reports from its artifact cache. Runtime variables remain available in strict environment mode through globalPassThroughEnv; task-level env declarations hash values that affect task results. CI checks package boundaries and typechecks affected packages, runs the full unit suite, and persists .turbo/cache between runs.
Where new code goes
A new template touches several places:
packages/schema/src/templates.ts, packages/pdf/src/templates/index.ts, the template source under packages/pdf/src/templates/<name>/, and preview images under apps/web/public/templates/{jpg,pdf}.
Add a helper to packages/utils only when no domain package is a better owner. JSON Patch behavior belongs in @reactive-resume/resume/patch, and DOCX builders belong in @reactive-resume/docx.
Boundary rules
Turborepo enforces these rules withpnpm exec turbo boundaries:
- Import other workspaces by package name and export subpath, such as
@reactive-resume/pdf/browser. Never reach into another workspace’ssrcthrough a relative path,@reactive-resume/*/src/*, or a TypeScript path alias. - Each workspace’s
turbo.jsondeclares tags.app:webandapp:servermark the apps.runtime:servermarks server-only packages (API, auth, database, environment, email, MCP),runtime:browsermarks browser-only UI, andruntime:universalmarks environment-neutral domain packages. - Runtime-specific code sits behind explicit subpaths such as
@reactive-resume/pdf/browser,@reactive-resume/pdf/server, and@reactive-resume/env/server. Keep root exports environment-neutral unless the whole package is server-only. - Wildcard exports are reserved for leaf libraries with a file-like surface:
@reactive-resume/ui/components/*,@reactive-resume/ui/hooks/*, and the schema model files. Everything else uses explicit exports.
pnpm exec turbo boundaries and check the affected consumers.
The web app
apps/web/src/routes stays route-owned: route files handle the URL, loaders, redirects, and metadata. Implementation lives in apps/web/src/features, grouped by product area: documents, resume (editor, preview, export, sharing, custom styles), letters, applications, assistant, ats-checker, settings, command-palette, auth, homepage, theme, locale, and user.
apps/web/src/router.tsx creates the router context with queryClient, orpc, theme, locale, session, and flags. Read these from route context instead of fetching them again. Never edit routeTree.gen.ts by hand; Vite regenerates it when you add or rename a route.
The oRPC client in apps/web/src/libs/orpc/client.ts calls /api/rpc with credentials. On Vercel, apps/web/src/libs/orpc/fetch.ts stages large request bodies through Blob storage.
When you add a public marketing route, also update its server fallback and SEO handling in apps/server/src/static/web.ts. Vite’s dev fallback can hide a production 404.
The API
packages/api/src/routers/index.ts combines the feature routers (resume, coverLetters, documents, applications, agent, ai, aiProviders, auth, storage, statistics, flags) into the contract served at /api/rpc. Each feature folder owns its procedures, services, helpers, and tests.
Use protectedProcedure from packages/api/src/context.ts for authenticated procedures, and check resource ownership inside the feature logic. API keys, bearer tokens, and cookies all resolve through the same shared auth path; don’t add a separate one.
Keep helpers inside the feature that uses them. Don’t reintroduce technical-layer folders such as services/ or helpers/ at the package root.
PDF rendering
packages/pdf renders every PDF. Templates are React components built from the package’s primitives. The code in src/forme renders them with a small React reconciler and converts the result into a Forme document. The Forme engine, compiled to WebAssembly, lays out and draws the pages. No Chromium, Browserless, or print service is involved.
@reactive-resume/pdf/browsercreates PDFs in the browser. The editor’s download and preview use it.@reactive-resume/pdf/servercreates PDFs on the server, for the public resume download and API exports.packages/pdf/src/templates/shared/filtering.tsholds the section filtering shared by all templates. Template-specific visual exceptions stay in that template’s folder.packages/pdf/src/hooks/use-register-fonts.tsresolves font families, weights, and fallback stacks for other scripts.
packages/pdf/src/section-title-catalog.json, built from the web app’s translations. See Contributing translations.
MCP
packages/mcp implements the MCP server with canonical, unprefixed tool names such as list_resumes, read_resume, apply_resume_patch, list_cover_letters, and list_applications. The server process imports it from @reactive-resume/mcp and injects an in-process oRPC router client, so MCP tools run the same business logic as the web app. MCP must never import code from apps/web. For the user-facing side, see Using the MCP server.
Related pages
- Development setup: run the app locally and learn the everyday commands.
- Deployment checks: how CI verifies the Vercel build and how to smoke-test an installation.
- Contributing translations: Crowdin, the glossary, and catalog commands.