Manifest
The .generatesaas/manifest.json record of every option your project was built with, which update reads to re-shape each new boilerplate version into your exact configuration.
.generatesaas/manifest.json is the record of how your project was scaffolded. It stores the full set of options you chose at init (minus secrets), so update can re-shape each new boilerplate version into your configuration before diffing.
You normally never edit this file by hand - init writes it and update keeps it current. Editing an option here changes how the next update shapes the template, which is exactly how a feature gets adopted.
How update keeps it current
The boilerplate gains new options over time; a project that predates one simply lacks that field. On every real update, before staging:
The new-options review always runs, even though the update is otherwise unattended - surfacing a new feature is never a decision the update should assume.
| Kind | Behavior |
|---|---|
| Init-locked (architecture, providers, identity) | Recorded but never re-offered. Switching one after the fact is a migration, not an update. |
| Adoptable | Features added since you built. They default to off, so behavior never changes until you choose. |
Field reference
Type is the JSON value. Default is what an absent field is filled with. Adoptable marks options the update flow offers when newly available.
Infrastructure
| Field | Type | Default | Adoptable | Notes |
|---|---|---|---|---|
architecture | "fullstack" | "separate" | "fullstack" | No | Frontend-hosted API or standalone Hono backend. |
deploymentTarget | "node" | "vercel" | "node" | No | Long-running runtime or Vercel serverless. |
databaseProvider | "postgres" | "neon" | "supabase" | "postgres" | No | Database driver and connection style. On deploymentTarget: "vercel" only the managed providers (neon, supabase) are valid - serverless cannot pool to a self-hosted postgres, and update refuses the pair if you write it by hand. |
cacheProvider | "redis" | "upstash" | "redis" | No | Cache/session backend. On deploymentTarget: "vercel" only upstash is valid - serverless cannot hold persistent Redis connections, and update refuses the pair if you write it by hand. |
Features
| Field | Type | Default | Adoptable | Notes |
|---|---|---|---|---|
paymentProvider | "stripe" | "polar" | "none" | "none" | No | Billing provider. |
defaultCurrency | currency code | "USD" | No | Billing and pricing currency. |
emailProvider | "smtp" | "ses" | "resend" | "smtp" | No | Transactional email provider. |
observability | ("sentry" | "posthog")[] | [] | Yes | Which services receive your app's errors, and on PostHog its LLM analytics. Pick any combination. Only a selected provider's adapter is generated under packages/observability and only its SDK installed, so an unpicked one costs no bytes and no dependency; [] ships no reporting SDK at all. The mobile app follows the same choice: it receives only the picked provider's React Native SDK, its startup half of apps/mobile/lib/observability.ts and its EXPO_PUBLIC_* key, and the Sentry Expo config plugin only when Sentry is picked. Change it by editing this array and running update - pasting a credential into config.observability never works for an adapter that was never generated. The Electron half follows desktop; PostHog has no Electron SDK, so a PostHog-only desktop build reports nothing and says so at startup. Projects carrying the older errorTracking string are migrated automatically. |
multiTenancy | boolean | false | Yes | Organizations - teams, members, shared resources. |
projects | boolean | false | Yes | Projects - a switcher that scopes a user's work inside their account, or inside the active organization when multiTenancy is on. Independently settable from multiTenancy but not independent in effect: enabling multiTenancy alone already scopes the desktop app's local data by an invisible per-organization project, so this option adds the UI rather than the scoping. In a single-tenant app, enabling projects moves desktop automations to the reachable-allowlist mode, where an explicit sign-out (or an expired session) stops all unattended automations on that device. See Projects. |
docs | boolean | false | Yes | The self-hosted Fumadocs docs app (apps/docs). |
desktop | boolean | false | Yes | The cross-platform desktop app (apps/desktop, built with Electron). |
mobile | boolean | false | Yes | The cross-platform mobile app (apps/mobile, built with Expo). Ships the iOS and Android app, the .well-known universal-link routes on your site, and the Expo push channel. Offered on the Pro plan and up. |
mobilePurchases | "revenuecat" | "none" | "none" | Yes | In-app purchases in the mobile app, through RevenueCat. Store purchases are additive to your web payment provider, never a replacement - the same account, billed by Apple or Google instead of by you. Only meaningful with mobile: true and a real paymentProvider; on any other shape the field is absent, and update refuses a hand-written "revenuecat" there. "none" ships the app on web billing, where its plan card says the subscription is managed on your website. |
ai | boolean | false | Yes | The app's AI features (config.ai.enabled): chat, models, automations, integrations. Master gate for the AI sub-options - when off they are forced off and the AI nav self-hides. A desktop project always ships the app's local AI stack; this option sets config.desktop.agents.enabled, the runtime gate on those screens. The AI packages themselves ship either way, because packages/translate depends on @repo/ai for the i18n pipeline every project uses. |
webAi | boolean | true | Yes | Whether the web app shows the AI surfaces - chat, automations, model and integration settings, and their nav entries. false is the desktop-first shape: your desktop or mobile app is the AI product and the website keeps account, login, billing and licensing only. The AI service is untouched either way - config.ai.enabled, the /api/ai/* routes and the desktop agents all stay exactly as ai set them, so the desktop app keeps talking to the same backend. Only meaningful with ai: true and an app to host the AI in - desktop: true or mobile: true; update refuses that combination if you write one by hand. |
credits | boolean | false | Yes | Metered usage credits on top of subscription plans. Requires a payment provider. |
revenueSharing | boolean | false | Yes | Opt-in MRR leaderboard with dofollow backlinks. |
System fields
Not options. Written by init and maintained by the CLI - do not edit them.
| Field | Notes |
|---|---|
version | The boilerplate version the project is on. update bumps it when an apply completes. |
initialVersion | The version the project was first scaffolded from. |
appName | Display name of the app. |
projectName | Slug used for the project directory and package names. |
frontend | Which frontend generated the project. Always "nextjs"; a project recording "nuxt" predates the single-frontend move and update refuses it (see update). |
baseUrl | Public base URL baked into config and .env, when set. |
licenseToken | Signed license token; refreshed by update for the target version. |
licenseKeyHash | Hash of the license key, for attribution. |
installId | Per-install identifier used by the heartbeat. |
A hand-edited value outside an option's allowed set is caught before staging: update names the field, the value it holds, and what it accepts. Retired keys are left alone - see update for how each one is reconciled.
Agent context
What a generated project ships for AI coding agents - AGENTS.md, the docs silo, and the bundled update and translate skills installed into every tool root - and how update refreshes and eject removes them.
Licensing & Heartbeat
How the license manifest and the daily heartbeat cron validate your installation, exactly what the request sends, and how to opt out permanently with eject.