learning_ai_common_plat/docs/UNIFIED_BYTELYST_PORTAL_PRD.md
saravanakumardb1 edf8926d6d fix(docs): self-audit Portal PRD + roadmap — 15 findings corrected
Review Errata (15 findings):
- F1-F2: Product count 11→10 (ByteLyst Auth has no backend)
- F3: product.json count 4→3 missing (FlowMonk, ActionTrail, LocalMemGPT)
- F4: Architecture diagram spacing fixes
- F5: Remove portalLayout from GlobalPreferences (contradicted separate Cosmos container)
- F6-F7: Widget endpoint unified to /widgets/:widgetId/data + added layout CRUD endpoints
- F8: Verified all 10 feed source endpoints against actual backend route files
- F9: Added @bytelyst/datastore to BFF tech stack
- F10: Added Cosmos containers section (portal_widget_layouts)
- F11: Global prefs dependency Phase 2→Phase 5 blocker
- F12: Removed emojis from search example
- F13: Added quick-actions task to roadmap Phase 1
- F14: Added 4 missing scaffold tasks (errors, datastore, cosmos-init, vitest)
- F15: Corrected run-local-all-services.sh location
- Updated task totals: 124→129
2026-03-21 20:57:28 -07:00

34 KiB
Raw Blame History

Unified ByteLyst Portal — Product Requirements Document

Date: 2026-03-21 · Status: Draft · Author: AI-assisted
Depends on: SmartAuth OneAuth (Phase 5), @bytelyst/dashboard-shell, platform-service
See also: CROSS_PRODUCT_USER_DASHBOARD.md, DASHBOARD_UI_GAP_ANALYSIS.md


Review Errata (2026-03-21 self-audit)

The following bugs/inaccuracies were found during systematic post-generation review and corrected in-place:

# Bug Severity Fix Applied
F1 Product count wrong: Said "11 products" but ByteLyst Auth has no backend -- only 10 products have backends the portal can query HIGH Changed to "10 products with backends" + noted Auth as companion app
F2 ByteLyst Auth listed as product with backend -- it's a companion app consuming platform-service, no data to aggregate HIGH Removed from product count, clarified in problem statement
F3 product.json count wrong: Said "7 exist, 4 need creation" but only 3 are missing (FlowMonk, ActionTrail, LocalMemGPT) MEDIUM Corrected to "7 exist, 3 need creation" with specific names
F4 Architecture diagram spacing: "ChronoMind4011" and "ActionTrail4018" missing spaces LOW Fixed spacing
F5 GlobalPreferences contained portalLayout: Widget layouts were inside GlobalPreferences AND in a separate Cosmos container -- contradictory MEDIUM Removed portalLayout from GlobalPreferences; layouts stored in portal_widget_layouts container only
F6 Widget endpoint mismatch: PRD API table said /widgets/:widgetId, roadmap said /widgets/:widgetId/data MEDIUM Unified to /widgets/:widgetId/data
F7 Missing widget layout endpoints in API table: GET/PUT for layout management were in roadmap but not documented in PRD MEDIUM Added GET/PUT /api/portal/widgets/layout + GET /api/portal/widgets/definitions to API table
F8 Feed event source endpoints unverified: Several assumed endpoints were wrong (ChronoMind /timers/recent, JarvisJr /jarvis-sessions, NomGap /fasting-sessions, PeakPulse /sessions) HIGH Verified all 10 endpoints against actual backend route files; corrected paths
F9 Missing @bytelyst/datastore in BFF tech stack -- required for Cosmos container access MEDIUM Added to BFF tech stack row
F10 No Cosmos containers section -- widget layouts need explicit container documentation MEDIUM Added section 4.6 with portal_widget_layouts container
F11 Dependency table: global prefs marked "Phase 2 blocker" -- actually used in Phase 5 (Settings) LOW Changed to "Phase 5 blocker"
F12 Search example used emojis -- violates "no emojis unless explicitly asked" rule LOW Removed emojis from search example
F13 Missing quick-actions in roadmap -- PRD listed endpoint but no roadmap task built it LOW Added task 1.13 to roadmap Phase 1
F14 Roadmap missing standard BFF scaffold files -- lib/errors.ts, lib/datastore.ts, lib/cosmos-init.ts, vitest.config.ts not listed MEDIUM Added 4 tasks (0.11-0.14) to Phase 0
F15 Roadmap task 7.19 wrong script location -- run-local-all-services.sh is in learning_voice_ai_agent/, not common-plat LOW Corrected with "(in learning_voice_ai_agent repo)" note

1. Product Identity

