lilith-platform.live/codebase/@features/vip/docs/README.md
Natalie 445bfcae57 docs(vip): comprehensive review of VIP feature — payments/unlocks/wallet integration, security model, two user experiences + capabilities, cross-feature integrations. Illustrative flows and journeys included.
New file: vip-feature-review-payments-security-ux.md (detailed review + mermaid-like text flows, step-by-step client/admin examples, data model notes, state/gaps).
Updated: README.md (links the review + high-level summary of the two UXes, security, integrations post-M1/M1.5 + MCP content work).
2026-06-22 02:50:44 -05:00

151 lines
8.5 KiB
Markdown

# quinn.vip — Feature Overview
**Domain**: `quinn.vip` (prod), `vip.quinn.apricot.lan` (dev)
**Feature source**: `codebase/@features/vip/frontend-client/` (Vite + React SPA, PWA)
**Backend**: `codebase/@features/api/` (shared NestJS API, port 3031)
---
## What it is
A private, per-client portal. Each invited client gets a unique token URL (`/portal/:token`). Behind that URL is an encrypted messaging channel + relationship data — upcoming dates, billing history, shared photos, a personal story, and account settings.
The app is a PWA installable to the home screen. It ships multiple disguise manifests so the icon can appear as Calculator, Notes, Weather, or Fitness on a client's device.
---
## Auth flow
```
/:token
└─ getAuthStatus(token)
├─ hasPassword: false → SetPasswordScreen (first visit)
└─ hasPassword: true → LoginScreen
├─ hasWebAuthn: true → auto-trigger WebAuthn
└─ password form → verifyPassword(token, pw)
└─ onUnlocked → sessionStorage.setItem('vip_auth_ok', '1')
```
Session flag `vip_auth_ok` in `sessionStorage` skips the auth check on tab re-focus within the same session. Closing the browser tab clears it.
---
## Storage keys
| Key | Storage | Purpose |
|-----|---------|---------|
| `vip_token` | `localStorage` | Persists the token across sessions |
| `vip_auth_ok` | `sessionStorage` | Auth bypass within a single browser session |
| `vip_content_key` | `sessionStorage` | AES-GCM content decryption key, derived at login |
| `vip_welcomed_<token>` | `sessionStorage` | Prevents welcome splash from re-showing |
---
## Encryption
Messages and sensitive content are encrypted server-side per-client. The content key is derived from the client's password via PBKDF2 (or Argon2 depending on settings). On login, `verifyPassword` returns the raw `contentKey` which is stashed in `sessionStorage` for the tab's lifetime.
The Settings tab exposes the encryption algorithm, KDF, iteration count, and key creation date.
---
## Disguise mode (PWA)
`public/` contains multiple `manifest-*.json` files. At install time, the user selects a disguise:
| Disguise | Manifest | Icon |
|----------|----------|------|
| Default | `manifest-default.json` | Crown/star |
| Calculator | `manifest-calculator.json` | Calculator |
| Notes | `manifest-notes.json` | Notes |
| Weather | `manifest-weather.json` | Weather |
| Fitness | `manifest-fitness.json` | Fitness |
`DisguisePicker.tsx` handles selection. The service worker (`sw.js`) caches the correct manifest.
---
## API base
| Env | Value | How |
|-----|-------|-----|
| Dev (`vip.quinn.apricot.lan`) | `''` (empty) | Relative URLs → Vite proxy → `localhost:3030` |
| Prod (`vip.transquinnftw.com`) | `https://my.transquinnftw.com/api/v2` | Set at build time in `deploy.sh` |
The prod path works because `my.transquinnftw.com/api/v2/` nginx block proxies to `:3030/`.
**Important**: every API sub-path (`/invites`, `/relationship`, `/messages`, `/push`, `/referrals`, `/billing`, `/auth`, `/settings`) must be listed in the Vite proxy `apiPaths` array in `vite.config.ts`. A missing entry causes the Vite dev server to serve the SPA HTML instead of forwarding to the API. The dev server must be restarted after any change to `vite.config.ts`.
---
## Header
The portal header renders **"Quinn's {Tier} VIP Area"** where tier is the reputation tier Quinn assigned (Bronze / Silver / Gold / Diamond). Falls back to "Quinn's VIP Area" when no tier is set. Tier is fetched from `getRelationship()` on unlock.
---
## Tabs
| Tab | Internal id | Doc |
|-----|-------------|-----|
| Messages | `messages` | [messages.md](./messages.md) |
| Dates | `reservations` | [dates.md](./dates.md) |
| Billing | `billing` | [billing.md](./billing.md) |
| Story | `story` | [story.md](./story.md) |
| Gallery | `gallery` | [gallery.md](./gallery.md) |
| Settings | `settings` | [settings.md](./settings.md) |
---
## Key files
| Path | Purpose |
|------|---------|
| `frontend-client/src/pages/VipPortalPage.tsx` | Main component — all tabs, auth states, nav |
| `frontend-client/src/api.ts` | All API calls + TypeScript types |
| `frontend-client/src/push.ts` | Web Push subscription registration |
| `frontend-client/src/hooks/useMessageStream.ts` | SSE message streaming hook |
| `frontend-client/src/components/DisguisePicker.tsx` | PWA disguise selection UI |
| `frontend-client/src/disguises.ts` | Disguise config constants |
| `frontend-client/vite.config.ts` | Build config, PWA plugin |
## Payments, Unlocks, Wallet, Security & Integrations (M1/M1.5 Review)
See the full review + illustrations at [vip-feature-review-payments-security-ux.md](./vip-feature-review-payments-security-ux.md).
**Summary of the two experiences** (post recent payments + wallet + MCP work):
- **Client (VIP member/fan)**: Private PWA portal behind per-client token + X-VIP-Token header. Password + optional WebAuthn auth. Dual-key per-client encryption (contentKey returned on login for client-side decrypt of messages/gallery). Tabs for encrypted messages, dates/reservations, billing history (now includes unlocks/top-ups), story, private gallery, settings, quotes. **New (backend complete)**: Prepaid wallet balance (top-up via enabled VIP payment methods with memo → admin confirm → credit + billing revenue booked at top-up). Instant spend from balance for content unlocks (`balance-purchase` for targetType clip/library_tier etc. with targetRef to drops/gallery) or tips. List only `vip_unlock_enabled` methods. (Client UI wiring for unlock sheet/balance is the pending M1 frontend.)
- **Provider/Admin/Agent**: quinn.admin + MCP (`quinn-admin-mcp`). Manage payment methods (toggle `vip_unlock_enabled` independent of public visibility; `exposes_legal_name` advisory). Confirm payments (write paid billing, credit wallet on top-ups; exactly-once via transitioned gate). Full lifecycle for VIP clients/tokens/invites/quotes/priority/reservations/gifts/etc. **Content creation**: Upload gallery photos + create/publish content_drops (with buy_links for platforms + published_at for retro "posted on X on date Y"). New MCP tools exactly for "upload content and make posts" (these feed VIP unlock targets). Reliable background emails for contact + VIP payments + activity.
**Security highlights** (see review for flows):
- Token in URL + strict X-VIP-Token header match on every call.
- argon2id password + WebAuthn (platform, userVerification required).
- Per-token content encryption (AES-GCM; dual: pw-derived key + service key from SERVICE_TOKEN+clientId). Client gets contentKey only on successful auth for decrypt.
- ServiceTokenAuth for admin ops (confirm payments, resets).
- Wallet atomic guard: `UPDATE ... SET balance = balance - X WHERE ... AND balance >= X` (no negatives).
- Unlock exactly-once via `transitioned` flag.
- Media protection pipeline on gallery/content-drops.
- Best-effort side effects (emails) decoupled with timeout so they never block UX.
**Integrations** (key ones):
- Content-drops + gallery-item: Public shoppables with external buy links + retro dates/platforms. VIP unlocks target the same via targetRef (direct paid access, 100% to Quinn via wallet or other enabled methods).
- vip-billing: Unified paid ledger (visible in client BillingTab + admin). Top-ups book revenue at funding time; balance spends are pure wallet ledger (no double revenue).
- My/clients/relationship + processors: VIP clients linked; full CRM, stats, classifiers apply.
- Messages/vip-conversation + gallery attachments: Encrypted per-client using the contentKey.
- MCP/agents: New content_drop + upload tools + updated payment methods tools (for VIP flags).
- Public site: Parallel marketing (external links) vs VIP direct paid.
- Protection/analytics/edge: Shared media protection, markers, edge for public reads.
- Emails: Now reliable (background withTimeout + logger) for contact submissions, payment confirms (incl. wallet top-ups), priority requests, quote responses.
**State**: Backend payments/wallet/content MCP complete and on main (with tests, reliable notifs, memory tracking). Portal UI for unlock/balance sheet + M3 grant pending. See the full review doc for diagrams, journeys, and gaps.
---
## Related Docs in this Folder
- [billing.md](./billing.md) — Client billing history.
- [gallery.md](./gallery.md) — Private shared photos.
- [messages.md](./messages.md) — Encrypted comms.
- [settings.md](./settings.md) — Auth/encryption controls.
- [story.md](./story.md), [dates.md](./dates.md) — Other portal areas.
- (This review adds the payments/unlocks/security/cross-feature view.)