merge the sms-service spec docs

This commit is contained in:
2026-04-12 20:16:58 -06:00
parent dccf7fd868
commit c269f626fb
2 changed files with 106 additions and 39 deletions

View File

@@ -0,0 +1,105 @@
# SMS Notification Service — Cross-Repo Project
You have full read-write access to everything under this project directory.
## Repos
- **sms-service** — New Deno/Hono service (the primary implementation target)
- **endorser-ch** — Reference for API patterns, auth middleware, DB conventions. Minimal changes expected (no new endpoints needed for Phase 1).
- **crowd-funder-for-time-pwa** — Time Safari app (Vue/Capacitor). UI integration for phone registration and notification settings.
- **README-gift-economies** — Project docs. Full spec at `progress/tech/PROJECT-sms-notifications.md`.
## Full Spec
Read `README-gift-economies/progress/tech/PROJECT-sms-notifications.md` for the complete design. Key decisions already made:
- **Auth for user requests**: Forward caller's JWT to endorser-ch `GET /api/v2/report/rateLimits` to validate and extract DID. No DID resolver needed in the SMS service.
- **Auth for scheduled endorser-ch calls**: User generates a long-lived JWT; the app sends it to the SMS service when notifications are enabled. The SMS service stores it and uses it for `alertSearch` calls.
- **Long-lived JWT lifecycle**: Not sent at registration. Sent when notifications are enabled. App refreshes it (e.g., every 90 days) by checking expiry on settings screen open.
- **Verification**: 6-digit code, 15-minute expiry, new code overrides old, max 3 codes/hour with 1-hour cooldown after limit. Phone must be verified before any notification can be enabled.
- **Database**: SQLite (consider SQLCipher for encryption at rest).
- **SMS provider**: Twilio, behind an interface for swappability.
- **UI**: Side-by-side notification channels — group by notification type (daily reminder, activity digest), show on-device and SMS as peer delivery channel rows.
## Implementation Plan — Phase 1: Core Infrastructure
### Step 1: sms-service project scaffolding
- [x] Initialize Deno + TypeScript project in `sms-service/`
- [x] Set up `src/`, `test/`, `sql/` directory structure
- [x] Add dependencies: Hono, @db/sqlite, twilio, node-cron
- [x] Add env config loading (--env-file): `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_PHONE_NUMBER`, `ENDORSER_API_URL`, `SMS_DB_PATH`
- [x] Set up deno.json tasks (start, dev, test, check)
### Step 2: Database schema and migrations
- [x] Create `sql/` migration files for:
- [x] `sms_user` table (phoneNumber, issuerDid, pendingDid, storedJwt, jwtExpiresAt, verified, verificationCode, verificationExpiry, verificationAttempts, codesRequestedThisHour, cooldownUntil, timestamps)
- [x] `sms_notification_pref` table (userId, notificationType, enabled, sendTimeUtc, timezoneOffset, reminderMessage, alertSearchParams, timestamps)
- [x] `sms_send_log` table (userId, notificationType, sentAt, status, providerMessageId, errorMessage)
- [x] DB initialization on service startup with auto-migration runner
### Step 3: JWT-forwarding auth middleware
- [x] Middleware that extracts `Authorization: Bearer <JWT>` from incoming requests
- [x] Forwards to endorser-ch `GET /api/v2/report/rateLimits` with the same Authorization header
- [x] Extracts and caches (60s by JWT hash) the caller's DID
- [x] Sets `c.set("issuerDid")` for downstream handlers
- [x] Returns 401 on endorser-ch rejection
### Step 4: Phone registration and verification endpoints
- [x] `POST /api/sms/register` — validate E.164 US-only, rate-limit checks (cooldown, codes-per-hour), generate 6-digit code, hash with PBKDF2, store, send via Twilio
- [x] `POST /api/sms/verify` — check attempts (max 5), check expiry (15 min), compare code, set verified=true. Handle DID reassignment (pendingDid flow).
- [x] `DELETE /api/sms/register` — remove phone, all prefs, clear stored JWT
### Step 5: Stored JWT management
- [x] `PUT /api/sms/jwt` — accepts a long-lived JWT from the app, stores it with its expiry for the authenticated user
- [x] Validate the JWT is valid by forwarding to endorser-ch before storing
- [ ] Return JWT expiry status in `GET /api/sms/preferences` response
### Step 6: Twilio SMS integration
- [x] Wrap Twilio behind a provider interface (`SmsProvider` with `sendSms(to, body)`)
- [x] Implement TwilioProvider using the Twilio SDK (lazy-loaded on first send)
- [x] Add a mock/log provider for development and testing
- [x] Log all sends to `sms_send_log`
## Implementation Plan — Phase 2: Notification Preferences & Scheduler
### Step 7: Preference endpoints
- [ ] `GET /api/sms/preferences` — return all prefs for authenticated user, plus JWT expiry status and phone verification status
- [ ] `PUT /api/sms/preferences` — update prefs. Reject enabling if phone not verified. Reject enabling `alert_search` if no valid stored JWT.
- [ ] `GET /api/sms/history` — return recent send log entries
### Step 8: Periodic scheduler
- [ ] node-cron job running every 15 minutes
- [ ] Query enabled prefs where sendTimeUtc falls in current 15-min window
- [ ] For `reminder`: send the user's custom message
- [ ] For `alert_search`: call endorser-ch `alertSearch` using stored JWT, summarize results, send SMS
- [ ] Idempotency: track last send time per pref to avoid duplicates
- [ ] Skip users with expired JWTs for alert_search (reminder still works)
## Implementation Plan — Phase 3: App Integration
### Step 9: Time Safari notification settings UI
- [ ] Find existing notification settings in crowd-funder-for-time-pwa
- [ ] Add phone number registration/verification UI (inline in settings)
- [ ] Refactor notification settings to show side-by-side channels per notification type
- [ ] Pre-fill SMS settings from on-device settings as defaults
- [ ] Silent JWT refresh on settings screen open
## Build & Run
```bash
# sms-service (Deno)
cd sms-service && deno task dev
# endorser-ch (reference only, run for testing auth forwarding)
cd endorser-ch && pkgx npm install && pkgx npm start
# crowd-funder (for UI work)
cd crowd-funder-for-time-pwa && pkgx npm install && pkgx npm run serve
```
## Conventions
- sms-service uses Deno with Hono (jsr:@hono/hono), SQLite (jsr:@db/sqlite), and TypeScript natively
- Follow endorser-ch patterns for route structure, error handling, and DB access
- Use TypeScript throughout
- Use `pkgx` prefix for node/npm/npx commands (endorser-ch, crowd-funder). sms-service uses `deno task` directly.

