@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.
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +15 -16
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/templates/bootstrap/backend/types/models/userTypes.template.md +4 -5
- package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
- package/dist/docs/templates/tools/model.template.md +2 -4
- package/dist/docs/versioned/next/README.md +1 -0
- package/dist/docs/versioned/next/explanation/README.md +1 -7
- package/dist/docs/versioned/next/how-to/README.md +2 -8
- package/dist/docs/versioned/next/how-to/deploy-to-gcp.md +228 -20
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
- package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +4 -57
- package/dist/docs/versioned/next/implementationPlans/README.md +30 -56
- package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +1 -2
- package/dist/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/deploy-to-gcp.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/deploy-to-vercel.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/deployment-foundation.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/docs-reference-coverage.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/oss-governance-baseline.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
- package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
- package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
- package/dist/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +0 -1
- package/dist/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +0 -1
- package/dist/docs/versioned/next/reference/README.md +1 -4
- package/dist/docs/versioned/next/reference/environment-variables.md +382 -220
- package/dist/docs/versioned/next/reference/syncdb.md +456 -0
- package/dist/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
- package/dist/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
- package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
- package/dist/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
- package/dist/docs/versioned/next/tasks/deployment-foundation.md +12 -12
- package/dist/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
- package/dist/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
- package/dist/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
- package/dist/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
- package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
- package/dist/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
- package/dist/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
- package/package.json +4 -5
- package/src/docs/templates/bootstrap/backend/types/models/userTypes.template.md +4 -5
- package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
- package/src/docs/templates/tools/model.template.md +2 -4
- package/src/docs/versioned/next/README.md +1 -0
- package/src/docs/versioned/next/explanation/README.md +1 -7
- package/src/docs/versioned/next/how-to/README.md +2 -8
- package/src/docs/versioned/next/how-to/deploy-to-gcp.md +228 -20
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
- package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +4 -57
- package/src/docs/versioned/next/implementationPlans/README.md +30 -56
- package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +1 -2
- package/src/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +0 -1
- package/src/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +0 -1
- package/src/docs/versioned/next/implementationPlans/deploy-to-gcp.md +0 -1
- package/src/docs/versioned/next/implementationPlans/deploy-to-vercel.md +0 -1
- package/src/docs/versioned/next/implementationPlans/deployment-foundation.md +0 -1
- package/src/docs/versioned/next/implementationPlans/docs-reference-coverage.md +0 -1
- package/src/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +0 -1
- package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +0 -1
- package/src/docs/versioned/next/implementationPlans/oss-governance-baseline.md +0 -1
- package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +0 -1
- package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +0 -1
- package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
- package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
- package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
- package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
- package/src/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +0 -1
- package/src/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +0 -1
- package/src/docs/versioned/next/reference/README.md +1 -4
- package/src/docs/versioned/next/reference/environment-variables.md +382 -220
- package/src/docs/versioned/next/reference/syncdb.md +456 -0
- package/src/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
- package/src/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
- package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
- package/src/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
- package/src/docs/versioned/next/tasks/deployment-foundation.md +12 -12
- package/src/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
- package/src/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
- package/src/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
- package/src/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
- package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
- package/src/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
- package/src/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
- package/LICENSE +0 -21
- package/dist/__tests__/bootstrap.test.d.ts +0 -2
- package/dist/__tests__/bootstrap.test.d.ts.map +0 -1
- package/dist/__tests__/bootstrap.test.js +0 -299
- package/dist/__tests__/bootstrap.test.js.map +0 -1
- package/dist/__tests__/databaseQuery.test.d.ts +0 -2
- package/dist/__tests__/databaseQuery.test.d.ts.map +0 -1
- package/dist/__tests__/databaseQuery.test.js +0 -83
- package/dist/__tests__/databaseQuery.test.js.map +0 -1
- package/dist/__tests__/docsRoot.test.d.ts +0 -2
- package/dist/__tests__/docsRoot.test.d.ts.map +0 -1
- package/dist/__tests__/docsRoot.test.js +0 -59
- package/dist/__tests__/docsRoot.test.js.map +0 -1
- package/dist/__tests__/inferPackages.test.d.ts +0 -2
- package/dist/__tests__/inferPackages.test.d.ts.map +0 -1
- package/dist/__tests__/inferPackages.test.js +0 -35
- package/dist/__tests__/inferPackages.test.js.map +0 -1
- package/dist/__tests__/preload.d.ts +0 -2
- package/dist/__tests__/preload.d.ts.map +0 -1
- package/dist/__tests__/preload.js +0 -12
- package/dist/__tests__/preload.js.map +0 -1
- package/dist/__tests__/prompts.test.d.ts +0 -2
- package/dist/__tests__/prompts.test.d.ts.map +0 -1
- package/dist/__tests__/prompts.test.js +0 -295
- package/dist/__tests__/prompts.test.js.map +0 -1
- package/dist/__tests__/resources.test.d.ts +0 -2
- package/dist/__tests__/resources.test.d.ts.map +0 -1
- package/dist/__tests__/resources.test.js +0 -352
- package/dist/__tests__/resources.test.js.map +0 -1
- package/dist/__tests__/search.test.d.ts +0 -2
- package/dist/__tests__/search.test.d.ts.map +0 -1
- package/dist/__tests__/search.test.js +0 -163
- package/dist/__tests__/search.test.js.map +0 -1
- package/dist/__tests__/tools.test.d.ts +0 -2
- package/dist/__tests__/tools.test.d.ts.map +0 -1
- package/dist/__tests__/tools.test.js +0 -540
- package/dist/__tests__/tools.test.js.map +0 -1
- package/dist/__tests__/upgradeGuide.test.d.ts +0 -2
- package/dist/__tests__/upgradeGuide.test.d.ts.map +0 -1
- package/dist/__tests__/upgradeGuide.test.js +0 -22
- package/dist/__tests__/upgradeGuide.test.js.map +0 -1
- package/dist/docs/upgrades/0.31.0.md +0 -60
- package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +0 -65
- package/dist/docs/versioned/next/explanation/deployment-baseline.md +0 -78
- package/dist/docs/versioned/next/explanation/positioning.md +0 -164
- package/dist/docs/versioned/next/explanation/repository-settings.md +0 -44
- package/dist/docs/versioned/next/explanation/roadmap-process.md +0 -330
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +0 -866
- package/dist/docs/versioned/next/how-to/build-for-web.md +0 -69
- package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +0 -115
- package/dist/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +0 -183
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +0 -114
- package/dist/docs/versioned/next/implementationPlans/billing-stripe.md +0 -158
- package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +0 -240
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +0 -107
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +0 -107
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +0 -102
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +0 -93
- package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +0 -188
- package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +0 -269
- package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +0 -105
- package/dist/docs/versioned/next/implementationPlans/orgs-and-teams.md +0 -158
- package/dist/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +0 -147
- package/dist/docs/versioned/next/reference/admin-spa.md +0 -130
- package/dist/docs/versioned/next/reference/ai.md +0 -519
- package/dist/docs/versioned/next/reference/test.md +0 -108
- package/dist/docs/versioned/next/roadmap.md +0 -9
- package/dist/docs/versioned/next/tasks/README.md +0 -42
- package/dist/docs/versioned/next/tasks/billing-stripe.md +0 -75
- package/dist/docs/versioned/next/tasks/comms-abstraction.md +0 -77
- package/dist/docs/versioned/next/tasks/comms-adapter-expo-push.md +0 -29
- package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +0 -29
- package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +0 -29
- package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +0 -19
- package/dist/docs/versioned/next/tasks/infra-mcp.md +0 -43
- package/dist/docs/versioned/next/tasks/native-module-baseline.md +0 -42
- package/dist/docs/versioned/next/tasks/orgs-and-teams.md +0 -70
- package/dist/docs/versioned/next/tasks/password-reset-and-email-verification.md +0 -60
- package/src/docs/upgrades/0.31.0.md +0 -60
- package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +0 -65
- package/src/docs/versioned/next/explanation/deployment-baseline.md +0 -78
- package/src/docs/versioned/next/explanation/positioning.md +0 -164
- package/src/docs/versioned/next/explanation/repository-settings.md +0 -44
- package/src/docs/versioned/next/explanation/roadmap-process.md +0 -330
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +0 -866
- package/src/docs/versioned/next/how-to/build-for-web.md +0 -69
- package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +0 -115
- package/src/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +0 -183
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +0 -114
- package/src/docs/versioned/next/implementationPlans/billing-stripe.md +0 -158
- package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +0 -240
- package/src/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +0 -107
- package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +0 -107
- package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +0 -102
- package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +0 -93
- package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +0 -188
- package/src/docs/versioned/next/implementationPlans/infra-mcp.md +0 -269
- package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +0 -105
- package/src/docs/versioned/next/implementationPlans/orgs-and-teams.md +0 -158
- package/src/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +0 -147
- package/src/docs/versioned/next/reference/admin-spa.md +0 -130
- package/src/docs/versioned/next/reference/ai.md +0 -519
- package/src/docs/versioned/next/reference/test.md +0 -108
- package/src/docs/versioned/next/roadmap.md +0 -9
- package/src/docs/versioned/next/tasks/README.md +0 -42
- package/src/docs/versioned/next/tasks/billing-stripe.md +0 -75
- package/src/docs/versioned/next/tasks/comms-abstraction.md +0 -77
- package/src/docs/versioned/next/tasks/comms-adapter-expo-push.md +0 -29
- package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +0 -29
- package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +0 -29
- package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +0 -19
- package/src/docs/versioned/next/tasks/infra-mcp.md +0 -43
- package/src/docs/versioned/next/tasks/native-module-baseline.md +0 -42
- package/src/docs/versioned/next/tasks/orgs-and-teams.md +0 -70
- 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.
|