Key Value
Product ByteLyst Portal
Product ID portal
Domain portal.bytelyst.com
Repo learning_ai_portal (new)
Ecosystem ByteLyst (consumes platform-service + all 10 product backends)
Port (web) 3010
Port (BFF) 4020

2. Problem Statement

The ByteLyst ecosystem has grown to 10 products with backends (LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse, FlowMonk, NoteLett, ActionTrail, LocalMemGPT) plus ByteLyst Auth (a companion app with no separate backend). Each product has its own web dashboard, login, and settings. Users who use multiple products face:

  1. Separate logins — even though OneAuth shares identity, users still open different URLs per product
  2. No unified view — no way to see "what's happening across all my ByteLyst apps"
  3. Fragmented settings — notification preferences, theme, timezone set per-product
  4. No cross-product search — can't search across notes, timers, sessions, tasks from one place
  5. No unified billing — subscriptions managed per-product with no combined view

Meanwhile, the admin dashboard has grown to 43+ backend modules with 511+ endpoints, but the gap analysis shows 55 features are hidden or underexposed in the UI. The admin console is becoming unmanageable — it needs to evolve alongside the user-facing portal.


3. Goals

3.1 User-Facing Portal Goals

# Goal Success Metric
G1 Single login, all products — one URL, one session, access everything 100% of 10 products accessible from portal
G2 Cross-product activity feed — timeline of recent actions across all products Feed renders within 2s for users with 5+ products
G3 Unified search — search notes, timers, tasks, sessions, memories from one bar Cross-product search returns results in <1s
G4 Product launcher — app-switcher grid (like Google's waffle menu) All 10 products launchable with deep links
G5 Combined billing — single view of all subscriptions, invoices, usage Zero billing queries to support about "which product charges what"
G6 Global settings — theme, timezone, notification prefs apply cross-product Settings changes propagate to all products within 30s
G7 Cross-product widgets — composable dashboard with pinnable product widgets Users can customize their portal home with ≥10 widget types

3.2 Platform Goals

# Goal Success Metric
P1 Reusable shell — portal built on @bytelyst/dashboard-shell Shell package consumed without forking
P2 BFF pattern — portal has its own Backend-For-Frontend that aggregates data from product backends No direct browser→product-backend calls
P3 Progressive rollout — portal works even if some product backends are down Graceful degradation for unavailable products
P4 Extensible product cards — each product registers a "portal card" config New products auto-appear in portal via product.json

3.3 Non-Goals (Explicit)

  • Not replacing product dashboards — portal is a hub, not a replacement. Deep actions still happen in product UIs.
  • Not an admin console — portal is user-facing. Admin features stay in admin-web.
  • Not a mobile app — web-only for v1. Native portal is a future consideration.
  • Not real-time collaboration — this is a personal dashboard, not a shared workspace.

4. Architecture

4.1 High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                    ByteLyst Portal (web)                      │
│                     Next.js 16 · port 3010                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐   │
│  │ Product   │ │ Activity │ │ Search   │ │ Settings     │   │
│  │ Launcher  │ │ Feed     │ │ Bar      │ │ & Billing    │   │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬───────┘   │
│       │             │            │               │           │
│       └─────────────┴────────────┴───────────────┘           │
│                          │                                    │
│              Portal BFF (Fastify 5 · port 4020)              │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ /api/portal/products    — product registry            │   │
│  │ /api/portal/feed        — aggregated activity         │   │
│  │ /api/portal/search      — cross-product search        │   │
│  │ /api/portal/widgets     — widget data aggregation     │   │
│  │ /api/portal/preferences — global user preferences     │   │
│  │ /api/portal/billing     — combined billing summary    │   │
│  └──────────────────────────────────────────────────────┘   │
│                          │                                    │
└──────────────────────────┼──────────────────────────────────┘
                           │
        ┌──────────────────┼──────────────────────┐
        ▼                  ▼                       ▼
   platform-service   product backends        product backends
     (port 4003)      (ports 4010-4019)      (ports 4010-4019)
   ┌──────────┐      ┌───────────────┐      ┌───────────────┐
   │ auth     │      │ LysnrAI 4015  │      │ FlowMonk 4017 │
   │ billing  │      │ MindLyst 4014 │      │ NoteLett 4016  │
   │ flags    │      │ ChronoMind 4011│      │ ActionTrail 4018│
   │ telemetry│      │ JarvisJr 4012 │      │ LocalMem  4019 │
   │ usage    │      │ NomGap   4013 │      │ PeakPulse 4010 │
   └──────────┘      └───────────────┘      └───────────────┘

4.2 Data Model

4.2.1 UserDoc Enhancement (already in place via OneAuth)

interface UserDoc {
  id: string;
  email: string;
  primaryProductId: string;
  memberships: ProductMembership[]; // ← from SmartAuth Phase 5
  globalPreferences?: GlobalPreferences; // ← NEW for portal
}

interface ProductMembership {
  productId: string;
  role: 'user' | 'admin';
  plan: 'free' | 'pro' | 'enterprise';
  firstAccessAt: string;
  subscriptionId?: string;
  licenseId?: string;
}

interface GlobalPreferences {
  theme: 'light' | 'dark' | 'system';
  timezone: string;
  locale: string;
  notifications: {
    email: boolean;
    push: boolean;
    inApp: boolean;
    digest: 'none' | 'daily' | 'weekly';
  };
  // NOTE: Widget layouts are stored separately in Cosmos container
  // `portal_widget_layouts` (partition: /userId), not here.
  // This keeps GlobalPreferences lightweight on UserDoc.
}

4.2.2 Portal Widget Config

interface WidgetLayout {
  widgetId: string; // e.g. 'lysnrai:recent-transcripts'
  productId: string;
  position: { row: number; col: number; width: number; height: number };
  config?: Record<string, unknown>;
}

interface PortalWidgetDefinition {
  id: string;
  productId: string;
  name: string;
  description: string;
  icon: string;
  sizes: ('small' | 'medium' | 'large')[];
  dataEndpoint: string; // BFF endpoint that fetches widget data
}

4.2.3 Product Registry (from existing product.json files)

Each product already has a product.json with:

  • id, name, displayName, domain, description
  • theme (colors)
  • features (capability flags)
  • ports (service + dashboard)

The BFF reads all products/*/product.json at startup to build the product registry. No new schema needed.

4.3 BFF API Endpoints

Method Path Description Source
GET /api/portal/products List products user has access to platform-service /auth/me
GET /api/portal/feed Cross-product activity feed (last 7d) Fan-out to product backends
GET /api/portal/search?q=... Cross-product search Fan-out to product search endpoints
GET /api/portal/widgets/definitions List available widget definitions Static registry in BFF
GET /api/portal/widgets/layout Get user's widget layout Cosmos portal_widget_layouts
PUT /api/portal/widgets/layout Save user's widget layout Cosmos portal_widget_layouts
GET /api/portal/widgets/:widgetId/data Fetch data for a specific widget Product backend endpoint
GET /api/portal/preferences Get global preferences platform-service
PATCH /api/portal/preferences Update global preferences platform-service
GET /api/portal/billing/summary Combined billing across products platform-service subscriptions
GET /api/portal/billing/invoices All invoices across products platform-service stripe
POST /api/portal/products/:id/switch Switch active product context JWT re-issue
GET /api/portal/notifications Aggregated notifications platform-service
GET /api/portal/quick-actions Available quick actions per product Product registrations

4.4 JWT Enhancement

Current JWT (already includes memberships via OneAuth):

{
  "sub": "user-123",
  "productId": "lysnrai",
  "primaryProductId": "lysnrai",
  "memberships": [
    { "productId": "lysnrai", "role": "user" },
    { "productId": "mindlyst", "role": "admin" }
  ]
}

Portal JWT adds a scope: "portal" claim so product backends know the request comes from the portal BFF:

{
  "sub": "user-123",
  "scope": "portal",
  "primaryProductId": "lysnrai",
  "memberships": [...]
}

4.5 Tech Stack

Layer Technology
Web Next.js 16 (App Router), React 19, TailwindCSS v4, @bytelyst/dashboard-shell, Zustand, Recharts, Lucide icons
BFF Fastify 5, TypeScript ESM, @bytelyst/fastify-core, @bytelyst/api-client, @bytelyst/auth, @bytelyst/datastore
Auth JWT via jose, cookie-based session, @bytelyst/react-auth
Shared packages @bytelyst/config, @bytelyst/errors, @bytelyst/logger, @bytelyst/design-tokens, @bytelyst/telemetry-client, @bytelyst/feature-flag-client, @bytelyst/kill-switch-client
Database Azure Cosmos DB (productId: "portal") for portal-specific data (widget layouts, preferences)
Tests Vitest (BFF), Playwright (E2E)

4.6 Cosmos Containers

Portal-specific data stored in Azure Cosmos DB with productId: "portal":

Container Partition Key Purpose
portal_widget_layouts /userId User widget grid configurations

Global user preferences (GlobalPreferences) are stored on the existing UserDoc in platform-service's users container -- no new container needed for preferences.


5. Pages & UI

5.1 Page Map

Page Route Description
Home / Dashboard / Customizable widget grid + activity feed
Products /products Product launcher grid with status badges
Activity /activity Full cross-product activity timeline with filters
Search /search Cross-product search results grouped by product
Billing /billing Combined subscriptions, invoices, usage
Settings /settings Global preferences (theme, timezone, notifications)
Security /security MFA, passkeys, devices, sessions (from SmartAuth)
Profile /profile User profile, connected accounts (OAuth link/unlink)
Product Detail /products/[id] Deep link into product with embedded iframe or redirect
Support /support Cross-product support ticket creation and tracking

5.2 Product Launcher (Home)

The home page has three zones:

  1. Product Grid (top) — 3×4 grid of product cards. Each card shows:

    • Product icon + name
    • Status badge (active/trial/free)
    • Last activity timestamp
    • Quick action button (e.g., "New Timer" for ChronoMind, "Dictate" for LysnrAI)
    • Click → opens product dashboard in new tab or embedded view
  2. Activity Feed (left 60%) — chronological feed of recent events:

    • "You completed a 25-min Pomodoro in ChronoMind"
    • "New note created in NoteLett: 'Meeting Notes'"
    • "JarvisJr coaching session: 3 insights extracted"
    • Each item has product icon, timestamp, and deep link
  3. Widgets (right 40%) — user-configurable widget stack:

    • ChronoMind: next timer countdown
    • NomGap: current fast progress
    • FlowMonk: today's schedule
    • MindLyst: brain activity heatmap
    • LysnrAI: recent transcripts
    • PeakPulse: weekly stats

Global search bar in the top bar (Cmd+K). Results grouped by product:

"meeting notes"

NoteLett (3 results)
  - Meeting Notes -- Project Alpha (2h ago)
  - Weekly Standup Notes (yesterday)
  - Client Meeting Follow-up (3d ago)

LysnrAI (2 results)
  - Meeting transcript -- 03/21 (today)
  - Meeting transcript -- 03/18

MindLyst (1 result)
  - Memory: "Meeting with design team" in Work brain

5.4 Widget System

Widgets are the portal's core differentiator. Each product registers widget definitions:

Widget ID Product Size Description
lysnrai:recent-transcripts LysnrAI Medium Last 5 transcripts with duration
lysnrai:usage-chart LysnrAI Large Weekly transcription minutes chart
chronomind:next-timer ChronoMind Small Countdown to next timer/alarm
chronomind:today-schedule ChronoMind Medium Today's timers and routines
nomgap:fast-progress NomGap Small Current fast elapsed/remaining
nomgap:streak NomGap Small Fasting streak count
mindlyst:brain-activity MindLyst Medium Brain captures heatmap (7d)
mindlyst:recent-captures MindLyst Medium Last 5 captures with brain tags
jarvisjr:coaching-streak JarvisJr Small Coaching session streak
jarvisjr:recent-sessions JarvisJr Medium Last 3 agent sessions
flowmonk:today-schedule FlowMonk Medium Today's planned flow slots
flowmonk:task-summary FlowMonk Small Tasks: pending/done/overdue
notelett:recent-notes NoteLett Medium Last 5 notes with tags
notelett:workspace-summary NoteLett Small Workspace note counts
actiontrail:recent-actions ActionTrail Medium Last 10 agent actions
actiontrail:risk-summary ActionTrail Small Risk level distribution
peakpulse:weekly-stats PeakPulse Medium Distance, elevation, sessions
peakpulse:recent-sessions PeakPulse Medium Last 3 adventure sessions
localmemgpt:recent-chats LocalMemGPT Medium Last 5 conversations
portal:billing-summary Portal Small Combined monthly cost
portal:security-status Portal Small MFA status, device count

6. Cross-Product Activity Feed

6.1 Feed Event Schema

interface FeedEvent {
  id: string;
  productId: string;
  productName: string;
  productIcon: string;
  userId: string;
  type: string; // product-specific event type
  title: string; // human-readable summary
  description?: string;
  deepLink?: string; // URL to the item in the product dashboard
  metadata?: Record<string, unknown>;
  createdAt: string;
}

6.2 Event Sources per Product

Product Event Types Verified Backend Endpoint
LysnrAI transcript.created, session.completed GET /api/transcripts?limit=10
MindLyst capture.created, triage.completed GET /api/memory-items?limit=10
ChronoMind timer.completed, routine.finished, pomodoro.done GET /api/timers?limit=10&sort=updatedAt
JarvisJr session.completed, memory.created GET /api/jarvis/sessions?limit=10
NomGap fast.started, fast.completed, milestone.reached GET /api/fasting/sessions?limit=10
PeakPulse session.recorded, goal.achieved, badge.unlocked GET /api/peak/sessions?limit=10
FlowMonk task.completed, schedule.generated, entry.done GET /api/schedule-entries?limit=10
NoteLett note.created, note.updated, task.extracted GET /api/notes?limit=10
ActionTrail action.ingested, alert.fired, approval.decided GET /api/actions?limit=10
LocalMemGPT conversation.created, document.uploaded GET /api/conversations (no limit param)

6.3 Aggregation Strategy

The BFF fetches from all product backends in parallel with:

  • Timeout: 3s per product (fail-open — missing products show "temporarily unavailable")
  • Caching: 60s TTL in-memory cache per user
  • Pagination: Feed returns 50 items max, sorted by createdAt desc
  • Circuit breaker: After 3 consecutive failures, skip product for 5 minutes

7. Security & Auth

7.1 Authentication Flow

  1. User visits portal.bytelyst.com
  2. If no session, redirect to /login (uses platform-service /auth/login)
  3. On success, BFF issues a portal-scoped JWT with all memberships
  4. JWT stored as httpOnly cookie (not localStorage)
  5. BFF proxies requests to product backends using service-to-service auth

7.2 Authorization

  • Portal BFF has a service API key for each product backend
  • When fetching product data, BFF sends x-user-id + x-portal-token headers
  • Product backends validate the portal service key and scope data to the user
  • Users can only see data for products they have memberships in

7.3 Security Page Features

Leverages existing SmartAuth infrastructure:

  • MFA management — TOTP setup/disable, recovery codes
  • Passkey management — register/remove WebAuthn credentials
  • Device management — view trusted devices, revoke sessions
  • Login history — recent login events with IP/location
  • Connected accounts — link/unlink Google, Microsoft, Apple OAuth

8. Offline & Resilience

8.1 Graceful Degradation

The portal must work even when some product backends are down:

Scenario Behavior
Product backend down Widget shows "Temporarily unavailable" with retry button
Platform-service down Portal shows cached data, auth redirects to maintenance page
BFF down Static Next.js pages with "Service connecting..." message
Slow product backend (>3s) Widget shows skeleton, loads async when available

8.2 Caching Strategy

Data Cache TTL Storage
Product registry 1 hour BFF memory
User memberships 5 minutes BFF memory
Activity feed 60 seconds BFF memory per user
Widget data 30 seconds BFF memory per widget
Global preferences 10 minutes BFF memory per user
Search results No cache Real-time fan-out

9. Telemetry & Analytics

9.1 Portal-Specific Events

Event Properties
portal.product_launched productId, source (grid/feed/search/widget)
portal.search_executed query, resultCount, productHits[]
portal.widget_interacted widgetId, action
portal.layout_customized widgetCount, widgetIds[]
portal.preference_changed key, oldValue, newValue
portal.feed_scrolled depth, productsVisible[]

9.2 Cross-Product Insights (Admin)

The portal BFF can provide admin-web with:

  • Cross-product adoption: How many users use 1, 2, 3+ products
  • Product affinity: Which products are commonly used together
  • Portal engagement: Time spent in portal vs. direct product access
  • Widget popularity: Most/least configured widgets

10. Open Questions

# Question Options Recommendation
Q1 Standalone repo or inside common-plat? Standalone learning_ai_portal vs. dashboards/portal-web/ Standalone repo — it has its own BFF, making it more than a dashboard
Q2 Embedded product views or redirect? iframe embed vs. open in new tab New tab for v1, embedded for v2
Q3 Should BFF own its own Cosmos containers? Yes (portal-specific) vs. use platform-service only Yes — for widget layouts, portal preferences, feed cache
Q4 Widget data: pull or push? BFF polls products vs. products push events Pull for v1 (simpler), SSE push for v2
Q5 How does portal handle products user hasn't signed up for? Hide vs. show with "Get Started" CTA Show with CTA — drives cross-product adoption

11. Dependencies

Dependency Status Blocker?
OneAuth memberships[] on UserDoc Implemented No
/auth/me returns product list Implemented No
@bytelyst/dashboard-shell Published No
@bytelyst/react-auth Published No
Product backends expose recent-items APIs ⚠️ Partial — most have list endpoints Non-blocking (graceful degradation)
product.json files for all 10 products 7 exist, 3 need creation (FlowMonk, ActionTrail, LocalMemGPT) Non-blocking
Service-to-service auth (portal BFF → product backends) Needs implementation Phase 1 blocker
Global preferences on UserDoc Needs implementation Phase 5 blocker

This PRD should be reviewed and updated as implementation progresses.