@terreno/mcp 0.31.0 → 56.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/dist/bootstrap.d.ts.map +1 -1
  2. package/dist/bootstrap.js +15 -16
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/docs/templates/bootstrap/backend/types/models/userTypes.template.md +4 -5
  5. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
  6. package/dist/docs/templates/tools/model.template.md +2 -4
  7. package/dist/docs/versioned/next/README.md +1 -0
  8. package/dist/docs/versioned/next/explanation/README.md +1 -7
  9. package/dist/docs/versioned/next/how-to/README.md +2 -8
  10. package/dist/docs/versioned/next/how-to/deploy-to-gcp.md +228 -20
  11. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
  12. package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +4 -57
  13. package/dist/docs/versioned/next/implementationPlans/README.md +30 -56
  14. package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +1 -2
  15. package/dist/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +0 -1
  16. package/dist/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +0 -1
  17. package/dist/docs/versioned/next/implementationPlans/deploy-to-gcp.md +0 -1
  18. package/dist/docs/versioned/next/implementationPlans/deploy-to-vercel.md +0 -1
  19. package/dist/docs/versioned/next/implementationPlans/deployment-foundation.md +0 -1
  20. package/dist/docs/versioned/next/implementationPlans/docs-reference-coverage.md +0 -1
  21. package/dist/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +0 -1
  22. package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +0 -1
  23. package/dist/docs/versioned/next/implementationPlans/oss-governance-baseline.md +0 -1
  24. package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +0 -1
  25. package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +0 -1
  26. package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
  27. package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
  28. package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
  29. package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
  30. package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
  31. package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
  32. package/dist/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +0 -1
  33. package/dist/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +0 -1
  34. package/dist/docs/versioned/next/reference/README.md +1 -4
  35. package/dist/docs/versioned/next/reference/environment-variables.md +382 -220
  36. package/dist/docs/versioned/next/reference/syncdb.md +456 -0
  37. package/dist/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
  38. package/dist/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
  39. package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
  40. package/dist/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
  41. package/dist/docs/versioned/next/tasks/deployment-foundation.md +12 -12
  42. package/dist/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
  43. package/dist/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
  44. package/dist/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
  45. package/dist/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
  46. package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
  47. package/dist/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
  48. package/dist/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
  49. package/package.json +4 -5
  50. package/src/docs/templates/bootstrap/backend/types/models/userTypes.template.md +4 -5
  51. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
  52. package/src/docs/templates/tools/model.template.md +2 -4
  53. package/src/docs/versioned/next/README.md +1 -0
  54. package/src/docs/versioned/next/explanation/README.md +1 -7
  55. package/src/docs/versioned/next/how-to/README.md +2 -8
  56. package/src/docs/versioned/next/how-to/deploy-to-gcp.md +228 -20
  57. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
  58. package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +4 -57
  59. package/src/docs/versioned/next/implementationPlans/README.md +30 -56
  60. package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +1 -2
  61. package/src/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +0 -1
  62. package/src/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +0 -1
  63. package/src/docs/versioned/next/implementationPlans/deploy-to-gcp.md +0 -1
  64. package/src/docs/versioned/next/implementationPlans/deploy-to-vercel.md +0 -1
  65. package/src/docs/versioned/next/implementationPlans/deployment-foundation.md +0 -1
  66. package/src/docs/versioned/next/implementationPlans/docs-reference-coverage.md +0 -1
  67. package/src/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +0 -1
  68. package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +0 -1
  69. package/src/docs/versioned/next/implementationPlans/oss-governance-baseline.md +0 -1
  70. package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +0 -1
  71. package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +0 -1
  72. package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
  73. package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
  74. package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
  75. package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
  76. package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
  77. package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
  78. package/src/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +0 -1
  79. package/src/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +0 -1
  80. package/src/docs/versioned/next/reference/README.md +1 -4
  81. package/src/docs/versioned/next/reference/environment-variables.md +382 -220
  82. package/src/docs/versioned/next/reference/syncdb.md +456 -0
  83. package/src/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
  84. package/src/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
  85. package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
  86. package/src/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
  87. package/src/docs/versioned/next/tasks/deployment-foundation.md +12 -12
  88. package/src/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
  89. package/src/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
  90. package/src/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
  91. package/src/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
  92. package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
  93. package/src/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
  94. package/src/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
  95. package/LICENSE +0 -21
  96. package/dist/__tests__/bootstrap.test.d.ts +0 -2
  97. package/dist/__tests__/bootstrap.test.d.ts.map +0 -1
  98. package/dist/__tests__/bootstrap.test.js +0 -299
  99. package/dist/__tests__/bootstrap.test.js.map +0 -1
  100. package/dist/__tests__/databaseQuery.test.d.ts +0 -2
  101. package/dist/__tests__/databaseQuery.test.d.ts.map +0 -1
  102. package/dist/__tests__/databaseQuery.test.js +0 -83
  103. package/dist/__tests__/databaseQuery.test.js.map +0 -1
  104. package/dist/__tests__/docsRoot.test.d.ts +0 -2
  105. package/dist/__tests__/docsRoot.test.d.ts.map +0 -1
  106. package/dist/__tests__/docsRoot.test.js +0 -59
  107. package/dist/__tests__/docsRoot.test.js.map +0 -1
  108. package/dist/__tests__/inferPackages.test.d.ts +0 -2
  109. package/dist/__tests__/inferPackages.test.d.ts.map +0 -1
  110. package/dist/__tests__/inferPackages.test.js +0 -35
  111. package/dist/__tests__/inferPackages.test.js.map +0 -1
  112. package/dist/__tests__/preload.d.ts +0 -2
  113. package/dist/__tests__/preload.d.ts.map +0 -1
  114. package/dist/__tests__/preload.js +0 -12
  115. package/dist/__tests__/preload.js.map +0 -1
  116. package/dist/__tests__/prompts.test.d.ts +0 -2
  117. package/dist/__tests__/prompts.test.d.ts.map +0 -1
  118. package/dist/__tests__/prompts.test.js +0 -295
  119. package/dist/__tests__/prompts.test.js.map +0 -1
  120. package/dist/__tests__/resources.test.d.ts +0 -2
  121. package/dist/__tests__/resources.test.d.ts.map +0 -1
  122. package/dist/__tests__/resources.test.js +0 -352
  123. package/dist/__tests__/resources.test.js.map +0 -1
  124. package/dist/__tests__/search.test.d.ts +0 -2
  125. package/dist/__tests__/search.test.d.ts.map +0 -1
  126. package/dist/__tests__/search.test.js +0 -163
  127. package/dist/__tests__/search.test.js.map +0 -1
  128. package/dist/__tests__/tools.test.d.ts +0 -2
  129. package/dist/__tests__/tools.test.d.ts.map +0 -1
  130. package/dist/__tests__/tools.test.js +0 -540
  131. package/dist/__tests__/tools.test.js.map +0 -1
  132. package/dist/__tests__/upgradeGuide.test.d.ts +0 -2
  133. package/dist/__tests__/upgradeGuide.test.d.ts.map +0 -1
  134. package/dist/__tests__/upgradeGuide.test.js +0 -22
  135. package/dist/__tests__/upgradeGuide.test.js.map +0 -1
  136. package/dist/docs/upgrades/0.31.0.md +0 -60
  137. package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +0 -65
  138. package/dist/docs/versioned/next/explanation/deployment-baseline.md +0 -78
  139. package/dist/docs/versioned/next/explanation/positioning.md +0 -164
  140. package/dist/docs/versioned/next/explanation/repository-settings.md +0 -44
  141. package/dist/docs/versioned/next/explanation/roadmap-process.md +0 -330
  142. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +0 -866
  143. package/dist/docs/versioned/next/how-to/build-for-web.md +0 -69
  144. package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +0 -115
  145. package/dist/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +0 -183
  146. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +0 -114
  147. package/dist/docs/versioned/next/implementationPlans/billing-stripe.md +0 -158
  148. package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +0 -240
  149. package/dist/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +0 -107
  150. package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +0 -107
  151. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +0 -102
  152. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +0 -93
  153. package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +0 -188
  154. package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +0 -269
  155. package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +0 -105
  156. package/dist/docs/versioned/next/implementationPlans/orgs-and-teams.md +0 -158
  157. package/dist/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +0 -147
  158. package/dist/docs/versioned/next/reference/admin-spa.md +0 -130
  159. package/dist/docs/versioned/next/reference/ai.md +0 -519
  160. package/dist/docs/versioned/next/reference/test.md +0 -108
  161. package/dist/docs/versioned/next/roadmap.md +0 -9
  162. package/dist/docs/versioned/next/tasks/README.md +0 -42
  163. package/dist/docs/versioned/next/tasks/billing-stripe.md +0 -75
  164. package/dist/docs/versioned/next/tasks/comms-abstraction.md +0 -77
  165. package/dist/docs/versioned/next/tasks/comms-adapter-expo-push.md +0 -29
  166. package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +0 -29
  167. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +0 -29
  168. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +0 -19
  169. package/dist/docs/versioned/next/tasks/infra-mcp.md +0 -43
  170. package/dist/docs/versioned/next/tasks/native-module-baseline.md +0 -42
  171. package/dist/docs/versioned/next/tasks/orgs-and-teams.md +0 -70
  172. package/dist/docs/versioned/next/tasks/password-reset-and-email-verification.md +0 -60
  173. package/src/docs/upgrades/0.31.0.md +0 -60
  174. package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +0 -65
  175. package/src/docs/versioned/next/explanation/deployment-baseline.md +0 -78
  176. package/src/docs/versioned/next/explanation/positioning.md +0 -164
  177. package/src/docs/versioned/next/explanation/repository-settings.md +0 -44
  178. package/src/docs/versioned/next/explanation/roadmap-process.md +0 -330
  179. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +0 -866
  180. package/src/docs/versioned/next/how-to/build-for-web.md +0 -69
  181. package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +0 -115
  182. package/src/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +0 -183
  183. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +0 -114
  184. package/src/docs/versioned/next/implementationPlans/billing-stripe.md +0 -158
  185. package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +0 -240
  186. package/src/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +0 -107
  187. package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +0 -107
  188. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +0 -102
  189. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +0 -93
  190. package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +0 -188
  191. package/src/docs/versioned/next/implementationPlans/infra-mcp.md +0 -269
  192. package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +0 -105
  193. package/src/docs/versioned/next/implementationPlans/orgs-and-teams.md +0 -158
  194. package/src/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +0 -147
  195. package/src/docs/versioned/next/reference/admin-spa.md +0 -130
  196. package/src/docs/versioned/next/reference/ai.md +0 -519
  197. package/src/docs/versioned/next/reference/test.md +0 -108
  198. package/src/docs/versioned/next/roadmap.md +0 -9
  199. package/src/docs/versioned/next/tasks/README.md +0 -42
  200. package/src/docs/versioned/next/tasks/billing-stripe.md +0 -75
  201. package/src/docs/versioned/next/tasks/comms-abstraction.md +0 -77
  202. package/src/docs/versioned/next/tasks/comms-adapter-expo-push.md +0 -29
  203. package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +0 -29
  204. package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +0 -29
  205. package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +0 -19
  206. package/src/docs/versioned/next/tasks/infra-mcp.md +0 -43
  207. package/src/docs/versioned/next/tasks/native-module-baseline.md +0 -42
  208. package/src/docs/versioned/next/tasks/orgs-and-teams.md +0 -70
  209. package/src/docs/versioned/next/tasks/password-reset-and-email-verification.md +0 -60
