@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,158 +0,0 @@
1
- # Implementation Plan: Organizations, teams, and multi-tenant scoping
2
-
3
- **Status:** Draft
4
- **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1024
5
- **Priority:** High
6
- **Effort:** Big batch
7
- **Owner:** unassigned
8
- **Created:** 2026-08-09
9
- **Program:** [B2B platform](b2b-platform-program.md)
10
- **Depends on:** none (soft: [rbac-permissions](rbac-permissions.md) for role vocabulary; this IP ships coarse org roles that RBAC later subsumes)
11
- **RTK deprecation flag:** None — backend only; frontend surfaces are `org-management-ui`
12
-
13
- ## Goal
14
-
15
- Terreno's entire access story is per-user: `ownerId` fields, `OwnerQueryFilter`,
16
- `Permissions.IsOwner`. A B2B app needs data owned by an **organization** and accessed by
17
- its **members**. This IP adds the tenant analog of the owner stack to `@terreno/api`:
18
- Organization and Membership models, an org-context middleware, an `orgScopedPlugin` for
19
- schemas, `OrgQueryFilter` and `Permissions.IsOrgMember`/`IsOrgAdmin` for modelRouter, and
20
- CRUD routes for orgs and memberships — so `modelRouter(Todo, {queryFilter: OrgQueryFilter,
21
- permissions: {...IsOrgMember}})` is all an app needs for tenant-safe endpoints.
22
-
23
- ## Non-Goals
24
-
25
- - Invitations, seat counting (`invitations-and-seats` item).
26
- - Fine-grained RBAC (roles beyond `member`/`admin` — [rbac-permissions](rbac-permissions.md)).
27
- - Org switcher and management screens (`org-management-ui` item).
28
- - Billing linkage (`billing-stripe` maps customers to orgs later).
29
- - Cross-org data sharing, nested teams/sub-orgs (future work).
30
-
31
- ## Decisions
32
-
33
- | Question | Decision |
34
- |----------|----------|
35
- | **D6 (resolved 2026-08-09):** native Mongoose models vs Better Auth `organization` plugin | **Native models.** The JWT/passport path (still supported and the example default) cannot use a Better-Auth-only construct; native models serve both auth paths, keep modelRouter/query-filter integration first-class, and can sync to Better Auth's plugin later if wanted |
36
- | Org context transport | `X-Organization-Id` header, resolved by middleware into `req.organization` + `req.membership`; explicit query/body override rejected |
37
- | One org or many per user? | Many (Membership join collection); apps wanting single-org enforce via option `maxOrgsPerUser: 1` |
38
- | Coarse roles now | `Membership.role: "admin" \| "member"` — deliberately minimal; RBAC IP replaces this enum with role references, migration documented there |
39
- | Scoping mechanism | `orgScopedPlugin` adds required indexed `organizationId`; `OrgQueryFilter` filters lists by active memberships; `preCreate` helper injects the context org |
40
- | Personal orgs? | No auto-created personal org; apps opt in via `createDefaultOrgOnSignup` option |
41
-
42
- ## Architecture
43
-
44
- ```
45
- api/src/orgs/
46
- organizationModel.ts # Organization + Membership schemas/models
47
- orgContext.ts # middleware: header → req.organization/req.membership
48
- orgPlugin.ts # orgScopedPlugin(schema) — organizationId field + index
49
- orgPermissions.ts # IsOrgMember, IsOrgAdmin, OrgQueryFilter
50
- orgsApp.ts # OrgsApp TerrenoPlugin — routes + options
51
- ```
52
-
53
- `OrgsApp` options:
54
-
55
- ```typescript
56
- new OrgsApp({
57
- maxOrgsPerUser?: number;
58
- createDefaultOrgOnSignup?: boolean;
59
- onOrgCreated?: (org, owner) => Promise<void>;
60
- })
61
- ```
62
-
63
- Request flow: `authenticateMiddleware` → `orgContextMiddleware` (loads org by header,
64
- verifies an active membership for `req.user`, 403 otherwise) → route. Routes that take
65
- `Permissions.IsOrgMember`/`IsOrgAdmin` require the context; `OrgQueryFilter` works with or
66
- without a context (no header → all orgs the user belongs to; header → that org only).
67
-
68
- ## Models
69
-
70
- Both use the five-type pattern, `description` on every field, `createdUpdatedPlugin` +
71
- `isDeletedPlugin`, `strict: "throw"`.
72
-
73
- **Organization** — `name` (string, required), `slug` (string, unique, indexed, generated),
74
- `ownerId` (ref User, required — the creating user; ownership transferable), `settings`
75
- (Mixed, app-defined), `disabled` (boolean).
76
-
77
- **Membership** — `organizationId` (ref Organization, required, indexed), `userId` (ref
78
- User, required, indexed), `role` ("admin" | "member", default "member"), `status`
79
- ("active" | "suspended", default "active"), compound unique index on
80
- `(organizationId, userId)`. Statics: `findActiveForUser`, `isMember`, `isOrgAdmin`.
81
-
82
- ## APIs
83
-
84
- | Method | Path | Permissions | Notes |
85
- |---|---|---|---|
86
- | POST | `/orgs` | IsAuthenticated | Creates org + admin membership for creator; respects `maxOrgsPerUser` |
87
- | GET | `/orgs` | IsAuthenticated | Lists orgs the user belongs to (membership join) |
88
- | GET | `/orgs/:id` | IsOrgMember | |
89
- | PATCH | `/orgs/:id` | IsOrgAdmin | name/settings; ownership transfer admin-only |
90
- | DELETE | `/orgs/:id` | IsOrgAdmin | Soft delete; memberships cascade to suspended |
91
- | GET | `/orgs/:id/members` | IsOrgMember | Paginated membership list (populated user name/email) |
92
- | PATCH | `/orgs/:id/members/:memberId` | IsOrgAdmin | Role/status change; cannot demote last admin |
93
- | DELETE | `/orgs/:id/members/:memberId` | IsOrgAdmin | Remove member; cannot remove last admin |
94
-
95
- Member creation is **not** exposed here — members arrive via `invitations-and-seats`
96
- (or `createDefaultOrgOnSignup`). All routes via modelRouter where shapes fit, custom
97
- `createOpenApiBuilder` routes for member management.
98
-
99
- ## Notifications
100
-
101
- None in this IP (invitation emails belong to `invitations-and-seats`).
102
-
103
- ## UI
104
-
105
- None in this IP (`org-management-ui`). example-backend gains an org-scoped demo model
106
- (`Project`) exercising `orgScopedPlugin` + `OrgQueryFilter` end to end.
107
-
108
- ## Phases
109
-
110
- 1. **Models + plugin:** Organization, Membership, `orgScopedPlugin`, statics, unit tests.
111
- 2. **Context + permissions:** middleware, `IsOrgMember`/`IsOrgAdmin`, `OrgQueryFilter`,
112
- modelRouter integration tests (member of A cannot read B's rows — the critical suite).
113
- 3. **Routes + app:** `OrgsApp`, org/member routes, last-admin guards, OpenAPI, supertest.
114
- 4. **Example + docs:** example-backend `Project` model + routes, seeds, admin panel model
115
- registration, `docs/how-to/add-organizations.md`, reference docs, SDK regen.
116
-
117
- ## Feature Flags & Migrations
118
-
119
- No migration for existing apps (new collections; opt-in plugin). Adopting apps that
120
- backfill `organizationId` onto existing collections get a documented backfill recipe —
121
- formal tooling arrives with `mongo-migrations`.
122
-
123
- ## Activity Log & User Updates
124
-
125
- Org create/delete and membership role changes fire an `onOrgAudit` hook (mirroring
126
- `onAdminAudit`) so apps can log them; framework-wide audit persistence is the
127
- `framework-audit-log` item.
128
-
129
- ## Not Included / Future Work
130
-
131
- - Teams within orgs (the Membership schema reserves a `teamIds` extension point but ships
132
- without it — YAGNI until a consumer needs it).
133
- - Org-level settings UI, domain capture/auto-join (SSO IP touches this).
134
- - Per-org rate limits, quotas.
135
-
136
- ## Files to Create / Modify
137
-
138
- - `api/src/orgs/*` (new), `api/src/index.ts` exports
139
- - `api/src/permissions.ts` — export org permissions alongside existing ones
140
- - `example-backend/src/models/project.ts`, `api/projects.ts`, seeds, `server.ts`
141
- - `docs/how-to/add-organizations.md`, `docs/reference/api.md`
142
- - `example-frontend` SDK regen (`bun run sdk`) after backend routes land
143
-
144
- ## Task List
145
-
146
- See [docs/tasks/orgs-and-teams.md](../tasks/orgs-and-teams.md).
147
-
148
- ## Acceptance Criteria
149
-
150
- - [ ] A user can create an org, becoming its admin member; `maxOrgsPerUser` is enforced.
151
- - [ ] With two orgs seeded, a member of org A listing an org-scoped model with
152
- `OrgQueryFilter` never receives org B documents — including via crafted `$or` /
153
- query-param attempts.
154
- - [ ] Requests with an `X-Organization-Id` the user is not an active member of get 403.
155
- - [ ] The last admin of an org cannot be demoted or removed.
156
- - [ ] Org routes appear in `/openapi.json`; regenerated SDK compiles; admin panel can
157
- browse Organizations and Memberships.
158
- - [ ] Soft-deleting an org suspends its memberships and hides its scoped data from lists.
@@ -1,147 +0,0 @@
1
- # Implementation Plan: Password reset and email verification
2
-
3
- **Status:** Draft
4
- **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1023
5
- **Priority:** High
6
- **Effort:** Big batch
7
- **Owner:** unassigned
8
- **Created:** 2026-08-09
9
- **Program:** [B2B platform](b2b-platform-program.md)
10
- **Depends on:** [comms-abstraction](comms-abstraction.md), [comms-adapter-sendgrid](comms-adapter-sendgrid.md)
11
- **RTK deprecation flag:** None — the RTK client's existing `resetPassword` endpoint gets a
12
- backend at last; new client work is a thin screen pair noted in UI
13
-
14
- ## Goal
15
-
16
- The JWT auth path has no way to recover an account or verify an email address: the
17
- `@terreno/rtk` client ships a `resetPassword` mutation with **no backend route**, and
18
- nothing sets or checks email verification. This IP adds forgot/reset password and email
19
- verification flows to `@terreno/api`'s JWT auth, wires the Better Auth equivalents to the
20
- same mail templates, and sends everything through `@terreno/comms`.
21
-
22
- ## Non-Goals
23
-
24
- - MFA / step-up auth (`mfa-step-up-auth` item).
25
- - Magic-link login.
26
- - SMS-based recovery (possible later via the Twilio Verify adapter).
27
- - Account lockout / brute-force throttling beyond token single-use + expiry
28
- (`rate-limiting` item hardens the endpoints).
29
-
30
- ## Decisions
31
-
32
- | Question | Decision |
33
- |----------|----------|
34
- | Token design | 32-byte crypto-random token, stored **hashed** (sha256) with expiry (`1h` reset / `24h` verify) and single-use consumption; separate `AuthToken` collection rather than fields on User |
35
- | Enumeration safety | `POST /auth/forgotPassword` always returns 202 regardless of whether the email exists |
36
- | Password set mechanism | `passport-local-mongoose`'s `setPassword` on the user document, then invalidate all refresh tokens |
37
- | RTK client compatibility | The existing RTK `resetPassword` endpoint's path/shape is the contract — verify at implementation time and alias if it differs from `/auth/resetPassword` |
38
- | Verification gating | Opt-in `requireEmailVerification` auth option: unverified users get 403 with code `email-not-verified` on login; default off (non-breaking) |
39
- | Better Auth path | Enable Better Auth's built-in reset/verification, providing `sendResetPassword` / `sendVerificationEmail` hooks that call the same comms templates so both auth paths send identical mail |
40
- | Link targets | Deep-link/URL base from a new `authOptions.publicAppUrl`; web route examples in example-frontend |
41
-
42
- ## Architecture
43
-
44
- ```
45
- api/src/authTokens.ts # AuthToken model + issue/consume helpers
46
- api/src/authRecovery.ts # forgot/reset/verify routes + options
47
- # registered by addAuthRoutes when comms is available
48
- comms templates # resetPassword + verifyEmail templates (subject/text/html)
49
- ```
50
-
51
- Flow (reset): `POST /auth/forgotPassword {email}` → 202 always; if user exists, issue
52
- token, send mail with `${publicAppUrl}/resetPassword?token=...` → `POST
53
- /auth/resetPassword {token, password}` → validate + consume token, `setPassword`,
54
- invalidate refresh tokens, 200.
55
-
56
- Flow (verify): token issued on signup (and via `POST /auth/sendVerification` for
57
- re-sends, authenticated); `POST /auth/verifyEmail {token}` sets `user.emailVerified =
58
- true`.
59
-
60
- ## Models
61
-
62
- **AuthToken** — `userId` (ref User, required, indexed), `tokenHash` (string, unique),
63
- `type` ("passwordReset" | "emailVerification"), `expiresAt` (date, TTL-indexed),
64
- `consumedAt` (date, optional). Statics: `issueFor(user, type)`, `consume(token, type)`
65
- (atomic findOneAndUpdate guarding single-use).
66
-
67
- **User** — adds `emailVerified` (boolean, default false) via a small
68
- `emailVerificationPlugin` so existing user schemas opt in without breaking.
69
-
70
- ## APIs
71
-
72
- | Method | Path | Permissions | Notes |
73
- |---|---|---|---|
74
- | POST | `/auth/forgotPassword` | anonymous | `{email}` → always 202 |
75
- | POST | `/auth/resetPassword` | anonymous | `{token, password}`; path/shape matched to the RTK client contract |
76
- | POST | `/auth/sendVerification` | IsAuthenticated | Re-send verification mail |
77
- | POST | `/auth/verifyEmail` | anonymous | `{token}` |
78
-
79
- All registered by `addAuthRoutes` when a mail-capable `CommsApp` is present; without one,
80
- routes 501 in production and console-send in development (inherited comms behavior).
81
-
82
- ## Notifications
83
-
84
- Two mail templates (`resetPassword`, `verifyEmail`) shipped in `@terreno/comms` templates
85
- with app-override hooks.
86
-
87
- ## UI
88
-
89
- example-frontend: `/resetPassword` (token from URL, new-password form using the existing
90
- RTK `resetPassword` mutation) and a "verify your email" banner + re-send button on the
91
- profile screen. `LoginScreen` gains an optional `onForgotPassword` link prop in
92
- `@terreno/ui`.
93
-
94
- ## Phases
95
-
96
- 1. **Token infrastructure:** AuthToken model, issue/consume, `emailVerificationPlugin`,
97
- unit tests.
98
- 2. **JWT routes + mail:** four routes, templates, refresh-token invalidation,
99
- enumeration-safety + single-use/expiry test suite.
100
- 3. **Better Auth parity:** wire `sendResetPassword`/`sendVerificationEmail` hooks to the
101
- same templates; parity tests.
102
- 4. **Client + docs:** example-frontend screens, `@terreno/ui` login link prop, SDK regen,
103
- `docs/how-to/password-reset.md`, env/reference docs.
104
-
105
- ## Feature Flags & Migrations
106
-
107
- `requireEmailVerification` defaults off; enabling it for an existing app is documented
108
- (backfill `emailVerified: true` for existing users or accept re-verification). No schema
109
- migration otherwise.
110
-
111
- ## Activity Log & User Updates
112
-
113
- Token issue/consume and verification events logged via `logger` and `CommsMessage`
114
- rows; no user-facing activity feed.
115
-
116
- ## Not Included / Future Work
117
-
118
- - Password strength policy hooks (exists via `PasswordRequirements` UI; server policy is
119
- future work).
120
- - Admin-triggered forced resets (admin panel action — future).
121
- - Endpoint rate limiting (`rate-limiting` item).
122
-
123
- ## Files to Create / Modify
124
-
125
- - `api/src/authTokens.ts`, `api/src/authRecovery.ts` (new), `api/src/auth.ts`,
126
- `api/src/plugins.ts` (emailVerificationPlugin), `api/src/index.ts`
127
- - `comms/src/templates.ts` — two templates
128
- - `example-frontend/app/resetPassword.tsx`, profile banner, SDK regen
129
- - `ui/src/login/LoginScreen.tsx` — `onForgotPassword` prop
130
- - `docs/how-to/password-reset.md`, `docs/reference/environment-variables.md`
131
-
132
- ## Task List
133
-
134
- See [docs/tasks/password-reset-and-email-verification.md](../tasks/password-reset-and-email-verification.md).
135
-
136
- ## Acceptance Criteria
137
-
138
- - [ ] `POST /auth/forgotPassword` returns 202 for existing and non-existing emails alike,
139
- sending mail only for existing ones.
140
- - [ ] A reset token works exactly once, fails after expiry, and a successful reset
141
- invalidates outstanding refresh tokens.
142
- - [ ] The unmodified RTK client `resetPassword` mutation completes the flow end to end.
143
- - [ ] With `requireEmailVerification` on, an unverified user gets 403 code
144
- `email-not-verified` on login and can complete verification to proceed.
145
- - [ ] Better Auth mode sends the same two templates through comms.
146
- - [ ] example-frontend can complete forgot → email link (console adapter output in dev) →
147
- reset → login with the new password; UI verification evidence attached to the PR.
@@ -1,130 +0,0 @@
1
- # @terreno/admin-spa
2
-
3
- Standalone Expo Router admin web SPA plus an Express plugin (`AdminSpaServeApp`) that serves the pre-built static export from a Terreno backend on the same origin.
4
-
5
- ## Table of Contents
6
-
7
- - [Install](#install)
8
- - [Commands](#commands)
9
- - [Architecture](#architecture)
10
- - [AdminSpaServeApp](#adminspaserveapp)
11
- - [App config](#app-config)
12
- - [Serving and caching](#serving-and-caching)
13
- - [Mount path constraint](#mount-path-constraint)
14
- - [Standalone SPA vs embedded admin-frontend](#standalone-spa-vs-embedded-admin-frontend)
15
-
16
- ## Install
17
-
18
- ```bash
19
- bun add @terreno/admin-spa @terreno/api
20
- ```
21
-
22
- `@terreno/api` is a peer dependency (TerrenoPlugin registration).
23
-
24
- ## Commands
25
-
26
- From the `@terreno/admin-spa` package directory:
27
-
28
- ```bash
29
- bun run compile # Compile the server plugin (src/ -> src/dist)
30
- bun run build:web # Expo static export into dist/
31
- bun run dev # expo start --web --port 8083
32
- bun run test:ci # Serve-plugin unit tests
33
- bun run smoke # Smoke test over built dist/
34
- bun run test:e2e # Playwright e2e over built dist/
35
- ```
36
-
37
- ## Architecture
38
-
39
- ```
40
- admin-spa/
41
- src/
42
- serve.ts # AdminSpaServeApp TerrenoPlugin
43
- appConfig.ts # AdminSpaAppConfig defaults + merge helper
44
- index.ts # Public exports
45
- app/ # Expo Router SPA (Better Auth + admin screens)
46
- store/ # Redux + Better Auth client
47
- dist/ # Pre-built static export (produced by build:web)
48
- ```
49
-
50
- Boot flow in the SPA: `AppConfigGate` fetches `${basePath}/app-config.json` → `StoreProvider` builds Better Auth + Redux → `AdminGate` redirects anonymous users to `/login` and non-admins to `/forbidden`.
51
-
52
- ## AdminSpaServeApp
53
-
54
- `AdminSpaServeApp` implements `TerrenoPlugin`. Register it on `TerrenoApp` alongside `AdminApp` and auth plugins:
55
-
56
- ```typescript
57
- import {AdminSpaServeApp} from "@terreno/admin-spa";
58
- import {AdminApp} from "@terreno/admin-backend";
59
- import {BetterAuthApp, TerrenoApp} from "@terreno/api";
60
-
61
- new TerrenoApp({userModel: User})
62
- .register(new BetterAuthApp({config: betterAuthConfig, userModel: User}))
63
- .register(new AdminApp({models: [...]}))
64
- .register(
65
- new AdminSpaServeApp({
66
- basePath: "/console",
67
- appConfig: {brandName: "Acme Admin", providers: ["email", "google"]},
68
- devProxyTarget: process.env.ADMIN_SPA_DEV_PROXY,
69
- })
70
- )
71
- .start();
72
- ```
73
-
74
- `example-backend` registers the plugin when `ADMIN_SPA_ENABLED=true`, with optional `ADMIN_SPA_DEV_PROXY` and `ADMIN_SPA_DIST_DIR` overrides.
75
-
76
- ### AdminSpaServeOptions
77
-
78
- | Option | Default | Description |
79
- |--------|---------|-------------|
80
- | `basePath` | `"/console"` | URL prefix where the SPA is mounted. Admin API stays at `/admin`. |
81
- | `appConfig` | see [App config](#app-config) | Partial config merged over defaults; served at `${basePath}/app-config.json`. |
82
- | `distDir` | `<package>/dist` | Directory containing the pre-built static export (tests/custom builds). |
83
- | `devProxyTarget` | — | When set, proxy all SPA paths to a running `expo start --web` server (e.g. `http://localhost:8083`). `app-config.json` is still served by the plugin. |
84
-
85
- ### Utility exports
86
-
87
- - `rewriteIndexHtml(html, basePath)` — Rewrites absolute `/_expo/` and `/assets/` refs for sub-path mounts.
88
- - `injectBaseGlobal(html, basePath)` — Injects `window.__ADMIN_SPA_BASE__` before `</head>`.
89
- - `resolveAppConfig(overrides?)` — Merge partial config with `DEFAULT_APP_CONFIG`.
90
- - `DEFAULT_APP_CONFIG` — Default `app-config.json` values.
91
-
92
- ## App config
93
-
94
- `AdminSpaAppConfig` fields (from `appConfig.ts`):
95
-
96
- | Field | Default | Description |
97
- |-------|---------|-------------|
98
- | `brandName` | `"Terreno Admin"` | Header brand name |
99
- | `logoUrl` | — | Optional logo URL (absolute or relative) |
100
- | `primaryColor` | `"#2563EB"` | Primary brand color (hex) |
101
- | `providers` | `["email"]` | Login providers: `"email"`, `"google"`, `"github"`, `"apple"` |
102
- | `authBasePath` | `"/api/auth"` | Better Auth routes on this origin |
103
- | `adminApiBasePath` | `"/admin"` | Admin API base path on this origin |
104
-
105
- ## Serving and caching
106
-
107
- **Production (no `devProxyTarget`):**
108
-
109
- 1. `${basePath}/_expo` and `${basePath}/assets` — `express.static` with `maxAge: 365d`, `immutable: true`.
110
- 2. `${basePath}/app-config.json` — `Cache-Control: no-store`.
111
- 3. `${basePath}` and `${basePath}/*splat` — SPA fallback returns rewritten `index.html` with `Cache-Control: no-store`.
112
-
113
- `index.html` is read once at startup; asset paths are rewritten to `${basePath}/...` and `window.__ADMIN_SPA_BASE__` is injected for deep-link config fetches.
114
-
115
- **Development (`devProxyTarget` set):**
116
-
117
- - `${basePath}/app-config.json` served locally (no cache).
118
- - All other `${basePath}/*` requests proxied to the Expo dev server (WebSocket enabled).
119
-
120
- If no bundle exists at `distDir/index.html`, the plugin logs a warning suggesting `bun run --filter '@terreno/admin-spa' build:web` or `devProxyTarget`.
121
-
122
- ## Mount path constraint
123
-
124
- The default web export is built with `experiments.baseUrl: "/console"`, so `basePath` must match. To mount elsewhere, rebuild with a matching base (e.g. `EXPO_BASE_URL=/admin-ui bun run build:web`) and set `basePath` accordingly. Mounting at `/` requires a root-base build and `basePath: "/"`.
125
-
126
- ## Standalone SPA vs embedded admin-frontend
127
-
128
- Use **`@terreno/admin-spa`** when you want a complete admin UI served from the API process (same-origin Better Auth cookies, no separate static deploy). The backend image stays lean until you opt in by registering `AdminSpaServeApp`.
129
-
130
- Use **`@terreno/admin-frontend`** when admin screens live inside your main Expo app (e.g. `example-frontend/app/admin/*`). You bring your own routing, auth, and deploy; the package supplies reusable list/table/form components and hooks against `/admin` CRUD routes from `@terreno/admin-backend`.