View File

@@ -245,45 +245,7 @@ Minimal changes to the endorser-ch server:
## Implementation Phases
### Phase 1: Core Infrastructure
- [ ] Set up new Node.js service with SQLite database
- [ ] Implement JWT-forwarding auth middleware (validate JWTs via endorser-ch `rateLimits` endpoint)
- [ ] Implement phone registration and verification endpoints (including DID reassignment flow)
- [ ] Implement stored JWT management (store user-issued JWTs, track expiry, refresh endpoint)
- [ ] Integrate Twilio SDK for sending SMS
### Phase 2: Notification Preferences & Scheduler
- [ ] Implement preference storage and API endpoints
- [ ] Build the periodic scheduler (15-minute interval)
- [ ] Implement `reminder` notification type (custom message send)
- [ ] Implement `alert_search` notification type (fetch + summarize + send)
### Phase 3: App Integration
- [ ] Add phone number registration UI to Time Safari settings
- [ ] Add notification preference controls
- [ ] Add send history view
### Phase 4: Hardening
- [ ] Delivery status webhooks from Twilio (track delivered vs. failed)
- [ ] Retry logic for failed sends
- [ ] Admin dashboard for monitoring send rates and failures
- [ ] Opt-out via SMS reply (STOP keyword handling)
- [ ] Upgrade to service DID delegation auth (replacing stored user JWTs)
### Phase 5: Paid Tier -- Higher Limits & International SMS
- [ ] Integrate a payment mechanism (e.g., Stripe, or crypto/gift-economy credit)
- [ ] Add `sms_user` columns for payment status and tier (`free` / `paid`)
- [ ] Enforce US-only phone numbers for free tier; allow international numbers for paid tier
- [ ] Raise daily rate limits for paid users (e.g., free: 2 messages/day, paid: 10 messages/day)
- [ ] Add payment status checks to the send path and preference validation
- [ ] Time Safari UI for payment/upgrade flow
### Phase 6: Event-Triggered SMS from Partner Functions
- [ ] Add `POST /api/sms/send` endpoint to SMS service (service-to-service auth)
- [ ] Add per-event-type opt-in preferences (profile messages, meeting invites, etc.)
- [ ] Integrate first trigger in endorser-ch (e.g., profile messaging)
- [ ] Add event-type rate limiting per recipient
- [ ] Extend Time Safari settings UI for event-type opt-in/opt-out
See [project checklit](./PROJECT-sms-notifications-checklist.md)
## Future Requirement: Event-Triggered SMS from Partner Functions