@@ -1,69 +0,0 @@
1
- # Build for web
2
-
3
- Export your Terreno Expo app as static files for hosting on GCS, Netlify, or any CDN.
4
-
5
- See the [deployment baseline](../explanation/deployment-baseline.md) for requirements that apply to every host.
6
-
7
- ## Prerequisites
8
-
9
- - Backend running and reachable from browsers (CORS configured).
10
- - MongoDB replica set for realtime features (see baseline).
11
-
12
- ## 1. Set the API URL at build time
13
-
14
- `EXPO_PUBLIC_API_URL` is **inlined into the JavaScript bundle** during export. Setting it on the hosting platform after deploy does nothing.
15
-
16
- ```bash
17
- cd example-frontend
18
- EXPO_PUBLIC_API_URL=https://api.example.com bun run export
19
- ```
20
-
21
- The export script in `example-frontend/package.json` is:
22
-
23
- ```bash
24
- bun expo export --platform web
25
- ```
26
-
27
- ### Alternative: `app.json` `extra.BASE_URL`
28
-
29
- `@terreno/rtk` resolves the API base from `expo-constants` `extra.BASE_URL` and `EXPO_PUBLIC_API_URL` (see `rtk/src/constants.ts` `resolveBaseUrls`). Priority in **production** builds:
30
-
31
- 1. `EXPO_PUBLIC_API_URL` environment variable (set before export)
32
- 2. `extra.BASE_URL` in app config (when env var is unset)
33
- 3. Development fallbacks (hostUri, experience URL, localhost) — only in `__DEV__`
34
-
35
- For production, prefer `EXPO_PUBLIC_API_URL` so CI and local builds behave the same.
36
-
37
- ## 2. Run the export
38
-
39
- ```bash
40
- EXPO_PUBLIC_API_URL=https://api.example.com bun run export
41
- ```
42
-
43
- Output lands in `dist/` (Expo default for web export). Contents depend on the configured web `output` mode — today Terreno apps use the `single` SPA mode (one `index.html`).
44
-
45
- ## 3. Verify locally before uploading
46
-
47
- Serve the `dist/` folder with any static server and confirm API calls hit your backend:
48
-
49
- ```bash
50
- npx serve dist -p 4173
51
- ```
52
-
53
- Open `http://localhost:4173`, log in, and check the network tab — requests must go to your `EXPO_PUBLIC_API_URL`, not `localhost`.
54
-
55
- ## 4. Deploy the static files
56
-
57
- Upload `dist/` to your host. **Client-side routing** requires the host to serve `index.html` for unknown paths (SPA fallback):
58
-
59
- | Host | Configuration |
60
- |------|---------------|
61
- | GCS + CDN | `notFoundPage=index.html` on the bucket — see [Deploy web to GCS + CDN](deploy-web-to-gcs-cdn.md) |
62
- | Netlify | `_redirects` or `netlify.toml` `/* /index.html 200` |
63
-
64
- ## Provider guides
65
-
66
- - [Deploy to GCP](deploy-to-gcp.md) — GCS + Cloud CDN (static web) + Cloud Run (backend)
67
- - Deployment baseline — [seven requirements](../explanation/deployment-baseline.md)
68
-
69
- For server-rendered web (future), see [Web SSR and admin SPA](https://github.com/FlourishHealth/terreno/blob/master/docs/implementationPlans/web-ssr-and-admin-spa.md).
@@ -1,115 +0,0 @@
1
- # Deploy backend to Cloud Run
2
-
3
- Deploy a Terreno `@terreno/api` backend (for example `example-backend`) to Google Cloud Run with Secret Manager.
4
-
5
- Replace placeholders: `$PROJECT_ID`, `$REGION`, `$SERVICE_NAME`, `$REPOSITORY`.
6
-
7
- ## Prerequisites
8
-
9
- - [gcloud CLI](https://cloud.google.com/sdk/docs/install) installed and authenticated
10
- - GCP project with billing enabled
11
- - MongoDB Atlas cluster (or other replica set) — [deployment baseline](../explanation/deployment-baseline.md)
12
- - Docker (local build) or Cloud Build
13
-
14
- Enable APIs:
15
-
16
- ```bash
17
- gcloud services enable run.googleapis.com artifactregistry.googleapis.com secretmanager.googleapis.com --project="$PROJECT_ID"
18
- ```
19
-
20
- ## 1. Create Artifact Registry
21
-
22
- ```bash
23
- gcloud artifacts repositories create "$REPOSITORY" \
24
- --repository-format=docker \
25
- --location="$REGION" \
26
- --project="$PROJECT_ID"
27
- ```
28
-
29
- ## 2. Build and push the image
30
-
31
- From the **repository root** (the Dockerfile expects the monorepo context):
32
-
33
- ```bash
34
- docker build -f example-backend/Dockerfile -t "$REGION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$SERVICE_NAME:latest" .
35
- docker push "$REGION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$SERVICE_NAME:latest"
36
- ```
37
-
38
- ## 3. Create secrets
39
-
40
- Create Secret Manager secrets (values added out of band):
41
-
42
- ```bash
43
- for name in mongodb-uri token-secret token-issuer refresh-token-secret session-secret; do
44
- gcloud secrets create "$SERVICE_NAME-$name" --project="$PROJECT_ID" --replication-policy=automatic
45
- done
46
- ```
47
-
48
- Add secret values (example for MongoDB URI):
49
-
50
- ```bash
51
- echo -n 'mongodb+srv://user:pass@cluster.example/terreno?retryWrites=true&w=majority' | \
52
- gcloud secrets versions add "$SERVICE_NAME-mongodb-uri" --data-file=- --project="$PROJECT_ID"
53
- ```
54
-
55
- Repeat for `TOKEN_SECRET`, `TOKEN_ISSUER`, `REFRESH_TOKEN_SECRET`, and `SESSION_SECRET`.
56
-
57
- For Better Auth, also create `better-auth-secret` and set `AUTH_PROVIDER=better-auth` plus `BETTER_AUTH_URL` at deploy time. See [Configure Better Auth](configure-better-auth.md).
58
-
59
- ## 4. Runtime service account
60
-
61
- ```bash
62
- gcloud iam service-accounts create "$SERVICE_NAME-runtime" --project="$PROJECT_ID"
63
-
64
- gcloud projects add-iam-policy-binding "$PROJECT_ID" \
65
- --member="serviceAccount:$SERVICE_NAME-runtime@$PROJECT_ID.iam.gserviceaccount.com" \
66
- --role="roles/secretmanager.secretAccessor"
67
- ```
68
-
69
- Grant object access on your uploads bucket separately if using GCS file storage.
70
-
71
- ## 5. Deploy to Cloud Run
72
-
73
- ```bash
74
- gcloud run deploy "$SERVICE_NAME" \
75
- --image="$REGION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$SERVICE_NAME:latest" \
76
- --region="$REGION" \
77
- --project="$PROJECT_ID" \
78
- --platform=managed \
79
- --port=3000 \
80
- --session-affinity \
81
- --timeout=3600 \
82
- --min-instances=1 \
83
- --allow-unauthenticated \
84
- --service-account="$SERVICE_NAME-runtime@$PROJECT_ID.iam.gserviceaccount.com" \
85
- --set-secrets="MONGO_URI=$SERVICE_NAME-mongodb-uri:latest,TOKEN_SECRET=$SERVICE_NAME-token-secret:latest,TOKEN_ISSUER=$SERVICE_NAME-token-issuer:latest,REFRESH_TOKEN_SECRET=$SERVICE_NAME-refresh-token-secret:latest,SESSION_SECRET=$SERVICE_NAME-session-secret:latest" \
86
- --set-env-vars="NODE_ENV=production"
87
- ```
88
-
89
- Adjust `min-instances` to `0` for dev sandboxes (websockets will churn on cold start).
90
-
91
- ## 6. Verify
92
-
93
- ```bash
94
- SERVICE_URL=$(gcloud run services describe "$SERVICE_NAME" --region="$REGION" --project="$PROJECT_ID" --format='value(status.url)')
95
- curl -s "$SERVICE_URL/health" | jq .
96
- ```
97
-
98
- Expect `"healthy": true`.
99
-
100
- ## Production constraints
101
-
102
- | Symptom | Cause | Fix |
103
- |---------|-------|-----|
104
- | Websockets disconnect every few requests | No session affinity on Cloud Run | Add `--session-affinity` |
105
- | Socket connections drop after ~5 minutes | Default request timeout (300s) | Raise `--timeout` (for example `3600`) |
106
- | Live feature flags / realtime never update; log mentions replica set | MongoDB is standalone, not a replica set | Use Atlas or `replSet` — change streams required (`feature-flags/src/featureFlagsApp.ts`) |
107
- | First request after idle is slow; sockets reconnect constantly | Scaled to zero | Set `--min-instances=1` for user-facing services |
108
- | Browser API calls blocked by CORS | `corsOrigin` / Better Auth `trustedOrigins` missing web origin | Add your CDN URL to backend CORS and auth config |
109
-
110
- ## Related
111
-
112
- - [Deployment baseline](../explanation/deployment-baseline.md)
113
- - [Environment variables](../reference/environment-variables.md)
114
- - [Deploy web to GCS + CDN](deploy-web-to-gcs-cdn.md)
115
- - [GCP architecture](../explanation/deployment-architecture-gcp.md)
@@ -1,183 +0,0 @@
1
- # Deploy web to GCS + CDN
2
-
3
- Host a static Terreno web export on Google Cloud Storage with Cloud CDN.
4
-
5
- Replace placeholders: `$PROJECT_ID`, `$REGION`, `$WEB_BUCKET`, `$SITE_NAME`, `$DOMAIN`.
6
-
7
- ## Prerequisites
8
-
9
- - [gcloud CLI](https://cloud.google.com/sdk/docs/install) installed and authenticated
10
- - Backend deployed and CORS configured — see [Deploy backend to Cloud Run](deploy-backend-to-cloud-run.md)
11
- - [Build for web](build-for-web.md) completed with the correct `EXPO_PUBLIC_API_URL`
12
-
13
- ## 1. Build the web bundle
14
-
15
- Set the API URL **before** export — it is inlined into the bundle:
16
-
17
- ```bash
18
- cd example-frontend
19
- EXPO_PUBLIC_API_URL=https://api.example.com bun run export
20
- ```
21
-
22
- The export script is `bun expo export --platform web` (see `example-frontend/package.json`).
23
-
24
- Output is in `dist/`.
25
-
26
- ## 2. Enable the Compute Engine API
27
-
28
- Cloud CDN and its global load-balancing resources require the Compute Engine API:
29
-
30
- ```bash
31
- gcloud services enable compute.googleapis.com --project="$PROJECT_ID"
32
- ```
33
-
34
- ## 3. Create the GCS bucket
35
-
36
- ```bash
37
- gsutil mb -p "$PROJECT_ID" -l "$REGION" "gs://$WEB_BUCKET/"
38
- ```
39
-
40
- ## 4. Allow public object reads
41
-
42
- Cloud CDN must be able to serve the bucket's objects to unauthenticated visitors:
43
-
44
- ```bash
45
- gsutil iam ch allUsers:objectViewer "gs://$WEB_BUCKET"
46
- ```
47
-
48
- Do not grant `allUsers` a write role such as `objectAdmin`.
49
-
50
- ## 5. Configure SPA routing
51
-
52
- Client-side routes require `index.html` for unknown paths:
53
-
54
- ```bash
55
- gsutil web set -e index.html "gs://$WEB_BUCKET"
56
- ```
57
-
58
- Do not set `MainPageSuffix` — GCS 301 redirects break client-side routing.
59
-
60
- ## 6. Upload the build
61
-
62
- ```bash
63
- gsutil -m -h "Cache-Control:public, max-age=31536000, immutable" \
64
- rsync -r -d -x '.*\.html$' \
65
- dist/ "gs://$WEB_BUCKET/"
66
-
67
- gsutil -h "Cache-Control:no-cache, no-store, must-revalidate" \
68
- cp dist/index.html "gs://$WEB_BUCKET/index.html"
69
- ```
70
-
71
- ## 7. Create HTTPS CDN resources
72
-
73
- The load balancer serves the bucket over HTTPS only. A Google-managed certificate secures
74
- the domain:
75
-
76
- ```bash
77
- # Backend bucket (CDN-enabled)
78
- gcloud compute backend-buckets create "${SITE_NAME}-backend" \
79
- --gcs-bucket-name="$WEB_BUCKET" \
80
- --enable-cdn \
81
- --no-negative-caching \
82
- --project="$PROJECT_ID"
83
-
84
- # URL map
85
- gcloud compute url-maps create "${SITE_NAME}-url-map" \
86
- --default-backend-bucket="${SITE_NAME}-backend" \
87
- --project="$PROJECT_ID"
88
-
89
- # Static IP
90
- gcloud compute addresses create "${SITE_NAME}-ip" --global --project="$PROJECT_ID"
91
-
92
- # Managed TLS certificate
93
- gcloud compute ssl-certificates create "${SITE_NAME}-cert" \
94
- --domains="$DOMAIN" \
95
- --global \
96
- --project="$PROJECT_ID"
97
-
98
- # HTTPS proxy
99
- gcloud compute target-https-proxies create "${SITE_NAME}-https-proxy" \
100
- --url-map="${SITE_NAME}-url-map" \
101
- --ssl-certificates="${SITE_NAME}-cert" \
102
- --project="$PROJECT_ID"
103
-
104
- # HTTPS forwarding rule
105
- gcloud compute forwarding-rules create "${SITE_NAME}-https-forwarding-rule" \
106
- --address="${SITE_NAME}-ip" \
107
- --target-https-proxy="${SITE_NAME}-https-proxy" \
108
- --global \
109
- --ports=443 \
110
- --project="$PROJECT_ID"
111
- ```
112
-
113
- ## 8. Redirect HTTP to HTTPS
114
-
115
- Port 80 should redirect rather than serve the bundle. Without a listener there at all, `http://$DOMAIN` fails to connect instead of sending visitors to the secure URL.
116
-
117
- `gcloud` exposes redirect behavior only through an imported spec, not through `url-maps create` flags:
118
-
119
- ```bash
120
- gcloud compute url-maps import "${SITE_NAME}-http-redirect-url-map" \
121
- --global --project="$PROJECT_ID" --source=/dev/stdin <<SPEC
122
- name: ${SITE_NAME}-http-redirect-url-map
123
- defaultUrlRedirect:
124
- httpsRedirect: true
125
- redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
126
- SPEC
127
-
128
- gcloud compute target-http-proxies create "${SITE_NAME}-http-proxy" \
129
- --url-map="${SITE_NAME}-http-redirect-url-map" \
130
- --project="$PROJECT_ID"
131
-
132
- gcloud compute forwarding-rules create "${SITE_NAME}-forwarding-rule" \
133
- --address="${SITE_NAME}-ip" \
134
- --target-http-proxy="${SITE_NAME}-http-proxy" \
135
- --global \
136
- --ports=80 \
137
- --project="$PROJECT_ID"
138
- ```
139
-
140
- Port 80 now only ever returns a 301; the bundle itself is served exclusively over TLS.
141
-
142
- Or run the parameterized script, which provisions steps 7 and 8 in one pass:
143
-
144
- ```bash
145
- ./scripts/setup-gcs-hosting.sh \
146
- --project "$PROJECT_ID" \
147
- --site-name "$SITE_NAME" \
148
- --bucket "$WEB_BUCKET" \
149
- --domain "$DOMAIN"
150
- ```
151
-
152
- ## 9. Point DNS and wait for the certificate
153
-
154
- ```bash
155
- gcloud compute addresses describe "${SITE_NAME}-ip" --global \
156
- --project="$PROJECT_ID" --format='value(address)'
157
- ```
158
-
159
- Create an A record for `$DOMAIN` pointing at that IP. Managed certificates only begin provisioning once DNS resolves to the load balancer, and can take up to about an hour to become active:
160
-
161
- ```bash
162
- gcloud compute ssl-certificates describe "${SITE_NAME}-cert" --global \
163
- --project="$PROJECT_ID" --format='value(managed.status)'
164
- ```
165
-
166
- Wait for `ACTIVE` before sending traffic. Still `PROVISIONING` after an hour usually means the A record does not resolve to the reserved IP.
167
-
168
- ## 10. Invalidate cache after deploy
169
-
170
- ```bash
171
- gcloud compute url-maps invalidate-cdn-cache "${SITE_NAME}-url-map" \
172
- --path "/*" --async --project="$PROJECT_ID"
173
- ```
174
-
175
- ## Static-only path
176
-
177
- This guide hosts the `expo export -p web` output as static files. Server-rendered web (SSR, API routes) requires Expo SDK ≥ 55 — see [Web SSR and admin SPA](https://github.com/FlourishHealth/terreno/blob/master/docs/implementationPlans/web-ssr-and-admin-spa.md).
178
-
179
- ## Related
180
-
181
- - [Build for web](build-for-web.md)
182
- - [Deploy backend to Cloud Run](deploy-backend-to-cloud-run.md)
183
- - [GCP architecture](../explanation/deployment-architecture-gcp.md)
@@ -1,114 +0,0 @@
1
- # Program: B2B platform
2
-
3
- **Status:** Draft — roadmap items drafted; decisions D1–D7 resolved 2026-08-09; first nine IPs open
4
- **Owner:** unassigned
5
- **Created:** 2026-08-09
6
-
7
- Umbrella plan coordinating the IPs that take Terreno from a single-tenant, per-user
8
- full-stack kit to a framework you can spin up a B2B SaaS app with in hours. Pairs with the
9
- roadmap items in
10
- [roadmap-seed-issues.md](../explanation/roadmap-seed-issues.md) (B2B platform program
11
- section).
12
-
13
- ## Goal
14
-
15
- A freshly scaffolded, deployed Terreno app supports the B2B critical path out of the box:
16
- **sign up → create an organization → invite a teammate → pick a plan → get an email.**
17
- Today zero of those five steps are possible without custom code.
18
-
19
- ## Background
20
-
21
- A capability audit (2026-08-08) found Terreno strong as a single-tenant CRUD + auth +
22
- realtime + admin + AI kit, with three structural gaps for B2B SaaS: **tenancy** (no
23
- orgs/teams/memberships), **money** (no billing), and **communication** (no email, SMS, or
24
- push delivery — `expo-server-sdk` is an unused dependency and the RTK client's
25
- `resetPassword` endpoint has no backend implementation). Supporting gaps: no invitations,
26
- no durable jobs, no inbound-webhook framework, no rate limiting, no framework-level audit
27
- log, no enterprise SSO/MFA, and missing B2B UI surfaces (server-filtered data grid, charts,
28
- org switcher, dark mode).
29
-
30
- ## Tracks
31
-
32
- | Track | Theme | IPs / roadmap items |
33
- |---|---|---|
34
- | A | Tenancy & access | `orgs-and-teams`, `rbac-permissions` (existing draft), `invitations-and-seats`, `org-management-ui` |
35
- | C | Communications (pluggable) | `comms-abstraction`, one item **per adapter**: `comms-adapter-expo-push` (first push adapter, per D4), `comms-adapter-twilio-sms`, `comms-adapter-twilio-verify`, `comms-adapter-sendgrid` (first mail adapter, per D2); consumers: `password-reset-and-email-verification`, `notification-center` |
36
- | B | Billing | `inbound-webhooks`, `billing-stripe` (web-first, per D1), `mobile-iap-revenuecat` (Future) |
37
- | D | Zero-to-deployed DX | `create-terreno-app`, `mongo-migrations` (deploy items already exist in the OSS launch program) |
38
- | E | B2B UI surfaces | `data-grid-server-filters`, `charts-and-dashboards`, `dark-mode-theme`, `command-palette`, `wizard-stepper`, `wysiwyg-editor` (markdown stays for now, per D3), `global-search` |
39
- | F | Enterprise & scale | `framework-audit-log` (Next), `rate-limiting` (Next), `job-queues` (Next), `enterprise-sso`, `mfa-step-up-auth` |
40
- | — | Native baseline | `native-module-baseline` — all new native modules land in one major release |
41
-
42
- ## Sequencing
43
-
44
- 1. **Foundations first:** `comms-abstraction` and `orgs-and-teams` are independent and both
45
- unblock most of the program. `inbound-webhooks` lands before billing and before comms
46
- delivery callbacks.
47
- 2. **Close the auth hole early:** `password-reset-and-email-verification` ships as soon as
48
- the abstraction plus one mail adapter exist — the client already calls `POST
49
- /resetPassword`.
50
- 3. **Billing after orgs + webhooks.**
51
- 4. **Native baseline rides the next major release** so every later feature ships as
52
- JS/OTA against the same binary.
53
- 5. Track E/F items are independent and can be scheduled opportunistically.
54
-
55
- ## Native module baseline (next major release)
56
-
57
- Adding a native module to `@terreno/ui` or the example apps forces consumers to cut a new
58
- dev-client/store build, so all additions land together in the next major:
59
-
60
- | Package | Enables | Notes |
61
- |---|---|---|
62
- | `@stripe/stripe-react-native` | Billing (payment sheet, Apple/Google Pay) | Config plugin; Kotlin 2.x + compileSdk 36 via `expo-build-properties` |
63
- | `react-native-purchases` (+ `-ui`) | Mobile IAP (RevenueCat) | Feature ships later (D1: Stripe web-first) — SDK included now so IAP lands without another major |
64
- | `expo-device` | Push registration guards | trivial |
65
- | `expo-crypto` | PKCE for SSO, invite tokens | trivial |
66
- | `expo-local-authentication` | Biometric step-up (MFA) | `NSFaceIDUsageDescription` |
67
- | `expo-system-ui` | Dark mode root/system chrome | trivial |
68
- | `react-native-otp-verify` | Android SMS OTP autofill | Included (D7) for the Twilio Verify flows |
69
-
70
- Excluded by decision: `@10play/tentap-editor` (D3 — markdown stays; revisit in a later
71
- major if WYSIWYG is adopted).
72
-
73
- Already linked, no action: skia, reanimated, gesture-handler, svg, webview,
74
- expo-notifications, expo-secure-store, expo-document-picker/image-picker, blur,
75
- permissions, expo-network. Charts (`victory-native`) is JS-only on existing peers.
76
-
77
- ## Decision log
78
-
79
- All decisions resolved by the maintainer on 2026-08-09:
80
-
81
- | # | Decision | Resolution |
82
- |---|---|---|
83
- | D1 | Billing vendor strategy | **Stripe web-first.** RevenueCat mobile IAP stays `Future`; `react-native-purchases` still ships in the native baseline |
84
- | D2 | First mail adapter | **SendGrid (Twilio SendGrid)** — pairs with the Twilio SMS/Verify credentials story; other providers get items when demand appears |
85
- | D3 | WYSIWYG editor | **Markdown-only for now.** TenTap excluded from the native baseline; revisit in a later major |
86
- | D4 | First push adapter | **Expo push.** |
87
- | D5 | Targets | **Twilio push adapter dropped entirely** (item removed). Promoted to `Next`: `notification-center`, `framework-audit-log`, `rate-limiting`, `job-queues`. Everything else stays as drafted |
88
- | D6 | Orgs data model | **Native Terreno Mongoose models** (works for JWT + Better Auth), optional Better Auth sync later |
89
- | D7 | Final native manifest | **Confirmed:** six core packages + `react-native-purchases` + `react-native-otp-verify`; no TenTap |
90
-
91
- ## IPs in this program
92
-
93
- | IP | Status | Tasks |
94
- |---|---|---|
95
- | [comms-abstraction](comms-abstraction.md) | Draft | [tasks](../tasks/comms-abstraction.md) |
96
- | [comms-adapter-expo-push](comms-adapter-expo-push.md) | Draft | [tasks](../tasks/comms-adapter-expo-push.md) |
97
- | [comms-adapter-twilio-sms](comms-adapter-twilio-sms.md) | Draft | [tasks](../tasks/comms-adapter-twilio-sms.md) |
98
- | [comms-adapter-twilio-verify](comms-adapter-twilio-verify.md) | Draft | [tasks](../tasks/comms-adapter-twilio-verify.md) |
99
- | [comms-adapter-sendgrid](comms-adapter-sendgrid.md) | Draft | [tasks](../tasks/comms-adapter-sendgrid.md) |
100
- | [password-reset-and-email-verification](password-reset-and-email-verification.md) | Draft | [tasks](../tasks/password-reset-and-email-verification.md) |
101
- | [orgs-and-teams](orgs-and-teams.md) | Draft | [tasks](../tasks/orgs-and-teams.md) |
102
- | [billing-stripe](billing-stripe.md) | Draft | [tasks](../tasks/billing-stripe.md) |
103
- | [native-module-baseline](native-module-baseline.md) | Draft | [tasks](../tasks/native-module-baseline.md) |
104
- | [rbac-permissions](rbac-permissions.md) | Draft (pre-existing API design) | TBD |
105
-
106
- Remaining items (`inbound-webhooks`, `invitations-and-seats`, `org-management-ui`,
107
- `notification-center`, `framework-audit-log`, `rate-limiting`, `job-queues`, …) get IPs as
108
- they approach the top of the queue.
109
-
110
- ## Not Included
111
-
112
- - Deploy guides and deployment foundation (OSS launch program).
113
- - `@terreno/syncdb` migration work (PR #869 track).
114
- - Marketing/positioning for the B2B pitch.
@@ -1,158 +0,0 @@
1
- # Implementation Plan: Stripe billing and subscriptions
2
-
3
- **Status:** Draft
4
- **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1025
5
- **Priority:** High
6
- **Effort:** Epic
7
- **Owner:** unassigned
8
- **Created:** 2026-08-09
9
- **Program:** [B2B platform](b2b-platform-program.md)
10
- **Depends on:** [orgs-and-teams](orgs-and-teams.md), inbound-webhooks (IP pending — hard dependency for Phase 3)
11
- **RTK deprecation flag:** None for the backend; the two screens in Phase 4 follow whatever
12
- data layer example-frontend uses at that time
13
-
14
- ## Goal
15
-
16
- Terreno has no way to charge customers. This IP adds `@terreno/billing`, a Stripe-backed
17
- (decision D1: web-first) billing plugin: Stripe customers mapped to organizations,
18
- code-defined plans mapped to Stripe prices, Checkout and Billing Portal session routes,
19
- webhook-driven subscription/entitlement sync, and plan gating that plugs into the existing
20
- feature-flag layer — so `plan: "pro"` can gate features the same way segments do today.
21
-
22
- ## Non-Goals
23
-
24
- - Mobile in-app purchases (`mobile-iap-revenuecat`, Future — the RevenueCat SDK ships in
25
- the native baseline so it can land without another major).
26
- - Usage-based/metered billing, invoicing customization, tax configuration (Stripe Tax is
27
- configured in Stripe, not by Terreno).
28
- - Payment methods beyond what Stripe Checkout/Portal handle.
29
- - Seat-count enforcement (lands with `invitations-and-seats` integration, Phase 5/future).
30
-
31
- ## Decisions
32
-
33
- | Question | Decision |
34
- |----------|----------|
35
- | Package | New workspace package `@terreno/billing` with `stripe` as a real dependency (vendor-specific by design per D1; a provider abstraction is not warranted for one vendor) |
36
- | Billing subject | The **organization** (B2B); optional `subject: "user"` mode for consumer apps |
37
- | Plan definition | Code-defined `plans: [{id, name, stripePriceId, features: string[]}]` passed to `BillingApp` — plans are reviewed code, prices live in Stripe |
38
- | Checkout UX | Stripe-hosted Checkout + Billing Portal (web-first per D1); no custom card forms; `@stripe/stripe-react-native` payment sheet arrives with the IAP item if ever needed |
39
- | Source of truth | Stripe is authoritative; local `BillingSubscription` is a webhook-synced cache — no entitlement decision ever calls Stripe inline |
40
- | Entitlements | `getEntitlements(orgId)` service + a feature-flag segment factory (`planSegment("pro")`) plugging into `FeatureFlagsApp` segments; optional `RequiresPlan("pro")` permission |
41
- | Webhooks | Consumed through the inbound-webhooks framework with Stripe signature verification and event idempotency (`stripeEventId` unique index) |
42
-
43
- ## Architecture
44
-
45
- ```
46
- billing/
47
- src/
48
- billingApp.ts # BillingApp TerrenoPlugin (routes, webhook registration)
49
- billingService.ts # getEntitlements, planSegment, RequiresPlan
50
- stripeSync.ts # webhook event handlers → model sync
51
- models/
52
- billingCustomer.ts # orgId ↔ stripeCustomerId
53
- billingSubscription.ts
54
- billingEvent.ts # processed webhook events (idempotency)
55
- ```
56
-
57
- ```typescript
58
- new BillingApp({
59
- stripeSecretKey: env,
60
- webhookSecret: env,
61
- plans: [{id: "pro", name: "Pro", stripePriceId: "price_...", features: ["ai", "sso"]}],
62
- subject: "organization", // default
63
- onSubscriptionChange?: (sub) => Promise<void>;
64
- })
65
- ```
66
-
67
- Webhook events handled: `checkout.session.completed`,
68
- `customer.subscription.created|updated|deleted`, `invoice.payment_failed`,
69
- `invoice.paid`. Each handler is idempotent via `BillingEvent`.
70
-
71
- ## Models
72
-
73
- Five-type pattern, descriptions, `createdUpdatedPlugin` + `isDeletedPlugin`.
74
-
75
- **BillingCustomer** — `organizationId` (ref, required, unique indexed),
76
- `stripeCustomerId` (string, unique indexed), `email` (string).
77
-
78
- **BillingSubscription** — `organizationId` (ref, required, indexed), `planId` (string —
79
- Terreno plan id), `stripeSubscriptionId` (string, unique), `status` (Stripe status enum:
80
- active/trialing/past_due/canceled/…), `currentPeriodEnd` (date), `cancelAtPeriodEnd`
81
- (boolean), `seats` (number, optional).
82
-
83
- **BillingEvent** — `stripeEventId` (string, unique), `type` (string), `processedAt`
84
- (date), `error` (string, optional).
85
-
86
- ## APIs
87
-
88
- | Method | Path | Permissions | Notes |
89
- |---|---|---|---|
90
- | GET | `/billing/plans` | IsAuthenticated | Code-defined plans (no Stripe call) |
91
- | GET | `/billing/subscription` | IsOrgMember | Current org's subscription + entitlements |
92
- | POST | `/billing/checkoutSession` | IsOrgAdmin | `{planId}` → Stripe Checkout URL; creates customer on first use |
93
- | POST | `/billing/portalSession` | IsOrgAdmin | Stripe Billing Portal URL |
94
- | POST | `/billing/webhooks/stripe` | signature-verified | Via inbound-webhooks framework; raw body |
95
-
96
- ## Notifications
97
-
98
- `invoice.payment_failed` fires `onSubscriptionChange` and sends a dunning email through
99
- `@terreno/comms` when a mail provider is configured (template `paymentFailed`).
100
-
101
- ## UI
102
-
103
- Phase 4, kept deliberately thin: a plan-picker screen (`GET /billing/plans` + checkout
104
- redirect) and a billing settings card (current plan, status, portal link) in
105
- example-frontend; org-management surfaces integrate later via `org-management-ui`.
106
-
107
- ## Phases
108
-
109
- 1. **Package + models:** `@terreno/billing` scaffold, three models, plan config
110
- validation, unit tests.
111
- 2. **Checkout + portal:** customer creation, session routes, `getEntitlements`,
112
- `planSegment`, `RequiresPlan`, tests with mocked Stripe SDK.
113
- 3. **Webhook sync** *(gated on inbound-webhooks)*: signature-verified event routes,
114
- idempotent handlers, dunning email, `stripe-mock`/fixture tests.
115
- 4. **UI + example + docs:** plan picker + billing settings in example-frontend, seeds
116
- with Stripe test-mode instructions, `docs/how-to/add-billing.md`, SDK regen.
117
-
118
- ## Feature Flags & Migrations
119
-
120
- New collections only. Entitlement gating composes with existing feature flags — a flag can
121
- target `planSegment("pro")` exactly like today's `pro-users` segment example.
122
-
123
- ## Activity Log & User Updates
124
-
125
- Subscription lifecycle changes logged via `logger` + `BillingEvent`; org-facing billing
126
- history is the Stripe Portal's job, not Terreno's.
127
-
128
- ## Not Included / Future Work
129
-
130
- - RevenueCat entitlement merge (`mobile-iap-revenuecat`).
131
- - Seat enforcement against Membership counts (`invitations-and-seats` follow-up).
132
- - Metered usage, coupons/promotions management, multi-currency handling beyond Stripe
133
- defaults.
134
-
135
- ## Files to Create / Modify
136
-
137
- - `billing/` (new workspace package), root `package.json` workspace + scripts
138
- - `example-backend/src/server.ts` — env-gated `BillingApp` registration
139
- - `example-frontend` — two screens + SDK regen
140
- - `docs/how-to/add-billing.md`, `docs/reference/billing.md`, env reference
141
- - `.github/workflows/billing-ci.yml`, `publish-on-tag.yml`
142
-
143
- ## Task List
144
-
145
- See [docs/tasks/billing-stripe.md](../tasks/billing-stripe.md).
146
-
147
- ## Acceptance Criteria
148
-
149
- - [ ] An org admin can create a Checkout session for a plan and, after the (test-mode)
150
- webhook lands, `GET /billing/subscription` reports the active plan and entitlements.
151
- - [ ] Webhook events are signature-verified and idempotent: replaying the same
152
- `stripeEventId` is a no-op.
153
- - [ ] A feature flag targeting `planSegment("pro")` evaluates true only for members of a
154
- subscribed org; `RequiresPlan("pro")` returns 403 otherwise.
155
- - [ ] Subscription cancellation (portal) downgrades entitlements after the webhook, not
156
- before.
157
- - [ ] Non-admin org members cannot create checkout/portal sessions.
158
- - [ ] Entitlement checks perform zero inline Stripe API calls.