@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,240 +0,0 @@
|
|
|
1
|
-
# Implementation Plan: Pluggable communications layer (@terreno/comms)
|
|
2
|
-
|
|
3
|
-
**Status:** Draft
|
|
4
|
-
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1018
|
|
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 (inbound-webhooks is needed only by adapter delivery-callback phases)
|
|
11
|
-
**RTK deprecation flag:** None — backend-only package
|
|
12
|
-
|
|
13
|
-
## Goal
|
|
14
|
-
|
|
15
|
-
Terreno backends cannot send email, SMS, or push notifications: there is no mail
|
|
16
|
-
integration at all, `expo-server-sdk` sits unused in `@terreno/api`, and the RTK client's
|
|
17
|
-
`resetPassword` call has no backend to talk to. This IP creates `@terreno/comms`, a new
|
|
18
|
-
workspace package that defines **provider interfaces** for the four outbound channels
|
|
19
|
-
(mail, SMS, push, OTP verification), a **`CommsApp`** TerrenoPlugin that registers
|
|
20
|
-
configured providers and exposes send services to routes and other plugins, **push-token
|
|
21
|
-
registration** routes and model, a **delivery log**, and **console adapters** so every
|
|
22
|
-
channel works in development with zero external accounts.
|
|
23
|
-
|
|
24
|
-
Concrete providers (Twilio, SendGrid, Expo push) are separate IPs — one per adapter — so
|
|
25
|
-
consumer apps install only the SDKs they use.
|
|
26
|
-
|
|
27
|
-
## Non-Goals
|
|
28
|
-
|
|
29
|
-
- Any concrete provider SDK integration (see `comms-adapter-*` IPs).
|
|
30
|
-
- In-app notification center, inbox UI, or user notification preferences
|
|
31
|
-
(`notification-center` item).
|
|
32
|
-
- Password reset / email verification flows (`password-reset-and-email-verification` item
|
|
33
|
-
— a consumer of this layer).
|
|
34
|
-
- Inbound webhook handling (`inbound-webhooks` item); this IP only defines the
|
|
35
|
-
`DeliveryEvent` type adapters will feed from callbacks.
|
|
36
|
-
- Broadcast/bulk campaign tooling.
|
|
37
|
-
|
|
38
|
-
## Decisions
|
|
39
|
-
|
|
40
|
-
| Question | Decision |
|
|
41
|
-
|----------|----------|
|
|
42
|
-
| New package or `@terreno/api` module? | New package `@terreno/comms`, mirroring `@terreno/feature-flags` — keeps provider SDKs out of the core dependency tree |
|
|
43
|
-
| How do provider SDKs stay optional? | Adapters live in subpath exports (`@terreno/comms/adapters/<name>`) with their SDKs as `peerDependencies` marked optional; core package has zero provider SDKs |
|
|
44
|
-
| Precedent to follow | `secretProviders.ts` (env + GCP behind one interface) and the OpenFeature provider in `@terreno/feature-flags` |
|
|
45
|
-
| Templates | Minimal: `{subject, text, html}` render helper with variable interpolation; no template builder |
|
|
46
|
-
| Where do send calls log? | `CommsMessage` Mongoose model, mirroring the `AIRequest.logRequest` pattern — logging failures never break the send |
|
|
47
|
-
| Dev experience | `ConsoleMailProvider` / `ConsoleSmsProvider` / `ConsolePushProvider` / `ConsoleVerificationProvider` print to logger and record to `CommsMessage`; default when no provider configured in non-production |
|
|
48
|
-
|
|
49
|
-
## Architecture
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
@terreno/comms
|
|
53
|
-
src/
|
|
54
|
-
types.ts # Provider interfaces + message/result types
|
|
55
|
-
commsApp.ts # CommsApp TerrenoPlugin
|
|
56
|
-
commsService.ts # sendMail/sendSms/sendPush/startVerification/checkVerification
|
|
57
|
-
models/
|
|
58
|
-
commsMessage.ts # Delivery log
|
|
59
|
-
pushToken.ts # Device push token registry
|
|
60
|
-
routes/
|
|
61
|
-
pushTokens.ts # modelRouter for token registration
|
|
62
|
-
commsExplorer.ts # Admin-only delivery log explorer
|
|
63
|
-
adapters/
|
|
64
|
-
console.ts # Dev adapters for all four channels
|
|
65
|
-
templates.ts # renderTemplate({subject, text, html}, data)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Provider interfaces (types.ts)
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
export interface SendResult {
|
|
72
|
-
providerMessageId?: string;
|
|
73
|
-
accepted: boolean;
|
|
74
|
-
error?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface MailMessage {
|
|
78
|
-
to: string | string[];
|
|
79
|
-
from?: string; // default from CommsApp options
|
|
80
|
-
subject: string;
|
|
81
|
-
text?: string;
|
|
82
|
-
html?: string;
|
|
83
|
-
replyTo?: string;
|
|
84
|
-
metadata?: Record<string, string>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface MailProvider {
|
|
88
|
-
readonly id: string; // "console" | "sendgrid" | ...
|
|
89
|
-
sendMail(message: MailMessage): Promise<SendResult>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface SmsProvider {
|
|
93
|
-
readonly id: string;
|
|
94
|
-
sendSms(message: {to: string; body: string}): Promise<SendResult>;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface PushMessage {
|
|
98
|
-
tokens: string[];
|
|
99
|
-
title: string;
|
|
100
|
-
body: string;
|
|
101
|
-
data?: Record<string, unknown>;
|
|
102
|
-
badge?: number;
|
|
103
|
-
sound?: string | null;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface PushProvider {
|
|
107
|
-
readonly id: string;
|
|
108
|
-
sendPush(message: PushMessage): Promise<SendResult[]>; // one per token
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface VerificationProvider {
|
|
112
|
-
readonly id: string;
|
|
113
|
-
startVerification(options: {to: string; channel: "sms" | "email"}): Promise<SendResult>;
|
|
114
|
-
checkVerification(options: {to: string; code: string}): Promise<{valid: boolean}>;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export interface DeliveryEvent {
|
|
118
|
-
channel: "mail" | "sms" | "push";
|
|
119
|
-
providerMessageId: string;
|
|
120
|
-
status: "delivered" | "bounced" | "failed" | "opened";
|
|
121
|
-
raw?: unknown;
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### CommsApp (TerrenoPlugin)
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
export interface CommsOptions {
|
|
129
|
-
mail?: MailProvider;
|
|
130
|
-
sms?: SmsProvider;
|
|
131
|
-
push?: PushProvider;
|
|
132
|
-
verification?: VerificationProvider;
|
|
133
|
-
defaultFrom?: string; // mail from fallback
|
|
134
|
-
logMessages?: boolean; // default true → CommsMessage
|
|
135
|
-
onDeliveryEvent?: (event: DeliveryEvent) => Promise<void>;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
new TerrenoApp({userModel: User})
|
|
139
|
-
.register(new CommsApp({mail: new ConsoleMailProvider(), push: new ExpoPushProvider()}))
|
|
140
|
-
.start();
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
`CommsApp.register()` mounts the push-token routes and admin explorer;
|
|
144
|
-
`getCommsService()` (module-level accessor, mirroring how `FeatureFlagsApp` exposes
|
|
145
|
-
evaluation) gives routes and other plugins `sendMail` / `sendSms` / `sendPushToUser` /
|
|
146
|
-
`startVerification` / `checkVerification`. `sendPushToUser(userId, message)` resolves the
|
|
147
|
-
user's active `PushToken`s and prunes tokens the provider reports dead.
|
|
148
|
-
|
|
149
|
-
Unconfigured channel behavior: in production, throw `APIError({status: 501, title:
|
|
150
|
-
"Comms channel not configured"})`; in development, fall back to the console adapter with a
|
|
151
|
-
`logger.warn`.
|
|
152
|
-
|
|
153
|
-
## Models
|
|
154
|
-
|
|
155
|
-
All fields carry `description`; both models use `createdUpdatedPlugin` + `isDeletedPlugin`
|
|
156
|
-
and live per the five-type pattern (`CommsMessageDocument`, etc.).
|
|
157
|
-
|
|
158
|
-
**PushToken** — `userId` (ref User, required, indexed), `token` (string, required, unique),
|
|
159
|
-
`platform` ("ios" | "android" | "web"), `deviceId` (string, optional), `active` (boolean,
|
|
160
|
-
default true), `lastSeenAt` (date).
|
|
161
|
-
|
|
162
|
-
**CommsMessage** — `channel` ("mail" | "sms" | "push" | "verification"), `provider`
|
|
163
|
-
(string), `to` (string, redacted-at-rest for sms/mail per option), `subject` (string,
|
|
164
|
-
optional), `userId` (ref User, optional), `status` ("sent" | "failed" | "delivered" |
|
|
165
|
-
"bounced"), `providerMessageId` (string, indexed), `error` (string, optional), `metadata`
|
|
166
|
-
(Mixed). Static `logSend()` mirrors `AIRequest.logRequest`.
|
|
167
|
-
|
|
168
|
-
## APIs
|
|
169
|
-
|
|
170
|
-
| Method | Path | Permissions | Notes |
|
|
171
|
-
|---|---|---|---|
|
|
172
|
-
| POST | `/comms/pushTokens` | IsAuthenticated | Upsert by `token`; sets `userId`, `platform`, `lastSeenAt` |
|
|
173
|
-
| GET | `/comms/pushTokens` | IsOwner via queryFilter | List own device tokens |
|
|
174
|
-
| DELETE | `/comms/pushTokens/:id` | IsOwner | Deactivate on logout/uninstall |
|
|
175
|
-
| GET | `/comms/messages` | IsAdmin | Paginated delivery log explorer; filters: `channel`, `status`, `userId`, date range |
|
|
176
|
-
|
|
177
|
-
Push token routes via `modelRouter` with `preCreate` owner injection and
|
|
178
|
-
`OwnerQueryFilter`; explorer via `createOpenApiBuilder`, modeled on
|
|
179
|
-
`addAiRequestsExplorerRoutes`.
|
|
180
|
-
|
|
181
|
-
## Notifications
|
|
182
|
-
|
|
183
|
-
None beyond the feature itself.
|
|
184
|
-
|
|
185
|
-
## UI
|
|
186
|
-
|
|
187
|
-
None in this IP. example-frontend gains a settings row that registers the Expo push token
|
|
188
|
-
(client helper already exists in `store/utils.ts`) once the Expo adapter lands.
|
|
189
|
-
|
|
190
|
-
## Phases
|
|
191
|
-
|
|
192
|
-
1. **Package + contracts:** `@terreno/comms` package scaffold, `types.ts`, console
|
|
193
|
-
adapters, `CommsMessage` + logging, `commsService`, unit tests.
|
|
194
|
-
2. **CommsApp + routes:** plugin registration, `PushToken` model + routes, admin explorer,
|
|
195
|
-
OpenAPI, supertest coverage.
|
|
196
|
-
3. **Integration:** example-backend registers `CommsApp` (console adapters), seed docs,
|
|
197
|
-
`docs/reference/comms.md`, catalog entries, publish wiring in `publish-on-tag.yml`.
|
|
198
|
-
|
|
199
|
-
## Feature Flags & Migrations
|
|
200
|
-
|
|
201
|
-
None. New collections only; no existing data touched.
|
|
202
|
-
|
|
203
|
-
## Activity Log & User Updates
|
|
204
|
-
|
|
205
|
-
Every send attempt is a `CommsMessage` row (that is the audit surface). No user-facing
|
|
206
|
-
updates.
|
|
207
|
-
|
|
208
|
-
## Not Included / Future Work
|
|
209
|
-
|
|
210
|
-
- Adapter IPs: `comms-adapter-expo-push`, `comms-adapter-twilio-sms`,
|
|
211
|
-
`comms-adapter-twilio-verify`, `comms-adapter-sendgrid`.
|
|
212
|
-
- Notification center, user notification preferences.
|
|
213
|
-
- Queue-backed sending with retries — arrives with `job-queues`; until then sends are
|
|
214
|
-
inline with one retry.
|
|
215
|
-
|
|
216
|
-
## Files to Create / Modify
|
|
217
|
-
|
|
218
|
-
- `comms/` (new workspace package: `package.json`, `tsconfig.json`, `biome.jsonc`, `src/*`
|
|
219
|
-
as in Architecture)
|
|
220
|
-
- Root `package.json` — workspace entry + scripts (`comms:compile|lint|test`)
|
|
221
|
-
- `example-backend/src/server.ts` — register `CommsApp`
|
|
222
|
-
- `docs/reference/comms.md`, `.cursor/rules/comms/00-comms.mdc` (via rulesync)
|
|
223
|
-
- `.github/workflows/publish-on-tag.yml`, new `comms-ci` workflow
|
|
224
|
-
|
|
225
|
-
## Task List
|
|
226
|
-
|
|
227
|
-
See [docs/tasks/comms-abstraction.md](../tasks/comms-abstraction.md).
|
|
228
|
-
|
|
229
|
-
## Acceptance Criteria
|
|
230
|
-
|
|
231
|
-
- [ ] A Terreno app with only console adapters can call `sendMail`, `sendSms`,
|
|
232
|
-
`sendPushToUser`, and `startVerification`/`checkVerification` in development, each
|
|
233
|
-
producing a logger line and a `CommsMessage` row.
|
|
234
|
-
- [ ] `POST /comms/pushTokens` registers a device token for the authenticated user;
|
|
235
|
-
re-posting the same token updates rather than duplicates; other users cannot list or
|
|
236
|
-
delete it.
|
|
237
|
-
- [ ] `GET /comms/messages` is admin-only, paginated, and filterable by channel and status.
|
|
238
|
-
- [ ] Production apps with an unconfigured channel get a 501 `APIError`, not a silent no-op.
|
|
239
|
-
- [ ] `@terreno/comms` core has zero provider SDKs in `dependencies`.
|
|
240
|
-
- [ ] All routes appear in `/openapi.json` and the generated SDK compiles.
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# Implementation Plan: Comms adapter — Expo push notifications
|
|
2
|
-
|
|
3
|
-
**Status:** Draft
|
|
4
|
-
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1019
|
|
5
|
-
**Priority:** High
|
|
6
|
-
**Effort:** Small 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)
|
|
11
|
-
**RTK deprecation flag:** None — backend adapter; client token registration already exists
|
|
12
|
-
|
|
13
|
-
## Goal
|
|
14
|
-
|
|
15
|
-
Implement `PushProvider` on Expo's push service so `sendPushToUser` delivers real
|
|
16
|
-
notifications to iOS/Android devices. `expo-server-sdk` is already a dependency of
|
|
17
|
-
`@terreno/api` (currently unused — it moves to this adapter's peer deps); the client-side
|
|
18
|
-
token fetch (`getExpoPushTokenAsync`) already exists in `example-frontend/store/utils.ts`.
|
|
19
|
-
|
|
20
|
-
## Non-Goals
|
|
21
|
-
|
|
22
|
-
- Direct FCM/APNs integration (future adapter if demand appears).
|
|
23
|
-
- Notification center / inbox UI.
|
|
24
|
-
- Rich notification content (images, actions) beyond title/body/data/badge/sound.
|
|
25
|
-
|
|
26
|
-
## Decisions
|
|
27
|
-
|
|
28
|
-
| Question | Decision |
|
|
29
|
-
|----------|----------|
|
|
30
|
-
| SDK | `expo-server-sdk` as optional peer of `@terreno/comms` (subpath `adapters/expoPush`); removed from `@terreno/api` deps |
|
|
31
|
-
| Receipts | Poll receipts after send (Expo's required pattern); surface as `DeliveryEvent`s to `onDeliveryEvent` |
|
|
32
|
-
| Dead tokens | `DeviceNotRegistered` receipt → `SendResult.accepted: false` with permanent-failure marker so core pruning deactivates the `PushToken` |
|
|
33
|
-
| Chunking | Use the SDK's `chunkPushNotifications`; one `SendResult` per token preserved through chunks |
|
|
34
|
-
|
|
35
|
-
## Architecture
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
// @terreno/comms/adapters/expoPush
|
|
39
|
-
export class ExpoPushProvider implements PushProvider {
|
|
40
|
-
readonly id = "expo";
|
|
41
|
-
constructor(options?: {accessToken?: string; receiptPollDelayMs?: number});
|
|
42
|
-
sendPush(message: PushMessage): Promise<SendResult[]>;
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Send flow: validate tokens with `Expo.isExpoPushToken` → chunk → send → collect tickets →
|
|
47
|
-
schedule one receipt poll (default 15 min, configurable; inline timer until `job-queues`
|
|
48
|
-
lands) → map receipt errors to `DeliveryEvent`s and permanent-failure results.
|
|
49
|
-
|
|
50
|
-
## Models
|
|
51
|
-
|
|
52
|
-
None new — uses `PushToken` and `CommsMessage` from the abstraction.
|
|
53
|
-
|
|
54
|
-
## APIs
|
|
55
|
-
|
|
56
|
-
None new.
|
|
57
|
-
|
|
58
|
-
## Notifications
|
|
59
|
-
|
|
60
|
-
This is the notification plumbing itself.
|
|
61
|
-
|
|
62
|
-
## UI
|
|
63
|
-
|
|
64
|
-
example-frontend: register the push token on login via `POST /comms/pushTokens` (guarded by
|
|
65
|
-
`expo-device` physical-device check once the native baseline lands) and a profile-screen
|
|
66
|
-
test-send row in dev builds.
|
|
67
|
-
|
|
68
|
-
## Phases
|
|
69
|
-
|
|
70
|
-
Single phase.
|
|
71
|
-
|
|
72
|
-
## Feature Flags & Migrations
|
|
73
|
-
|
|
74
|
-
None.
|
|
75
|
-
|
|
76
|
-
## Activity Log & User Updates
|
|
77
|
-
|
|
78
|
-
Sends and receipt failures logged to `CommsMessage`.
|
|
79
|
-
|
|
80
|
-
## Not Included / Future Work
|
|
81
|
-
|
|
82
|
-
- Queue-backed receipt polling (moves to `job-queues` when available).
|
|
83
|
-
- Web push (Expo tokens cover native only; web is a future adapter).
|
|
84
|
-
|
|
85
|
-
## Files to Create / Modify
|
|
86
|
-
|
|
87
|
-
- `comms/src/adapters/expoPush.ts` + tests (mock `Expo` client)
|
|
88
|
-
- `comms/package.json` — optional peer `expo-server-sdk`
|
|
89
|
-
- `api/package.json` — remove unused `expo-server-sdk`
|
|
90
|
-
- `example-frontend/app/(tabs)/profile.tsx`, `store/utils.ts` — token registration wiring
|
|
91
|
-
- `docs/reference/comms.md` — adapter section
|
|
92
|
-
|
|
93
|
-
## Task List
|
|
94
|
-
|
|
95
|
-
See [docs/tasks/comms-adapter-expo-push.md](../tasks/comms-adapter-expo-push.md).
|
|
96
|
-
|
|
97
|
-
## Acceptance Criteria
|
|
98
|
-
|
|
99
|
-
- [ ] With a mocked Expo client, `sendPush` returns one `SendResult` per token across chunk
|
|
100
|
-
boundaries.
|
|
101
|
-
- [ ] A `DeviceNotRegistered` receipt deactivates the corresponding `PushToken` via core
|
|
102
|
-
pruning.
|
|
103
|
-
- [ ] Invalid tokens are rejected before hitting the SDK and logged as failed
|
|
104
|
-
`CommsMessage` rows.
|
|
105
|
-
- [ ] `@terreno/api` no longer lists `expo-server-sdk`; apps not using the adapter install
|
|
106
|
-
nothing new.
|
|
107
|
-
- [ ] example-frontend registers its token after login on a physical device build.
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# Implementation Plan: Comms adapter — transactional email (SendGrid)
|
|
2
|
-
|
|
3
|
-
**Status:** Draft
|
|
4
|
-
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1022
|
|
5
|
-
**Priority:** High
|
|
6
|
-
**Effort:** Small 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); inbound-webhooks (Phase 2 only)
|
|
11
|
-
**RTK deprecation flag:** None — backend only
|
|
12
|
-
|
|
13
|
-
## Goal
|
|
14
|
-
|
|
15
|
-
Implement `MailProvider` on Twilio SendGrid (decision D2 — first mail adapter, sharing the
|
|
16
|
-
Twilio account story with the SMS and Verify adapters) so `sendMail` delivers real
|
|
17
|
-
transactional email. This unblocks `password-reset-and-email-verification` and
|
|
18
|
-
`invitations-and-seats`.
|
|
19
|
-
|
|
20
|
-
## Non-Goals
|
|
21
|
-
|
|
22
|
-
- Other mail providers (Resend/SES/SMTP — items when demand appears).
|
|
23
|
-
- Marketing campaigns, contact lists, SendGrid dynamic template management.
|
|
24
|
-
- Inbound email parsing.
|
|
25
|
-
|
|
26
|
-
## Decisions
|
|
27
|
-
|
|
28
|
-
| Question | Decision |
|
|
29
|
-
|----------|----------|
|
|
30
|
-
| SDK | `@sendgrid/mail` as optional peer, subpath `@terreno/comms/adapters/sendgrid` |
|
|
31
|
-
| Auth | `SENDGRID_API_KEY` env or constructor option; fail fast at registration when missing |
|
|
32
|
-
| From address | Constructor `fromEmail`/`fromName` falling back to `CommsApp.defaultFrom`; sender verification is a documented SendGrid prerequisite, not automated |
|
|
33
|
-
| Templates | Terreno-side rendering (`templates.ts` from the abstraction) sent as `text`/`html`; SendGrid dynamic templates supported pass-through via `templateId` + `dynamicTemplateData` but not required |
|
|
34
|
-
| Sandbox | SendGrid `mail_settings.sandbox_mode` toggle for CI/e2e so tests never send |
|
|
35
|
-
| Delivery events | Phase 2: SendGrid Event Webhook (delivered/bounce/dropped/open) with ECDSA signature verification via the inbound-webhooks framework, mapped to `DeliveryEvent`s |
|
|
36
|
-
|
|
37
|
-
## Architecture
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
// @terreno/comms/adapters/sendgrid
|
|
41
|
-
export class SendGridMailProvider implements MailProvider {
|
|
42
|
-
readonly id = "sendgrid";
|
|
43
|
-
constructor(options?: {
|
|
44
|
-
apiKey?: string; // default env SENDGRID_API_KEY
|
|
45
|
-
fromEmail?: string;
|
|
46
|
-
fromName?: string;
|
|
47
|
-
sandboxMode?: boolean; // default true when NODE_ENV === "test"
|
|
48
|
-
});
|
|
49
|
-
sendMail(message: MailMessage): Promise<SendResult>;
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Error mapping: SendGrid 4xx (bad address, unverified sender) → `accepted: false` with the
|
|
54
|
-
response body's first error message in `SendResult.error` and full body in
|
|
55
|
-
`CommsMessage.metadata`; 429/5xx retried once by the core facade. The provider never
|
|
56
|
-
throws through `sendMail`.
|
|
57
|
-
|
|
58
|
-
## Models / APIs / Notifications / UI
|
|
59
|
-
|
|
60
|
-
None new. Phase 2 adds one webhook route via the inbound-webhooks framework.
|
|
61
|
-
|
|
62
|
-
## Phases
|
|
63
|
-
|
|
64
|
-
1. **Send path:** provider, config resolution, sandbox mode, error mapping, mocked-client
|
|
65
|
-
tests; example-backend env-gated registration; docs (including the sender-verification
|
|
66
|
-
prerequisite checklist).
|
|
67
|
-
2. **Delivery events** *(gated on inbound-webhooks IP)*: Event Webhook route with ECDSA
|
|
68
|
-
signature verification; delivered/bounce/dropped/open → `DeliveryEvent` →
|
|
69
|
-
`CommsMessage.status`.
|
|
70
|
-
|
|
71
|
-
## Feature Flags & Migrations
|
|
72
|
-
|
|
73
|
-
None.
|
|
74
|
-
|
|
75
|
-
## Activity Log & User Updates
|
|
76
|
-
|
|
77
|
-
All sends logged to `CommsMessage` with provider `sendgrid`.
|
|
78
|
-
|
|
79
|
-
## Not Included / Future Work
|
|
80
|
-
|
|
81
|
-
- Suppression-list management, unsubscribe groups.
|
|
82
|
-
- Batch sending beyond the multiple-recipient `to` array.
|
|
83
|
-
|
|
84
|
-
## Files to Create / Modify
|
|
85
|
-
|
|
86
|
-
- `comms/src/adapters/sendgrid.ts` + tests
|
|
87
|
-
- `comms/package.json` — optional peer `@sendgrid/mail`
|
|
88
|
-
- `example-backend/src/server.ts` — env-gated registration
|
|
89
|
-
- `docs/reference/comms.md`, `docs/reference/environment-variables.md`
|
|
90
|
-
|
|
91
|
-
## Task List
|
|
92
|
-
|
|
93
|
-
See [docs/tasks/comms-adapter-sendgrid.md](../tasks/comms-adapter-sendgrid.md).
|
|
94
|
-
|
|
95
|
-
## Acceptance Criteria
|
|
96
|
-
|
|
97
|
-
- [ ] `sendMail` delivers text/html mail with from-address fallback chain (provider option
|
|
98
|
-
→ `CommsApp.defaultFrom`), and passes `templateId`/`dynamicTemplateData` through when
|
|
99
|
-
given.
|
|
100
|
-
- [ ] Sandbox mode is on by default under test and produces accepted results without real
|
|
101
|
-
sends.
|
|
102
|
-
- [ ] SendGrid 4xx errors surface as `accepted: false` with the SendGrid error message;
|
|
103
|
-
nothing throws through the facade.
|
|
104
|
-
- [ ] Missing API key fails fast at `CommsApp` registration with a clear error.
|
|
105
|
-
- [ ] Apps not using the adapter do not install `@sendgrid/mail`.
|
|
106
|
-
- [ ] (Phase 2) Event webhook updates `CommsMessage.status` only for
|
|
107
|
-
signature-verified payloads.
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# Implementation Plan: Comms adapter — Twilio SMS
|
|
2
|
-
|
|
3
|
-
**Status:** Draft
|
|
4
|
-
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1020
|
|
5
|
-
**Priority:** Medium
|
|
6
|
-
**Effort:** Small 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); inbound-webhooks (Phase 2 only)
|
|
11
|
-
**RTK deprecation flag:** None — backend only
|
|
12
|
-
|
|
13
|
-
## Goal
|
|
14
|
-
|
|
15
|
-
Implement `SmsProvider` on Twilio Programmable Messaging so Terreno apps can send
|
|
16
|
-
transactional SMS (OTP fallback, alerts, invitation nudges) through the `@terreno/comms`
|
|
17
|
-
facade with delivery logging.
|
|
18
|
-
|
|
19
|
-
## Non-Goals
|
|
20
|
-
|
|
21
|
-
- OTP verification (that is `comms-adapter-twilio-verify`, on Twilio Verify).
|
|
22
|
-
- Push via Twilio (dropped by decision D5 — Expo push is the push adapter).
|
|
23
|
-
- Bulk/campaign messaging, MMS/RCS.
|
|
24
|
-
- Phone-number provisioning automation.
|
|
25
|
-
|
|
26
|
-
## Decisions
|
|
27
|
-
|
|
28
|
-
| Question | Decision |
|
|
29
|
-
|----------|----------|
|
|
30
|
-
| SDK | `twilio` as optional peer, subpath `@terreno/comms/adapters/twilioSms` |
|
|
31
|
-
| Sender config | Prefer `TWILIO_MESSAGING_SERVICE_SID`; fall back to `TWILIO_FROM_NUMBER`; constructor options override env |
|
|
32
|
-
| Number validation | Validate/normalize to E.164 with `libphonenumber-js` (already in catalog) before hitting the API |
|
|
33
|
-
| Delivery status | Phase 2: status callback endpoint via the inbound-webhooks framework mapping Twilio statuses to `DeliveryEvent`s |
|
|
34
|
-
| Secrets | `TWILIO_ACCOUNT_SID` + `TWILIO_AUTH_TOKEN` via env or the existing secret-provider layer |
|
|
35
|
-
|
|
36
|
-
## Architecture
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
// @terreno/comms/adapters/twilioSms
|
|
40
|
-
export class TwilioSmsProvider implements SmsProvider {
|
|
41
|
-
readonly id = "twilio";
|
|
42
|
-
constructor(options?: {
|
|
43
|
-
accountSid?: string; // default env TWILIO_ACCOUNT_SID
|
|
44
|
-
authToken?: string; // default env TWILIO_AUTH_TOKEN
|
|
45
|
-
messagingServiceSid?: string;
|
|
46
|
-
fromNumber?: string;
|
|
47
|
-
statusCallbackUrl?: string; // Phase 2
|
|
48
|
-
});
|
|
49
|
-
sendSms(message: {to: string; body: string}): Promise<SendResult>;
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Errors map Twilio exception codes to `SendResult.error` with the Twilio error code kept in
|
|
54
|
-
`CommsMessage.metadata` for triage. Invalid destination numbers fail fast with a
|
|
55
|
-
`BadRequestError` before any API call.
|
|
56
|
-
|
|
57
|
-
## Models / APIs / Notifications / UI
|
|
58
|
-
|
|
59
|
-
None new — Phase 2 adds one webhook route via the inbound-webhooks framework.
|
|
60
|
-
|
|
61
|
-
## Phases
|
|
62
|
-
|
|
63
|
-
1. **Send path:** provider, config resolution, E.164 validation, error mapping, tests with
|
|
64
|
-
mocked Twilio client; example-backend env-gated registration; docs.
|
|
65
|
-
2. **Delivery callbacks** *(gated on inbound-webhooks IP)*: status callback route with
|
|
66
|
-
Twilio signature verification, statuses → `DeliveryEvent` → `CommsMessage.status`
|
|
67
|
-
updates.
|
|
68
|
-
|
|
69
|
-
## Feature Flags & Migrations
|
|
70
|
-
|
|
71
|
-
None.
|
|
72
|
-
|
|
73
|
-
## Activity Log & User Updates
|
|
74
|
-
|
|
75
|
-
All sends logged to `CommsMessage` with provider `twilio`.
|
|
76
|
-
|
|
77
|
-
## Not Included / Future Work
|
|
78
|
-
|
|
79
|
-
- Inbound SMS (replies) handling.
|
|
80
|
-
- Alphanumeric sender IDs, international compliance tooling.
|
|
81
|
-
|
|
82
|
-
## Files to Create / Modify
|
|
83
|
-
|
|
84
|
-
- `comms/src/adapters/twilioSms.ts` + tests
|
|
85
|
-
- `comms/package.json` — optional peer `twilio`
|
|
86
|
-
- `example-backend/src/server.ts` — env-gated registration
|
|
87
|
-
- `docs/reference/comms.md`, `docs/reference/environment-variables.md`
|
|
88
|
-
|
|
89
|
-
## Task List
|
|
90
|
-
|
|
91
|
-
See [docs/tasks/comms-adapter-twilio-sms.md](../tasks/comms-adapter-twilio-sms.md).
|
|
92
|
-
|
|
93
|
-
## Acceptance Criteria
|
|
94
|
-
|
|
95
|
-
- [ ] `sendSms` delivers via messaging service or from-number config, preferring the
|
|
96
|
-
messaging service when both are present.
|
|
97
|
-
- [ ] A non-E.164 destination fails with a 400 `APIError` before any Twilio call.
|
|
98
|
-
- [ ] Twilio API errors produce `accepted: false` results with the Twilio error code in
|
|
99
|
-
`CommsMessage.metadata`; nothing throws through the facade.
|
|
100
|
-
- [ ] Apps not using the adapter do not install the `twilio` SDK.
|
|
101
|
-
- [ ] (Phase 2) Status callbacks flip `CommsMessage.status` to `delivered`/`failed` with
|
|
102
|
-
verified Twilio signatures.
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Implementation Plan: Comms adapter — Twilio Verify (OTP)
|
|
2
|
-
|
|
3
|
-
**Status:** Draft
|
|
4
|
-
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1021
|
|
5
|
-
**Priority:** Medium
|
|
6
|
-
**Effort:** Small 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)
|
|
11
|
-
**RTK deprecation flag:** None — backend only
|
|
12
|
-
|
|
13
|
-
## Goal
|
|
14
|
-
|
|
15
|
-
Implement `VerificationProvider` on Twilio Verify so Terreno apps get managed one-time-code
|
|
16
|
-
flows (SMS and email channels) without storing, expiring, or rate-limiting codes
|
|
17
|
-
themselves. This is the delivery channel the future `mfa-step-up-auth` work builds on, and
|
|
18
|
-
it is immediately usable for phone-number verification.
|
|
19
|
-
|
|
20
|
-
## Non-Goals
|
|
21
|
-
|
|
22
|
-
- The MFA enrollment/step-up product itself (`mfa-step-up-auth` item).
|
|
23
|
-
- TOTP (app-authenticator) support — that is code-local (`otpauth`), not a Verify concern.
|
|
24
|
-
- Custom code storage or self-managed rate limiting (Verify owns both).
|
|
25
|
-
|
|
26
|
-
## Decisions
|
|
27
|
-
|
|
28
|
-
| Question | Decision |
|
|
29
|
-
|----------|----------|
|
|
30
|
-
| SDK | `twilio` optional peer (shared with the SMS adapter), subpath `@terreno/comms/adapters/twilioVerify` |
|
|
31
|
-
| Service config | `TWILIO_VERIFY_SERVICE_SID` env or constructor option |
|
|
32
|
-
| Channels | `sms` and `email` (Verify's email channel requires a configured Verify email integration; documented, not automated) |
|
|
33
|
-
| Abuse control | Rely on Verify's built-in rate limits; add Terreno-side per-user attempt caps only when `rate-limiting` IP lands |
|
|
34
|
-
| Logging | Start/check attempts logged to `CommsMessage` with channel `verification`, never logging the code |
|
|
35
|
-
|
|
36
|
-
## Architecture
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
// @terreno/comms/adapters/twilioVerify
|
|
40
|
-
export class TwilioVerifyProvider implements VerificationProvider {
|
|
41
|
-
readonly id = "twilio-verify";
|
|
42
|
-
constructor(options?: {accountSid?: string; authToken?: string; verifyServiceSid?: string});
|
|
43
|
-
startVerification(options: {to: string; channel: "sms" | "email"}): Promise<SendResult>;
|
|
44
|
-
checkVerification(options: {to: string; code: string}): Promise<{valid: boolean}>;
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
`checkVerification` maps Verify's `approved` status to `valid: true`; `pending`, `expired`,
|
|
49
|
-
and max-attempt states return `valid: false` with reason in the result error, letting
|
|
50
|
-
callers throw the appropriate `APIError`.
|
|
51
|
-
|
|
52
|
-
## Models / APIs / Notifications / UI
|
|
53
|
-
|
|
54
|
-
None new. Consumer routes (e.g. `POST /auth/verifyPhone`) arrive with the features that
|
|
55
|
-
need them; this IP ships the provider plus example-backend registration only.
|
|
56
|
-
|
|
57
|
-
## Phases
|
|
58
|
-
|
|
59
|
-
Single phase.
|
|
60
|
-
|
|
61
|
-
## Feature Flags & Migrations
|
|
62
|
-
|
|
63
|
-
None.
|
|
64
|
-
|
|
65
|
-
## Activity Log & User Updates
|
|
66
|
-
|
|
67
|
-
Verification attempts logged to `CommsMessage` (no codes, destination redacted to
|
|
68
|
-
last-4).
|
|
69
|
-
|
|
70
|
-
## Not Included / Future Work
|
|
71
|
-
|
|
72
|
-
- MFA enrollment, recovery codes, biometric step-up (`mfa-step-up-auth`).
|
|
73
|
-
- WhatsApp/voice channels.
|
|
74
|
-
- Android SMS autofill hash configuration (`react-native-otp-verify`, decision D7).
|
|
75
|
-
|
|
76
|
-
## Files to Create / Modify
|
|
77
|
-
|
|
78
|
-
- `comms/src/adapters/twilioVerify.ts` + tests (mocked Twilio client)
|
|
79
|
-
- `example-backend/src/server.ts` — env-gated registration
|
|
80
|
-
- `docs/reference/comms.md`, `docs/reference/environment-variables.md`
|
|
81
|
-
|
|
82
|
-
## Task List
|
|
83
|
-
|
|
84
|
-
See [docs/tasks/comms-adapter-twilio-verify.md](../tasks/comms-adapter-twilio-verify.md).
|
|
85
|
-
|
|
86
|
-
## Acceptance Criteria
|
|
87
|
-
|
|
88
|
-
- [ ] `startVerification` creates a Verify verification on the configured service for sms
|
|
89
|
-
and email channels.
|
|
90
|
-
- [ ] `checkVerification` returns `valid: true` only for `approved`; expired and
|
|
91
|
-
max-attempt states return `valid: false` with a reason.
|
|
92
|
-
- [ ] No OTP code ever appears in logs or `CommsMessage` rows; destinations are redacted.
|
|
93
|
-
- [ ] Missing `TWILIO_VERIFY_SERVICE_SID` fails fast at registration with a clear error.
|