Files
fosterflow/docs/superpowers/specs/2026-03-26-fosterflow-saas-design.md
justinandClaude Opus 4.6 d192062793 docs: add tiered pricing model to design spec
Three paid tiers: Manage ($12/mo, admin-only + embeddable widgets),
Cloud ($25/mo, full hosted site), API add-on (+$5/mo). Self-hosted
remains free. Updated revenue projections and billing products.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 13:09:38 -05:00

547 lines
22 KiB
Markdown

# FosterFlow SaaS Platform — Design Spec
**Date:** 2026-03-26
**Domain:** fosterflow.app
**Status:** Approved
---
## Overview
FosterFlow is a multi-tenant SaaS platform for animal rescues. It provides a complete website + management system — everything a foster-based rescue needs, nothing it doesn't. Clean room rebuild using the AHCR (Almost Home Canine Rescue) codebase as reference.
**Two deployment modes:**
- **Self-hosted** — free, open source (MIT), rescue runs the tenant app themselves via Docker
- **FosterFlow Cloud** — $19/mo managed hosting with automated provisioning, subdomain, SSL, backups, updates
**Tech stack:** SvelteKit 2, Svelte 5 (runes), Tailwind CSS 4, Drizzle ORM, MariaDB, Stripe, Nodemailer, Caddy, Ollama (AI features)
---
## 1. Overall Architecture
```
┌─────────────────────────────────────────────────────┐
│ Caddy Proxy │
│ fosterflow.app → platform app (marketing, │
│ signup, operator dashboard) │
│ *.fosterflow.app → tenant app (by subdomain) │
│ custom domains → tenant app (by domain) │
└──────────────┬──────────────────────┬───────────────┘
│ │
┌──────────▼──────────┐ ┌────────▼────────────┐
│ Platform App │ │ Tenant Instance(s) │
│ (SvelteKit) │ │ (SvelteKit) │
│ │ │ │
│ / landing│ │ / public site │
│ /signup form │ │ /admin dashboard │
│ /operator mgmt │ │ /foster portal │
│ /api hooks │ │ /api endpoints │
└──────────┬──────────┘ └────────┬────────────┘
│ │
┌──────────▼──────────┐ ┌────────▼────────────┐
│ fosterflow_platform│ │ ff_{slug} (per DB) │
│ (tenants, billing, │ │ (pets, users, apps, │
│ signups, events) │ │ donations, etc.) │
└─────────────────────┘ └─────────────────────┘
│ │
└──────────┬───────────┘
MariaDB Server
```
**Two separate apps, two separate repos:**
- **`fosterflow`** (public, GitHub) — the tenant app. Full rescue site + admin + foster portal. This is what self-hosted users clone. MIT licensed.
- **`fosterflow-cloud`** (private) — the platform app. Operator dashboard, signup/approval, provisioning engine, billing. Only runs on FosterFlow infrastructure.
The tenant app has zero knowledge of multi-tenancy. It's just "a rescue site." The platform app orchestrates instances of it.
**Shared Ollama instance** — platform-level AI service all tenants call for the dog name generator (free perk).
---
## 2. Data Model
### Platform Database (`fosterflow_platform`)
```sql
-- Signup applications (pre-approval)
CREATE TABLE signups (
id INT PRIMARY KEY AUTO_INCREMENT,
org_name VARCHAR(255) NOT NULL,
admin_name VARCHAR(255) NOT NULL,
admin_email VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
slug VARCHAR(100) NOT NULL UNIQUE,
website_url VARCHAR(255),
ein VARCHAR(20),
description TEXT,
city VARCHAR(100),
state VARCHAR(100),
status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
reviewed_by INT,
reviewed_at TIMESTAMP,
rejection_reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Active tenants (post-approval)
CREATE TABLE tenants (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
slug VARCHAR(100) NOT NULL UNIQUE,
subdomain VARCHAR(100) NOT NULL UNIQUE,
custom_domain VARCHAR(255),
db_name VARCHAR(100) NOT NULL,
port INT NOT NULL,
admin_email VARCHAR(255) NOT NULL,
admin_name VARCHAR(255) NOT NULL,
plan ENUM('trial', 'manage', 'cloud', 'self_hosted') DEFAULT 'trial',
stripe_customer_id VARCHAR(255),
stripe_subscription_id VARCHAR(255),
status ENUM('provisioning', 'active', 'suspended', 'deleted') DEFAULT 'provisioning',
trial_ends_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Audit trail for tenant lifecycle
CREATE TABLE tenant_events (
id INT PRIMARY KEY AUTO_INCREMENT,
tenant_id INT NOT NULL REFERENCES tenants(id),
event VARCHAR(50) NOT NULL,
details JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Operator users
CREATE TABLE operators (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
### Tenant Database (`ff_{slug}`)
| Group | Tables |
|-------|--------|
| **Auth** | `users`, `sessions` |
| **Pets** | `pets`, `pet_photos`, `pet_medical`, `foster_assignments`, `pet_mentors` |
| **Applications** | `applications`, `application_homevisit_notes` |
| **People** | `vets`, `mentors`, `sponsors`, `intake_sources` |
| **Finance** | `donations`, `expenses` |
| **Content** | `events`, `happy_tails`, `content_blocks`, `newsletter_subscribers`, `contact_messages` |
| **Foster** | `foster_submissions`, `supply_requests` |
| **Config** | `site_settings`, `org_config` |
| **System** | `audit_log`, `page_views`, `ai_jobs` |
**`org_config` table** — key/value store with JSON values:
```sql
CREATE TABLE org_config (
id INT PRIMARY KEY AUTO_INCREMENT,
`key` VARCHAR(100) NOT NULL UNIQUE,
value JSON NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Example rows:
-- ('branding', '{"primaryColor":"#0d9488","accentColor":"#f59e0b","logoUrl":"/uploads/logo.png"}')
-- ('org_info', '{"name":"Almost Home","city":"Sioux Falls","state":"SD","phone":"..."}')
-- ('locale', '{"language":"en","currency":"USD","dateFormat":"MM/DD/YYYY"}')
-- ('applications', '{"adoptionEnabled":true,"fosterEnabled":true,"adoptionFee":250}')
-- ('agreements', '{"adoption":[...],"foster":[...]}')
```
**Changes from AHCR:**
- Added `org_config` table — drives theming, org details, locale, form settings
- All user-facing strings from config, not hardcoded
- Dropped `sync_jobs` (PetFinder API is dead)
- Dropped AHCR-specific import tables
- Generic CSV import replaces hardcoded importers
---
## 3. Tenant Lifecycle
```
Rescue finds fosterflow.app
│
▼
┌─────────────────────┐
│ Signup Form │
│ org name, admin │
│ name/email, pass, │
│ subdomain, website │
│ EIN (opt), desc, │
│ city/state │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Pending Review │ → Operator gets email notification
│ (stored in │
│ platform DB) │
└─────────┬───────────┘
│
┌─────▼─────┐
│ Operator │
│ reviews │
├────┬───────┤
│ │ │
Approve Reject
│ │
│ └→ Rejection email with reason
▼
┌─────────────────────┐
│ Auto-Provision │ (~60 seconds)
│ 1. Create DB │
│ 2. Run migrations │
│ 3. Seed admin user │
│ 4. Generate .env │
│ 5. Clone tenant app│
│ 6. Build & start │
│ 7. Add Caddy route │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Welcome Email │
│ login URL, temp │
│ password, guide │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Setup Wizard │
│ branding, config, │
│ application forms, │
│ optional CSV import│
└─────────┬───────────┘
│
▼
Live at {slug}.fosterflow.app
14-day free trial (no card required)
│
▼
Trial expires → Pay $19/mo → Active
→ Don't pay → Suspended (7 day grace)
→ Deleted (30 days, data archived)
```
**Signup validation:** subdomain availability, unique email, spam checks.
**Suspension:** App still runs but shows "account suspended" page. Data preserved. Reactivate by paying.
**Custom domains:** Tenant sets domain in settings, adds CNAME to `{slug}.fosterflow.app`, Caddy auto-provisions SSL.
**Deprovisioning:** Stop service, remove Caddy route, archive DB dump + uploads, mark deleted. Hard delete after 30 days.
---
## 4. Roles & Permissions
### Operator Level (Platform App)
| Role | Access |
|------|--------|
| `operator` | Full access — approve/reject signups, manage tenants, billing, health, migrations |
Separate auth system from tenant auth. Email/password login.
### Tenant Level (Tenant App)
| Role | Permissions |
|------|-------------|
| `sysadmin` | All 14 permissions (full control of rescue instance) |
| `director` | All except `system` |
| `foster_coordinator` | dashboard, pets, applications |
| `volunteer_manager` | dashboard, volunteers, events |
| `vet_liaison` | dashboard, pets, vets, medical, expenses |
| `content_editor` | dashboard, sponsors, content, events |
| `applications_manager` | dashboard, applications, pets_view |
| `viewer` | dashboard only |
| `foster` | Foster portal only |
**14 permissions:** pets, applications, donations, expenses, vets, medical, events, content, volunteers, users, shop, import, system, dashboard
Tenant `sysadmin` has zero visibility into the platform. They see their rescue site and nothing else.
---
## 5. Feature Set
### Core Features (Rebuilt from AHCR Reference)
| Feature | Description |
|---------|-------------|
| **Pet Management** | Profiles, photos, medical records, foster assignments, status tracking, mentors |
| **Applications** | Adoption, foster, volunteer, surrender — configurable questions per form |
| **Foster Portal** | Dedicated dashboard — submit updates, photos, supply requests |
| **Donations & Expenses** | Stripe payments, multi-source tracking (PayPal, Venmo, cash, etc.), financial reports |
| **Events** | Listings with optional Facebook auto-posting |
| **Content CMS** | Editable page sections via `content_blocks`, happy tails / success stories |
| **Newsletter** | Subscriber management, compose and send |
| **Contact Forms** | Public contact form with message management in admin |
| **Sponsors** | Three tiers (top dog, wagging tails, wet noses), public sponsor page |
| **Analytics** | Page views, traffic tracking, bot detection |
| **Bot Protection** | Dynamic robots.txt, honeypot, managed blocklist/whitelist |
| **AI Name Generator** | Shared Ollama instance, free for all tenants |
### New for FosterFlow
| Feature | Description |
|---------|-------------|
| **Setup Wizard** | First-login onboarding — branding, org info, forms config, optional CSV import |
| **Configurable Theming** | Primary + accent color, logo upload via CSS custom properties |
| **`org_config` System** | All org-level settings in one place, drives the whole app |
| **Generic CSV Import** | Upload any CSV, map columns to FosterFlow fields, preview before commit |
| **Health Endpoint** | `/api/health` for platform monitoring |
| **i18n Architecture** | Translation key system, English strings in locale files, ready for more languages |
### Dropped from AHCR
| Dropped | Why |
|---------|-----|
| PetFinder sync | API is dead |
| AHCR-specific CSV import scripts | Replaced with generic column-mapping CSV import |
| Hardcoded content | Replaced with CMS content blocks + org_config |
---
## 6. Tech Architecture Details
### Tenant App Structure
```
fosterflow/
├── src/
│ ├── lib/
│ │ ├── server/
│ │ │ ├── db.ts # Drizzle + MariaDB connection
│ │ │ ├── schema.ts # All tenant tables
│ │ │ ├── auth.ts # Session management, Argon2
│ │ │ ├── auth-utils.ts # Permission checks
│ │ │ ├── email.ts # Nodemailer (from address via config)
│ │ │ ├── stripe.ts # Payment processing
│ │ │ ├── upload.ts # File uploads
│ │ │ ├── audit.ts # Audit logging
│ │ │ ├── rate-limit.ts # Rate limiting
│ │ │ ├── content.ts # CMS content blocks
│ │ │ ├── contract.ts # Adoption contract generation
│ │ │ ├── ai.ts # Ollama client (name generator)
│ │ │ └── config.ts # Reads org_config, caches it
│ │ ├── components/
│ │ │ ├── ui/ # Shared UI primitives
│ │ │ ├── admin/ # Admin dashboard components
│ │ │ └── public/ # Public site components
│ │ ├── i18n/
│ │ │ ├── index.ts # Translation loader
│ │ │ └── locales/
│ │ │ └── en.json # English strings
│ │ ├── roles.ts # Role + permission definitions
│ │ └── slugify.ts
│ ├── routes/
│ │ ├── (public)/ # Public-facing pages
│ │ ├── admin/ # Admin dashboard
│ │ ├── foster/ # Foster portal
│ │ ├── api/ # API endpoints
│ │ ├── application/ # Public application forms
│ │ ├── setup/ # First-login setup wizard
│ │ ├── login/
│ │ ├── logout/
│ │ ├── register/
│ │ ├── change-password/
│ │ ├── pay/ # Stripe payment pages
│ │ ├── robots.txt/
│ │ └── sitemap.xml/
│ ├── hooks.server.ts # Auth, security headers, tracking
│ └── app.html
├── drizzle/ # Migration files
├── scripts/
│ ├── seed.ts # Seed placeholder data
│ └── seed-tenant.ts # Seed admin user (used by provisioner)
├── static/
├── .env.example
├── package.json
├── svelte.config.js
├── drizzle.config.ts
├── vite.config.ts
├── docker-compose.yml
├── Dockerfile
└── Caddyfile
```
### Config-Driven Architecture
Everything flows from two sources:
- **`.env`** — infrastructure config (DB URL, ports, secrets, Stripe keys, SMTP)
- **`org_config` table** — org-level config (branding, org info, locale, form settings, agreements)
```
Request comes in
→ hooks.server.ts loads org_config (cached, refreshes every 5 min)
→ event.locals.orgConfig available in all routes
→ Components read orgConfig for branding, org name, colors
→ CSS custom properties set from orgConfig.branding
→ i18n loads locale from orgConfig.locale
```
### Theming
```css
/* Generated from org_config.branding */
:root {
--color-primary: #0d9488;
--color-accent: #f59e0b;
}
```
Tailwind CSS 4 picks these up via CSS custom properties. Components use `text-primary`, `bg-accent`, etc. No hardcoded colors.
### i18n Approach
Every user-facing string goes through a `t()` function:
```svelte
<h1>{t('pets.title')}</h1>
<p>{t('applications.adoption.intro')}</p>
```
`en.json` ships with the app. Adding a language = adding a locale file + tenant picks locale in settings. No code changes required.
### Generic CSV Import
- Upload any CSV file
- App auto-detects columns, shows preview
- User maps their columns to FosterFlow fields (name, breed, age, status, etc.)
- Preview mapped data before committing
- Handle mismatches gracefully (skip bad rows, show errors)
- Available in setup wizard and in admin anytime for bulk imports
---
## 7. Testing Strategy
| Layer | Tool | What |
|-------|------|------|
| **Unit** | Vitest | Schema validation, permission checks, config loading, i18n, utility functions |
| **Integration** | Vitest | DB operations, auth flows, API endpoints (real DB, not mocks) |
| **E2E** | Playwright | Full user flows — setup wizard, pet CRUD, application submission, foster portal |
**Key test scenarios:**
- Setup wizard completes and populates org_config
- Role permissions block/allow correctly
- Theming applies from org_config
- CSV import with various column formats
- Application forms render based on config
- Public site reflects CMS content blocks
- Health endpoint returns correct status
---
## 8. Deployment & Infrastructure
### Self-Hosted (Tenant App Only)
```bash
git clone https://github.com/jrei/fosterflow
cd fosterflow
cp .env.example .env # edit with your details
docker compose up -d # MariaDB + app + Caddy
```
Three containers:
- `fosterflow-app` — Node.js SvelteKit
- `fosterflow-db` — MariaDB
- `fosterflow-proxy` — Caddy (auto SSL)
No operator dashboard. No multi-tenant machinery. Just a rescue site.
### FosterFlow Cloud
Caddy proxy, one app process per tenant, database-per-tenant on shared MariaDB. Platform app runs alongside.
Provisioning script automates: create DB, run migrations, seed admin user, clone tenant app, generate `.env`, build, start systemd service, add Caddy route.
**Scaling path:**
| Tenants | Setup |
|---------|-------|
| 0-100 | Single VPS ($15-20/mo) |
| 100-200 | Separate DB server, shared app build (symlink not copy), automated health checks |
| 200+ | Multiple app servers, Cloudflare R2 for images, monitoring (Grafana), consider containers |
**Revenue at scale:**
| Tenants | Monthly Revenue | Server Costs | Margin |
|---------|----------------|--------------|--------|
| 10 | $190 | $15 | $175 |
| 50 | $950 | $30 | $920 |
| 100 | $1,900 | $60 | $1,840 |
| 500 | $9,500 | $200 | $9,300 |
### Backups
- Per-tenant `mysqldump` on cron
- Uploads archived separately
- Platform DB backed up independently
- 30-day retention after tenant deletion
### Pricing Tiers
| Tier | Price | What They Get |
|------|-------|---------------|
| **Self-Hosted** | Free | Full source code, run it yourself, MIT licensed |
| **Manage** | $12/mo | Admin dashboard + foster portal only. No hosted public site. Embeddable pet widgets for their existing website. |
| **Cloud** | $25/mo | Full hosted website + management + subdomain + SSL + backups + updates |
| **API Add-on** | +$5/mo | REST API access for custom integrations (available on Manage or Cloud) |
**Trial:** 14 days free on any paid tier (no card required)
**Why tiered pricing:**
- Most rescues already have a website (WordPress, Facebook page, etc.). The **Manage** tier captures rescues that want better management tools but don't want to switch their whole site.
- **Embeddable widgets** bridge the gap — a JS snippet that shows available pets on their existing site, pulling from FosterFlow data. Displays "Powered by FosterFlow" (marketing for upgrades).
- **Cloud** is the premium experience — full hosted site + management, zero setup.
- **API access** lets tech-savvy rescues build custom integrations or embed data however they want.
**Manage tier details:**
- Tenant gets admin dashboard + foster portal (same app, just no public routes served)
- Caddy routes their subdomain to admin/foster only
- Embeddable widgets: `<script src="https://{slug}.fosterflow.app/embed/pets.js"></script>` renders a pet grid on any page
- Widget is an iframe or web component pulling from the existing `/api/pets` endpoint
- Lower infrastructure cost (no public traffic, just admin users)
**Revenue projections (updated):**
| Tenants | Mix (Manage/Cloud) | Monthly Revenue | Server Costs | Margin |
|---------|-------------------|----------------|--------------|--------|
| 10 | 5/5 | $185 | $15 | $170 |
| 50 | 30/20 | $860 | $30 | $830 |
| 100 | 60/40 | $1,720 | $60 | $1,660 |
| 500 | 300/200 | $8,600 | $200 | $8,400 |
### Billing (Stripe)
- **Products:** FosterFlow Manage ($12/mo), FosterFlow Cloud ($25/mo), API Add-on (+$5/mo)
- **Trial:** 14 days free (no card required)
- **Webhook events:**
- `customer.subscription.created` → activate tenant
- `customer.subscription.deleted` → suspend tenant (grace period)
- `invoice.payment_failed` → warning email, suspend after 3 failures
- **Dunning:** Stripe handles retry logic
---
## Reference
- **Domain:** fosterflow.app
- **AHCR source:** `../almosthomecaninerescue.com-2026/ahcr/`
- **Market:** ~10,000 rescue organizations in the US, ~14,000 total animal welfare orgs
- **Differentiator:** Full website + management in one (competitors are mostly backend-only tools). Manage tier captures rescues that just want the backend.
- **Competition:** Shelterluv ($2/adoption), RescueGroups (free), Petstablished (free), BARRK ($20/mo)