Plataforma SaaS que facilite la transformación de aplicaciones self-hosted en soluciones multi-tenant con integración automática de dispositivos, gestión de sincronización escalable y facturación integrada.
Detectado hace 8 horas
Convierte esta senal en ventaja
Te ayudamos a construirla, validarla y llegar primero.
Pasamos de la idea al plan: quien compra, que MVP lanzar, como validarlo y que medir antes de invertir meses.
Contexto extra
Ver mas sobre la idea
Te contamos que significa realmente la oportunidad, que problema existe hoy, como esta idea lo resolveria y los conceptos clave detras de ella.
Desglose del puntaje
La falta de soporte multiusuario completo y la ausencia de una interfaz de integración y facturación automatizada limitan la escalabilidad y monetización de herramientas SaaS basadas en dispositivos personales.
Desarrolladores y startups que tienen aplicaciones basadas en datos de dispositivos personales y quieren escalar a SaaS multiusuario sin complejidad operativa.
"This issue documents the full architectural and product analysis for turning Mr. Bridge from a single-user self-hosted tool into a multi-tenant SaaS application"
feat: architecture and product plan for multi-tenant SaaS launch
Publicado: hace 8 horas
Repository: Theioz/mr-bridge-assistant Author: Theioz ## Summary This issue documents the full architectural and product analysis for turning Mr. Bridge from a single-user self-hosted tool into a multi-tenant SaaS application — where users sign up, connect their own devices, and interact with their data through Mr. Bridge without ever touching a terminal, environment variable, or Supabase dashboard. This is a planning and decision issue. Each section below frames the decision space, analyzes options, and lands on a recommendation. Concrete implementation issues will be filed for each pillar once decisions are ratified. --- ## Current architecture snapshot (honest assessment) | Layer | What we have | Multi-tenant ready? | |---|---|---| | Database | Supabase Postgres, RLS on all tables, `user_id` on every row | ✅ Already multi-tenant | | Auth | Supabase Auth (email + Google OAuth) | ✅ Works for N users | | Web app | Next.js App Router on Vercel | ✅ Stateless, scales automatically | | Per-user fitness tokens | `fitbit_refresh_token`, `oura_token` stored in `profile` table per user | ✅ Already per-user | | App-level OAuth credentials | `FITBIT_CLIENT_ID/SECRET`, `GOOGLE_CLIENT_ID/SECRET` as env vars | ✅ These are app-level — same for all users, correct pattern | | Sync cron job | `OWNER_USER_ID` hardcoded — runs sync for one user only | ❌ Single-user assumption | | Python scripts | `scripts/` — machine-local, reference `OWNER_USER_ID` | ❌ Not deployable, not multi-user | | AI usage | No limits, no tracking, no per-user quotas | ❌ Would be bankrupt at scale | | Integration setup | Manual: CLI scripts, env vars, Supabase dashboard | ❌ No user can self-serve this | | Apple Health | Not implemented | ❌ Missing the dominant iOS platform | | Billing | None | ❌ No monetization | **The good news**: Supabase RLS is already the correct foundation — every query is already isolated by `user_id`. The codebase is about 60% of the way to multi-tenant. The remaining 40% is cron multi-user support, integration onboarding UI, cost controls, and Apple Health. --- ## Decision 1 — Infrastructure: keep current stack vs. containerize vs. rewrite ### Option A — Keep Vercel + Supabase, move Python sync to a TypeScript background worker (recommended) Rewrite the Python sync scripts in TypeScript. Deploy them as a separate Node.js worker service on Railway or Render. The worker polls a `sync_queue` table in Supabase for pending sync jobs and processes them. The Vercel cron triggers enqueueing for all users; the worker does the actual API calls. **Pros:** - Vercel + Supabase already works — no migration risk - TypeScript unifies the entire codebase (no Python maintenance tax going forward) - Railway/Render worker costs ~$5–7/month for a persistent Node.js process - Supabase scales to thousands of concurrent users on Pro plan ($25/month) - Vercel scales automatically — no ops work - The existing sync TypeScript modules (`web/src/lib/sync/fitbit.ts`, `oura.ts`, `googlefit.ts`) are already written and working — the worker just calls them outside of Vercel's 15-second function timeout **Cons:** - Railway/Render adds one more service to manage - Long sync jobs (backfill years of Fitbit data) need timeout handling - Cold starts on the worker if using serverless-style deployment ### Option B — Full containerization (Docker + Kubernetes / AWS ECS) Package the entire application in Docker containers, run on ECS, GKE, or self-managed Kubernetes. **Pros:** - Full control over scaling, resource allocation, and deployment - Can run long-running processes without timeout constraints **Cons:** - Massively premature — adds months of DevOps work before shipping a single user - Requires maintaining infrastructure that Vercel and Supabase already handle for free - Kubernetes operational complexity is a full-time job at this stage - No meaningful benefit over Vercel + Railway until the app has hundreds of thousands of users ### Option C — Rewrite backend in Go Replace Python scripts and potentially the Next.js API routes with a Go backend. **Pros:** - Go is fast, easy to deploy as a single binary, excellent for background workers - Lower memory footprint than Node.js **Cons:** - Complete rewrite — months of work, zero new features shipped - The current TypeScript codebase is clean and performant enough for this scale - Anthropic SDK, Supabase SDK, and health API clients all have first-class TypeScript support; Go equivalents are community-maintained **Recommendation: Option A.** The stack is correct. The only infrastructure gap is the Python sync scripts — rewrite them in TypeScript and run on Railway. Everything else scales as-is. --- ## Decision 2 — Authentication and data isolation ### Current state Supabase Auth handles login. RLS policies enforce that every Supabase query only returns rows where `auth.uid() = user_id`. The `createServiceClient()` bypasses RLS and is correctly limited to system operations (sync, cron, admin). This is already correct multi-tenant architecture. ### What needs to change 1. **Apple Sign In** — required for App Store apps; strongly preferred by iOS users 2. **Email verification** — currently optional in Supabase; must be enforced for production 3. **Data deletion** — users must be able to delete their account and all associated data (GDPR Article 17, CCPA) 4. **Session security** — Supabase handles JWTs already; review token expiry settings 5. **Admin access controls** — an admin panel to manage users, disable accounts, view aggregate (non-PII) usage stats ### Option A — Supabase Auth only, add Apple + social providers (recommended) Stay on Supabase Auth. Add Apple Sign In and extend Google OAuth. Implement a `/api/account/delete` route that cascades delete all user data. Use Supabase's built-in email verification. **Pros:** - Zero migration — add providers via Supabase dashboard - Apple Sign In is a native Supabase Auth provider - Data deletion is a single `DELETE FROM auth.users WHERE id = $uid` which cascades via `ON DELETE CASCADE` on all tables (already configured) - Supabase handles token rotation, session storage, PKCE **Cons:** - Supabase Auth has limits on the free plan (50k MAU); Pro plan is needed at scale (unlimited MAU, $25/mo) ### Option B — Replace Supabase Auth with Clerk or Auth0 Migrate authentication to a dedicated auth provider. **Pros:** - Clerk has excellent UI components, organization support, audit logs - Auth0 has enterprise SSO features **Cons:** - Migration of existing sessions is complex - Adds another vendor and monthly cost (~$25+/month) - Supabase Auth already covers all required features for this use case **Recommendation: Option A.** Supabase Auth is sufficient. Add Apple Sign In, enforce email verification, implement data deletion. No migration needed. ### Data isolation audit (required before launch) - Audit every use of `createServiceClient()` — must only be used for system operations, never for returning user-specific data to the frontend - Remove all `OWNER_USER_ID` references from query logic; every query must use `auth.uid()` from the authenticated request - Add a `deleted_at` soft-delete to `auth.users` via a database function — gives a 30-day grace period before hard deletion --- ## Decision 3 — Integration onboarding UX (replacing env vars) ### Current state Connecting Fitbit requires: registering a Fitbit developer app, setting 4 env vars, running a Python OAuth setup script, then running sync manually. No normal user can do this. ### What the onboarding flow needs to be A **Connections** page in Settings (or a dedicated `/connect` route) with integration cards — similar to how Notion, Zapier, or Strava handle third-party connections. ``` ┌─────────────────────────────────────────┐ │ Connected Devices │ ├─────────────────────────────────────────┤ │ 🟢 Fitbit Connected [Sync] [Disconnect] │ │ 🔴 Apple Health Not connected [Connect] │ │ 🔴 Oura Ring Not connected [Connect] │ │ 🟡 Google Fit Connected [Sync] [Disconnect] │ │ 🔴 Garmin Not connected [Connect] │ ├─────────────────────────────────────────┤ │ 📧 Gmail Connected │ │ 📅 Google Calendar Connected │ └─────────────────────────────────────────┘ ``` ### Option A — Build per-provider OAuth flows ourselves (recommended for initial launch) For each provider (Fitbit, Oura, Google Fit), implement a standard OAuth 2.0 authorization code flow: 1. User clicks "Connect Fitbit" 2. Redirected to Fitbit's auth page with our app's `client_id` 3. Fitbit redirects back to `/api/connect/fitbit/callback` with `?code=...` 4. Exchange code for tokens, encrypt and store in `user_integrations` table per user 5. Show "Connected ✓" The app registers ONE Fitbit developer app, ONE Google app — all users authenticate through it. This is standard SaaS OAuth. New `user_integrations` table: ```sql create table user_integrations ( id uuid primary key default gen_random_uuid(), user_id uuid not null references auth.users(id) on delete cascade, provider text not null, -- 'fitbit', 'oura', 'google_fit', 'google_calendar', 'gmail' access_token text, -- encrypted at rest (Supabase Vault or pgcrypto) refresh_token text, -- encrypted at rest expires_at timestamptz, scope text, connected_at timestamptz default now(), unique (user_id, provider) ); ``` **Pros:** - Full control over the OAuth flow and token storage - No additional vendor cost or dependency - Fitbit, Oura, Google APIs are well-documented with standard OAuth 2.0 - The TypeScript sync modules already handle token refresh correctly **Cons:** - Each provider requires a separate developer app registration - Must maintain OAuth apps with each provider (review policies, scope changes) - Apple Health requires a native app — cannot be done via web OAuth (see Decision 4) ### Option B — Use a health data aggregator (Terra API, Vital, Sahha) Register with Terra or Vital — they provide a single SDK/API that aggregates Fitbit, Oura, Apple Health, Garmin, Whoop, and more via their own OAuth infrastructure. **Pros:** - Single integration handles 10+ providers including Apple Health (via Terra's iOS SDK) - Webhook-based: Terra pushes data to our endpoint when it's available — eliminates cron/sync entirely - Handles token refresh, provider API changes, and data normalization **Cons:** - Terra costs: ~$0.10–0.25 per connected user per month (adds up at scale) - Data passes through a third party — privacy/compliance consideration for health data - Less control over data granularity and sync timing - Vendor lock-in: if Terra raises prices or shuts down, migration is expensive **Recommendation: Option A for launch (Fitbit, Oura, Google Fit), Option B as a future accelerator for Apple Health and Garmin.** Build the OAuth flows ourselves for the three providers we already support — the code is already written. Add Terra specifically for Apple Health and Garmin where web OAuth is impossible. --- ## Decision 4 — Apple Health Apple Health is the dominant fitness platform for US iPhone users. It captures data from Apple Watch, third-party apps (Strava, MyFitnessPal, etc.), and manual entries. It is the single biggest missing integration. ### The constraint Apple Health (HealthKit) is only accessible via native iOS code. There is no web API. A pure web app cannot read HealthKit data. ### Option A — Manual XML export (no native app, available at launch) Apple Health allows users to export all their data as an XML file (`export.xml`). We build an import page that accepts this file, parses it, and loads historical data. **Pros:** - Available immediately, no native app needed - Captures complete historical data - Zero ongoing infrastructure cost **Cons:** - Not automatic — user must manually re-export and re-import to refresh data - Export file can be hundreds of MB for long-time Apple Watch users — needs streaming parser - Not real-time — can't get today's steps without another manual export ### Option B — PWA + background sync via Shortcuts Build a Progressive Web App. Use Apple Shortcuts automation to read HealthKit data and POST it to our `/api/sync/apple-health` endpoint on a schedule (e.g., every hour). **Pros:** - No App Store review needed - Can automate sync without a native app - Shortcuts can read steps, heart rate, sleep, workouts from HealthKit **Cons:** - Requires users to set up an Automations shortcut — not zero-friction - Limited to what Shortcuts can export (no raw HRV, no detailed sleep stages) - Runs only when the shortcut triggers (not truly real-time) ### Option C — Native iOS companion app (React Native / Expo) Build a lightweight React Native app whose sole job is reading HealthKit and syncing to Supabase. The main UI stays on the web. **Pros:** - Full HealthKit access: steps, heart rate, HRV, sleep stages, workouts, body weight, blood oxygen - Background sync — can post data every 15–60 minutes automatically - Apple Watch complications possible - Unlocks Apple Sign In (required for App Store apps) **Cons:** - App Store review process (1–2 weeks for initial review, faster for updates) - React Native / Expo adds a second codebase to maintain - Apple Developer Program: $99/year - HealthKit entitlement requires App Store submission (no TestFlight distribution without review for HealthKit) ### Option D — Terra API iOS SDK Integrate Terra's iOS SDK in a native companion app. Terra handles HealthKit permissions, normalization, and webhook delivery to our backend. **Pros:** - Fastest path to comprehensive HealthKit data (Terra's SDK handles the complexity) - Same backend integration as Decision 3 Option B — one webhook endpoint for all providers **Cons:** - Still requires a native app for the HealthKit entitlement - Terra cost per connected user (see Decision 3) **Recommendation:** - **Launch**: Option A (XML import) — ships immediately, captures historical data, no App Store dependency - **Phase 2**: Option B (Shortcuts automation) — bridges the gap for daily sync without an app - **Phase 3**: Option C (React Native companion) — full HealthKit access, required for serious fitness tracking users. This is the right long-term answer; build it once the web product is validated. --- ## Decision 5 — AI cost management and usage limits ### Current state No limits. Every user can make unlimited requests. At ~$2/day for one user, 100 users = $200/day = $6,000/month in Anthropic bills alone. ### Cost drivers (ranked, from current analysis) 1. `maxSteps: 12` — up to 12 tool call round-trips per message 2. System prompt size — loads full profile, recent meals, habits on every request 3. No `maxTokens` cap on responses 4. Context window: last 20 messages sent on every request 5. Model routing: Haiku handles simple queries but complex ones always hit Sonnet ### Option A — Token budget per user per month with subscription tiers (recommended) Track token usage in a `token_usage` table (input tokens, output tokens, model, timestamp per request). Enforce monthly limits by tier. Use Stripe for subscription management. ``` Free tier: 50,000 tokens/month (~$0.75 at Sonnet pricing) — enough for ~25 conversations Pro tier: 500,000 tokens/month (~$7.50) — $9.99/month subscription Unlimited: No cap — $24.99/month ``` **Pros:** - Direct relationship between cost and revenue - Stripe is standard, well-documented, has Next.js examples - Free tier drives signups; Pro converts power users - Token counting is exact — Anthropic returns usage in every API response **Cons:** - Users hate token limits — need clear UI showing remaining budget - Must handle gracefully: show warning at 80%, soft limit at 100% (no hard cutoff mid-conversation) - Stripe integration is a week of work (checkout, webhooks, subscription management) ### Option B — Request-based limits (X messages per day) Simpler than token counting — limit total messages sent per day/month. **Pros:** - Easy to understand for users ("10 messages/day free") - Simple to implement (count rows in `chat_messages`) **Cons:** - "Message" is a bad unit — a 3-word question costs the same as a detailed analysis request - Power users will game it by cramming everything into one long message - Doesn't map to actual costs ### Option C — Flat monthly subscription, no per-request limits Charge a flat fee ($9.99–19.99/month), don't meter usage. Absorb cost variance. **Pros:** - Simplest UX — no budget anxiety - No infrastructure for metering **Cons:** - High-usage users could cost $50+/month to serve while paying $10 - Financially risky without usage data to model cost distribution **Recommendation: Option A (token budget + Stripe tiers).** Token metering is the honest unit of cost. The implementation is: (1) read `usage` from every Anthropic API response and insert into `token_usage`, (2) check budget at the start of each chat request and return 402 if exhausted, (3) show a usage indicator in the Chat UI. Stripe for billing. Also implement the cost-optimization changes from issue #189 (maxSteps, maxTokens, context window) before launch — these reduce per-user costs by ~60%, widening the margin on every tier. --- ## Decision 6 — QoS, rate limiting, and abuse prevention ### What's needed - **Per-user rate limiting**: max N requests per minute to prevent runaway loops or abuse - **Global circuit breaker**: if Anthropic API is down, return a clear error rather than hanging - **Sync throttling**: don't run all users' fitness syncs simultaneously — stagger across the hour - **Webhook verification**: all incoming webhooks (Fitbit, Terra, Stripe) must verify HMAC signatures ### Option A — Upstash Redis for rate limiting (recommended) Use Upstash (serverless Redis) for sliding-window rate limiting at the Next.js middleware layer. Free tier: 10,000 requests/day. Pro: $0.2 per 100K requests. **Pros:** - Vercel-native: Upstash has a first-class `@upstash/ratelimit` library designed for Vercel Edge - Sliding window prevents burst abuse - Sub-millisecond latency — doesn't add perceptible delay - Can also use for caching expensive queries (profile, briefing data) **Cons:** - Another service dependency - Free tier may not cover high-volume users ### Option B — Supabase-based rate limiting (count recent requests in DB) Count recent `chat_messages` rows per user as a proxy for rate limiting. **Pros:** - No new service **Cons:** - DB query on every request adds latency - Supabase connection pool pressure **Recommendation: Option A (Upstash).** The `@upstash/ratelimit` + Vercel Edge combination is the standard pattern. Add it to the chat route: 20 requests/minute per user on Free, 60/minute on Pro. --- ## Decision 7 — Compliance and privacy Health data carries higher sensitivity than typical SaaS data. Key considerations: ### HIPAA Mr. Bridge is **not** a covered entity under HIPAA (it's not a healthcare provider, insurer, or clearinghouse). The fitness tracking use case (steps, sleep, weight) is personal wellness, not medical records. **HIPAA does not apply.** However, marketing language should be careful not to position Mr. Bridge as a medical device or health management tool. ### GDPR / CCPA Applies if users are in the EU or California. Required: - Privacy policy and terms of service - Cookie consent banner (minimal — only Supabase auth cookies) - **Right to deletion**: `/api/account/delete` that removes all user data - **Data export**: `/api/account/export` that returns a JSON dump of all the user's data - Data processing agreement with Supabase and Anthropic (both have DPAs available) ### Token/health data encryption - OAuth tokens (Fitbit refresh tokens, Oura tokens) are currently stored in plaintext in the `profile` table. Before launch: encrypt these at rest using Supabase Vault or `pgcrypto`. - Anthropic does not train on API data by default — no action needed, but document this in the privacy policy. --- ## Phased launch roadmap ### Phase 1 — Foundation (pre-launch, ~4–6 weeks) Issues to file and implement: 1. **Multi-user cron sync**: Update `/api/cron/sync` to loop over all users with connected integrations, not just `OWNER_USER_ID` 2. **OAuth connections UI**: `user_integrations` table + `/connect` page with Fitbit, Oura, Google Fit OAuth flows 3. **Remove OWNER_USER_ID**: Replace all single-user assumptions with authenticated user context 4. **Token usage tracking + limits**: Insert to `token_usage` on every chat request; enforce monthly budget 5. **Stripe integration**: Subscription tiers, checkout flow, webhook handler 6. **Data deletion + export**: `/api/account/delete` and `/api/account/export` 7. **Token encryption**: Encrypt OAuth tokens at rest in `user_integrations` table ### Phase 2 — Apple Health + mobile (post-launch, ~6–8 weeks) 8. **Apple Health XML import**: Upload and parse `export.xml`, load into `fitness_log` and `recovery_metrics` 9. **Apple Health Shortcuts bridge**: `/api/sync/apple-health` endpoint + Shortcuts template for users 10. **PWA manifest**: installable web app, home screen icon, splash screen ### Phase 3 — Native app + growth (~3 months post-launch) 11. **React Native companion app**: HealthKit sync, push notifications, App Store submission 12. **Garmin / Whoop via Terra**: Expand device support via Terra webhooks 13. **Team/family plans**: Shared household data, multiple profiles under one subscription --- ## Acceptance criteria (for this planning issue) - [ ] All 7 decisions above are reviewed and ratified (or amended) by the user - [ ] Phase 1 issues are filed with full implementation prompts - [ ] `user_integrations` table schema is finalized and approved - [ ] Stripe pricing tiers are decided (Free / Pro / Unlimited amounts) - [ ] Apple Health launch strategy is decided (XML import only vs. Shortcuts bridge at launch) - [ ] Privacy policy scope is defined (GDPR compliance level, data residency) ## Related - #189 (API cost optimization) — must ship before launch to reduce per-user cost basis - #188 (voice mode) — Phase 1 feature - #192 (workout program) — Phase 1 feature