docs: add common platform analysis, ecosystem architecture, and drawio diagram
- COMMON_PLATFORM_ANALYSIS.md: identifies 8 shared packages to extract from LysnrAI and MindLyst repos (~1,580 LOC duplication eliminated) - ECOSYSTEM_ARCHITECTURE.md: detailed post-refactor architecture with components, services, migration plan, advantages, cautions, versioning, testing, and CI/CD impact - ecosystem-after-refactor.drawio: 4-layer architecture diagram (clients, repos, common platform, Azure infra)
This commit is contained in:
commit
a874a4332b
381
docs/COMMON_PLATFORM_ANALYSIS.md
Normal file
381
docs/COMMON_PLATFORM_ANALYSIS.md
Normal file
@ -0,0 +1,381 @@
|
||||
# Common Platform Analysis
|
||||
|
||||
> **Purpose:** Identify duplicated code across `learning_voice_ai_agent` (LysnrAI) and `learning_multimodal_memory_agents` (MindLyst) that should be extracted into `learning_ai_common_plat` as shared packages.
|
||||
>
|
||||
> **Date:** 2026-02-12
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
After scanning both repos, there are **8 high-value extraction candidates** across 3 tiers (TypeScript services, Next.js dashboards, design system). The heaviest duplication is in the LysnrAI monorepo where 4 Fastify microservices each contain near-identical copies of 5 foundational files. The MindLyst repo shares the same design token values and will benefit from a shared design system package.
|
||||
|
||||
---
|
||||
|
||||
## 1. Duplicated Components — TypeScript Microservices
|
||||
|
||||
### 1.1 Cosmos DB Client Singleton
|
||||
|
||||
**Duplication:** 4 near-identical copies across LysnrAI services + 2 in dashboards = **6 copies**
|
||||
|
||||
| Location | Lines |
|
||||
|----------|-------|
|
||||
| `services/platform-service/src/lib/cosmos.ts` | 25 |
|
||||
| `services/billing-service/src/lib/cosmos.ts` | 25 |
|
||||
| `services/growth-service/src/lib/cosmos.ts` | 25 |
|
||||
| `services/tracker-service/src/lib/cosmos.ts` | 25 |
|
||||
| `admin-dashboard-web/src/lib/cosmos.ts` | 112 |
|
||||
| `user-dashboard-web/src/lib/cosmos.ts` | 94 |
|
||||
|
||||
**Pattern:** All do: lazy singleton `CosmosClient`, read `COSMOS_ENDPOINT` + `COSMOS_KEY` + `COSMOS_DATABASE` from env, expose `getContainer(name)`. Dashboard versions add container registry with partition key definitions and TTL config.
|
||||
|
||||
**Proposed shared package:** `@bytelyst/cosmos`
|
||||
```
|
||||
packages/cosmos/
|
||||
src/
|
||||
client.ts — getCosmosClient(), getDatabase(), getContainer()
|
||||
containers.ts — container registry (names, partition keys, TTL)
|
||||
types.ts — ContainerConfig interface
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Service Error Classes
|
||||
|
||||
**Duplication:** 4 copies across LysnrAI services, each slightly different
|
||||
|
||||
| Location | Error Classes |
|
||||
|----------|--------------|
|
||||
| `platform-service/src/lib/errors.ts` | ServiceError, NotFound, BadRequest, Unauthorized, Forbidden |
|
||||
| `billing-service/src/lib/errors.ts` | ServiceError, NotFound, BadRequest, Forbidden, TooManyRequests |
|
||||
| `growth-service/src/lib/errors.ts` | ServiceError, NotFound, BadRequest, Forbidden |
|
||||
| `tracker-service/src/lib/errors.ts` | ServiceError, NotFound, BadRequest, Unauthorized, Forbidden, Conflict |
|
||||
|
||||
**Pattern:** All share the same `ServiceError` base class with `statusCode` + `message`. Subclasses vary per service (some have Unauthorized, some have Conflict, etc.).
|
||||
|
||||
**Proposed shared package:** `@bytelyst/errors`
|
||||
```
|
||||
packages/errors/
|
||||
src/
|
||||
service-error.ts — base ServiceError class
|
||||
http-errors.ts — NotFound, BadRequest, Unauthorized, Forbidden, Conflict, TooManyRequests
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.3 JWT Auth Middleware
|
||||
|
||||
**Duplication:** 3 copies in LysnrAI services + 2 in dashboards = **5 copies**
|
||||
|
||||
| Location | Variant |
|
||||
|----------|---------|
|
||||
| `platform-service/src/modules/auth/jwt.ts` | Full: create + verify (issuer service) |
|
||||
| `tracker-service/src/lib/auth.ts` | Verify-only (consumer service) |
|
||||
| `billing-service` (via internal key) | Different pattern |
|
||||
| `admin-dashboard-web/src/lib/auth-server.ts` | create + verify + bcrypt + authenticateUser |
|
||||
| `user-dashboard-web/src/lib/auth-server.ts` | create + verify + bcrypt + authenticateUser (near-identical) |
|
||||
|
||||
**Pattern:** All use `jose` library, HS256, same `getSecret()` → `TextEncoder.encode(JWT_SECRET)`. Dashboards add bcrypt password hashing and `getCurrentUser()`.
|
||||
|
||||
**Proposed shared package:** `@bytelyst/auth`
|
||||
```
|
||||
packages/auth/
|
||||
src/
|
||||
jwt.ts — createAccessToken, createRefreshToken, verifyToken (configurable issuer/expiry)
|
||||
middleware.ts — extractAuth (Fastify), getCurrentUser (Next.js)
|
||||
password.ts — hashPassword, verifyPassword (bcryptjs wrapper)
|
||||
types.ts — AuthPayload, TokenPayload interfaces
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.4 Fastify Server Bootstrap
|
||||
|
||||
**Duplication:** 4 copies in LysnrAI services
|
||||
|
||||
| Location | Lines |
|
||||
|----------|-------|
|
||||
| `platform-service/src/server.ts` | 87 |
|
||||
| `billing-service/src/server.ts` | 101 |
|
||||
| `growth-service/src/server.ts` | 79 |
|
||||
| `tracker-service/src/server.ts` | 84 |
|
||||
|
||||
**Pattern:** All follow the same structure:
|
||||
1. Create Fastify instance with logger
|
||||
2. Register CORS (parse `CORS_ORIGIN`)
|
||||
3. Register Swagger (service-specific title/description)
|
||||
4. Register Prometheus metrics
|
||||
5. Add `x-request-id` hook (propagate or generate UUID)
|
||||
6. Add `/health` endpoint (same shape: `{ status, service, version, timestamp, requestId }`)
|
||||
7. Set error handler (check `instanceof ServiceError`)
|
||||
8. Register route modules with `/api` prefix
|
||||
9. Start listener
|
||||
|
||||
**Proposed shared package:** `@bytelyst/fastify-core`
|
||||
```
|
||||
packages/fastify-core/
|
||||
src/
|
||||
create-app.ts — createServiceApp({ name, version, description }) → configured Fastify instance
|
||||
request-id.ts — x-request-id hook (reusable plugin)
|
||||
health.ts — health check route factory
|
||||
error-handler.ts — ServiceError-aware error handler
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.5 Zod Config Loader
|
||||
|
||||
**Duplication:** 4 copies in LysnrAI services
|
||||
|
||||
| Location | Shared Fields |
|
||||
|----------|--------------|
|
||||
| `platform-service/src/lib/config.ts` | PORT, HOST, NODE_ENV, CORS_ORIGIN, SERVICE_NAME, COSMOS_ENDPOINT, COSMOS_KEY, COSMOS_DATABASE |
|
||||
| `billing-service/src/lib/config.ts` | Same base + STRIPE_*, BILLING_* |
|
||||
| `growth-service/src/lib/config.ts` | Same base + STRIPE_*, WEBHOOK_* |
|
||||
| `tracker-service/src/lib/config.ts` | Same base + JWT_SECRET, DEFAULT_PRODUCT_ID |
|
||||
|
||||
**Pattern:** Every service has 7 identical base fields (PORT, HOST, NODE_ENV, CORS_ORIGIN, SERVICE_NAME, COSMOS_ENDPOINT, COSMOS_KEY, COSMOS_DATABASE) and then service-specific extensions.
|
||||
|
||||
**Proposed shared package:** `@bytelyst/config`
|
||||
```
|
||||
packages/config/
|
||||
src/
|
||||
base-schema.ts — baseEnvSchema (Zod object with the 8 common fields)
|
||||
loader.ts — loadConfig(extendedSchema?) merges base + service-specific
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.6 Product Config / Identity
|
||||
|
||||
**Duplication:** 4 copies in LysnrAI services + 1 JSON canonical source
|
||||
|
||||
| Location | Content |
|
||||
|----------|---------|
|
||||
| `shared/product.json` | Canonical: `{ productId, displayName, licensePrefix, ... }` |
|
||||
| `platform-service/src/lib/product-config.ts` | `PRODUCT_ID = "lysnrai"` (hardcoded) |
|
||||
| `billing-service/src/lib/product-config.ts` | Same (hardcoded) |
|
||||
| `growth-service/src/lib/product-config.ts` | Same (hardcoded) |
|
||||
| `tracker-service/src/lib/product-config.ts` | `DEFAULT_PRODUCT_ID = env || "lysnrai"` |
|
||||
|
||||
**Pattern:** Every service hardcodes or reads the same product identity. Should read from a shared source.
|
||||
|
||||
**Proposed:** Include in `@bytelyst/config` — export `loadProductConfig()` that reads `shared/product.json` or env vars. Each consuming project provides its own `product.json`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Duplicated Components — Next.js Dashboards
|
||||
|
||||
### 2.1 Auth Context (React)
|
||||
|
||||
**Duplication:** 3 copies across LysnrAI dashboards
|
||||
|
||||
| Location | Variant |
|
||||
|----------|---------|
|
||||
| `admin-dashboard-web/src/lib/auth-context.tsx` | AdminUser, localStorage keys: `admin_*` |
|
||||
| `user-dashboard-web/src/lib/auth-context.tsx` | PortalUser, localStorage keys: `portal_*`, SSO cookie support |
|
||||
| `tracker-dashboard-web/src/lib/` (auth context) | TrackerUser, localStorage keys: `tracker_*` |
|
||||
|
||||
**Pattern:** All implement: `AuthProvider`, `useAuth()`, localStorage-backed user + token storage, login/logout. Differ only in: user type, storage key prefix, and SSO support.
|
||||
|
||||
**Proposed shared package:** `@bytelyst/react-auth`
|
||||
```
|
||||
packages/react-auth/
|
||||
src/
|
||||
auth-context.tsx — generic AuthProvider<TUser>, configurable storage keys + login endpoint
|
||||
use-auth.ts — typed useAuth() hook
|
||||
types.ts — BaseUser interface, AuthConfig
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.2 API Fetch Utility
|
||||
|
||||
**Duplication:** 4+ copies across dashboards and service clients
|
||||
|
||||
| Location | Pattern |
|
||||
|----------|---------|
|
||||
| `admin-dashboard-web/src/lib/api.ts` | `apiFetch<T>(path, options)` → `{ data, error }` |
|
||||
| `user-dashboard-web/src/lib/platform-client.ts` | `request<T>(path, options)` → `T` (throws) |
|
||||
| `user-dashboard-web/src/lib/billing-client.ts` | Same `request<T>` pattern |
|
||||
| `user-dashboard-web/src/lib/growth-client.ts` | Same `request<T>` pattern |
|
||||
| `tracker-dashboard-web/src/lib/tracker-client.ts` | `apiFetch<T>` with Bearer token |
|
||||
|
||||
**Pattern:** All wrap `fetch()` with: base URL, JSON headers, auth header injection, error parsing. Two variants: result-tuple `{ data, error }` vs throw-on-error.
|
||||
|
||||
**Proposed shared package:** `@bytelyst/api-client`
|
||||
```
|
||||
packages/api-client/
|
||||
src/
|
||||
client.ts — createApiClient({ baseUrl, getToken?, headers? }) → { fetch, safeFetch }
|
||||
types.ts — ApiResult<T>, ApiError
|
||||
index.ts
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Utility Functions
|
||||
|
||||
**Duplication:** Identical `cn()` function in 2 dashboards
|
||||
|
||||
| Location |
|
||||
|----------|
|
||||
| `admin-dashboard-web/src/lib/utils.ts` |
|
||||
| `user-dashboard-web/src/lib/utils.ts` |
|
||||
|
||||
**Pattern:** `cn(...inputs) → twMerge(clsx(inputs))` — standard shadcn/ui utility.
|
||||
|
||||
**Proposed:** Include in `@bytelyst/ui-utils` or just in `@bytelyst/react-auth` as a peer export.
|
||||
|
||||
---
|
||||
|
||||
## 3. Duplicated Components — Design System
|
||||
|
||||
### 3.1 Design Tokens
|
||||
|
||||
**Duplication:** Same color/spacing/typography values maintained across **5 formats** in MindLyst + referenced in LysnrAI dashboards
|
||||
|
||||
| Location | Format |
|
||||
|----------|--------|
|
||||
| `design-system/tokens/mindlyst.tokens.json` | Canonical JSON |
|
||||
| `shared/src/.../theme/MindLystTokens.kt` | KMP Kotlin object |
|
||||
| `iosApp/MindLystTheme.swift` | SwiftUI structs |
|
||||
| `androidApp/.../MindLystTheme.kt` | Compose theme |
|
||||
| `web/src/styles/globals.css` | CSS custom properties |
|
||||
| `design-system/web/mindlyst.css` | CSS custom properties (duplicate of above) |
|
||||
|
||||
**Note:** LysnrAI's admin/user dashboards use TailwindCSS + shadcn/ui with a different color system currently. If both products converge on a shared design language, the token JSON can be the single source.
|
||||
|
||||
**Proposed shared package:** `@bytelyst/design-tokens`
|
||||
```
|
||||
packages/design-tokens/
|
||||
tokens/
|
||||
colors.json — palette, semantic dark/light, brain gradients
|
||||
typography.json — font families, sizes, weights, line heights
|
||||
spacing.json — 8pt grid scale
|
||||
radius.json — border radius scale
|
||||
motion.json — duration + easing
|
||||
layout.json — breakpoints, gutter, max-width
|
||||
generated/ — auto-generated from tokens JSON
|
||||
tokens.css — CSS custom properties (--ml-*)
|
||||
tokens.ts — TypeScript constants
|
||||
tokens.kt — Kotlin object (for KMP)
|
||||
tokens.swift — Swift structs (for iOS)
|
||||
scripts/
|
||||
generate.ts — reads JSON, outputs all formats
|
||||
package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Potential Future Shared Components
|
||||
|
||||
These are not exact duplicates yet but represent patterns that will diverge if not unified:
|
||||
|
||||
| Component | LysnrAI | MindLyst | Shared Potential |
|
||||
|-----------|---------|----------|------------------|
|
||||
| **OpenAI API client** | Python `src/llm/` + Azure OpenAI | KMP `api/OpenAIClient.kt` | Shared TS client for backend services |
|
||||
| **Whisper/STT client** | Python `src/audio/azure_stt.py` | KMP `api/WhisperClient.kt` | Both need speech-to-text |
|
||||
| **Health check aggregator** | `services/monitoring/health-check.ts` | N/A (could reuse) | Generic multi-service health poller |
|
||||
| **Docker Compose patterns** | Full stack compose | Planned | Shared compose fragments |
|
||||
| **CI workflow templates** | 9 GitHub Actions | 1 CI workflow | Reusable workflow templates |
|
||||
| **Python Cosmos client** | `src/cloud/cosmos_client.py` | N/A | If MindLyst adds a Python backend |
|
||||
|
||||
---
|
||||
|
||||
## 5. Recommended Package Structure
|
||||
|
||||
```
|
||||
learning_ai_common_plat/
|
||||
├── packages/
|
||||
│ ├── cosmos/ — Azure Cosmos DB client singleton + container registry
|
||||
│ ├── errors/ — Typed HTTP service errors (ServiceError hierarchy)
|
||||
│ ├── auth/ — JWT create/verify + bcrypt + Fastify middleware
|
||||
│ ├── fastify-core/ — Server bootstrap, request-id, health check, error handler
|
||||
│ ├── config/ — Zod base env schema + product identity loader
|
||||
│ ├── api-client/ — Typed fetch wrapper for dashboards/service clients
|
||||
│ ├── react-auth/ — AuthProvider + useAuth() for Next.js dashboards
|
||||
│ └── design-tokens/ — Canonical token JSON + generators for CSS/TS/Kotlin/Swift
|
||||
├── docs/
|
||||
│ ├── COMMON_PLATFORM_ANALYSIS.md (this file)
|
||||
│ └── MIGRATION_GUIDE.md (how to adopt in each repo)
|
||||
├── package.json — workspace root (npm/pnpm workspaces)
|
||||
├── tsconfig.base.json — shared TypeScript config
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Migration Priority
|
||||
|
||||
Ordered by **impact × ease**:
|
||||
|
||||
| Priority | Package | Impact | Effort | Reason |
|
||||
|----------|---------|--------|--------|--------|
|
||||
| **P0** | `@bytelyst/errors` | High | Low | Drop-in, no config, 6 consumers |
|
||||
| **P0** | `@bytelyst/cosmos` | High | Low | 6 identical files, most-used utility |
|
||||
| **P1** | `@bytelyst/config` | High | Medium | Eliminates 4 config files + 4 product-config files |
|
||||
| **P1** | `@bytelyst/auth` | High | Medium | 5 copies of JWT logic, security-critical |
|
||||
| **P1** | `@bytelyst/fastify-core` | High | Medium | 4 nearly identical server.ts files (~350 lines saved) |
|
||||
| **P2** | `@bytelyst/api-client` | Medium | Low | 5+ fetch wrappers in dashboards |
|
||||
| **P2** | `@bytelyst/react-auth` | Medium | Medium | 3 auth contexts, saves ~400 lines |
|
||||
| **P3** | `@bytelyst/design-tokens` | Medium | High | Cross-platform token generation pipeline |
|
||||
|
||||
---
|
||||
|
||||
## 7. Consumption Model
|
||||
|
||||
### TypeScript packages (services + dashboards)
|
||||
- Publish as **npm workspace packages** or use **git submodule** + `file:` references
|
||||
- Each consuming repo adds `"@bytelyst/<pkg>": "file:../learning_ai_common_plat/packages/<pkg>"` to `package.json`
|
||||
- Alternatively, publish to a private npm registry (GitHub Packages)
|
||||
|
||||
### Design tokens
|
||||
- `design-tokens` package exports a CLI: `npx @bytelyst/design-tokens generate --format css,ts,kt,swift`
|
||||
- Each consuming repo runs the generator as a build step or commits the generated output
|
||||
|
||||
### KMP (Kotlin Multiplatform)
|
||||
- MindLyst's `MindLystTokens.kt` could be auto-generated from `design-tokens/tokens/*.json`
|
||||
- Add a Gradle task in `shared/build.gradle.kts` that reads the JSON and emits Kotlin
|
||||
|
||||
---
|
||||
|
||||
## 8. Lines of Code Impact
|
||||
|
||||
| Category | Current Duplicated LOC | After Extraction |
|
||||
|----------|----------------------|------------------|
|
||||
| Cosmos client (6 files) | ~306 | ~50 (one shared) |
|
||||
| Error classes (4 files) | ~157 | ~45 (one shared) |
|
||||
| JWT auth (5 files) | ~250 | ~60 (one shared) |
|
||||
| Server bootstrap (4 files) | ~351 | ~30 (per-service config only) |
|
||||
| Config loader (4 files) | ~107 | ~10 (per-service extension only) |
|
||||
| Product config (4 files) | ~35 | 0 (read from shared) |
|
||||
| API fetch utils (5 files) | ~200 | ~30 (one shared) |
|
||||
| Auth context (3 files) | ~450 | ~50 (one shared, configured per-app) |
|
||||
| **Total** | **~1,856 LOC** | **~275 LOC** |
|
||||
|
||||
**Net savings: ~1,580 lines of duplicated code eliminated**, plus single-point maintenance for security-critical auth and database infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## 9. Next Steps
|
||||
|
||||
1. Initialize `learning_ai_common_plat` as a pnpm/npm workspace monorepo
|
||||
2. Start with P0 packages (`errors`, `cosmos`) — lowest risk, highest copy count
|
||||
3. Add tests (port existing service tests that cover these utilities)
|
||||
4. Update LysnrAI services to import from `@bytelyst/*` instead of local `./lib/*`
|
||||
5. Wire MindLyst's Next.js web app to use shared design tokens
|
||||
6. Set up CI to test the common platform packages independently
|
||||
808
docs/ECOSYSTEM_ARCHITECTURE.md
Normal file
808
docs/ECOSYSTEM_ARCHITECTURE.md
Normal file
@ -0,0 +1,808 @@
|
||||
# ByteLyst Ecosystem — Post-Refactor Architecture
|
||||
|
||||
> **Companion diagram:** `ecosystem-after-refactor.drawio` (open in draw.io or VS Code draw.io extension)
|
||||
>
|
||||
> **Date:** 2026-02-12 · **Author:** Cascade
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Ecosystem Overview](#1-ecosystem-overview)
|
||||
2. [Three-Repo Structure](#2-three-repo-structure)
|
||||
3. [Common Platform Packages — Detailed](#3-common-platform-packages--detailed)
|
||||
4. [Component & Service Inventory](#4-component--service-inventory)
|
||||
5. [Dependency Graph](#5-dependency-graph)
|
||||
6. [Migration Plan](#6-migration-plan)
|
||||
7. [Advantages](#7-advantages)
|
||||
8. [Cautions & Risks](#8-cautions--risks)
|
||||
9. [Versioning Strategy](#9-versioning-strategy)
|
||||
10. [Testing Strategy](#10-testing-strategy)
|
||||
11. [CI/CD Impact](#11-cicd-impact)
|
||||
12. [Decision Log](#12-decision-log)
|
||||
|
||||
---
|
||||
|
||||
## 1. Ecosystem Overview
|
||||
|
||||
The ByteLyst ecosystem consists of **two product repos** and **one shared infrastructure repo**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CLIENT APPLICATIONS │
|
||||
│ LysnrAI Desktop · iOS · Android · Admin · User Portal · Tracker │
|
||||
│ MindLyst iOS · Android · Web │
|
||||
└───────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────┐ ┌────────┴────────┐ ┌──────────────────────────────┐
|
||||
│ LysnrAI Repo │ │ Common Platform │ │ MindLyst Repo │
|
||||
│ (voice_ai_agent)│←─│ (common_plat) │─→│ (multimodal_memory_agents) │
|
||||
│ │ │ @bytelyst/* │ │ │
|
||||
│ 4 Fastify svcs │ │ 8 npm packages │ │ KMP shared module │
|
||||
│ 3 Next.js apps │ │ │ │ 3 native apps │
|
||||
│ 1 FastAPI │ │ Design tokens │ │ 1 Next.js web │
|
||||
│ 1 Desktop app │ │ (JSON → all) │ │ design-system/ │
|
||||
│ 2 Mobile apps │ │ │ │ │
|
||||
└────────┬─────────┘ └─────────────────┘ └──────────────┬───────────────┘
|
||||
│ │
|
||||
┌────────┴─────────────────────────────────────────────────┴───────────────┐
|
||||
│ AZURE CLOUD INFRASTRUCTURE │
|
||||
│ Cosmos DB · Blob Storage · Key Vault · Speech · OpenAI · Stripe │
|
||||
│ Docker Compose (Traefik · Loki · Grafana) · GitHub Actions │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Three-Repo Structure
|
||||
|
||||
### 2.1 learning_voice_ai_agent (LysnrAI)
|
||||
|
||||
**Purpose:** Cross-platform voice-to-text dictation platform.
|
||||
|
||||
| Component | Tech | Port | Description |
|
||||
|-----------|------|------|-------------|
|
||||
| Desktop App | Python 3.12, tkinter | — | macOS/Windows dictation app |
|
||||
| FastAPI Backend | Python 3.12 | 8000 | REST API, cloud sync |
|
||||
| platform-service | Fastify + TS | 4003 | Auth, audit, flags, notifications, blob, rate limiting |
|
||||
| billing-service | Fastify + TS | 4002 | Subscriptions, plans, usage, licenses, Stripe |
|
||||
| growth-service | Fastify + TS | 4001 | Invitations, referrals, promo codes |
|
||||
| tracker-service | Fastify + TS | 4004 | Feature requests, bugs, votes, public roadmap |
|
||||
| admin-dashboard-web | Next.js 16 | 3001 | Admin panel (users, tokens, audit, themes) |
|
||||
| user-dashboard-web | Next.js 16 | 3002 | User portal (profile, billing, settings, SSO) |
|
||||
| tracker-dashboard-web | Next.js | 3003 | Tracker board, kanban, public roadmap |
|
||||
| iOS App | Swift + SwiftUI | — | Native (no KMP) |
|
||||
| Android App | Kotlin + Compose | — | Native (no KMP) |
|
||||
| Monitoring | Loki + Grafana | — | Health checks, logs, metrics |
|
||||
|
||||
### 2.2 learning_multimodal_memory_agents (MindLyst)
|
||||
|
||||
**Purpose:** Role-based Life OS — multimodal second-brain with AI triage.
|
||||
|
||||
| Component | Tech | Description |
|
||||
|-----------|------|-------------|
|
||||
| KMP Shared Module | Kotlin Multiplatform | Models, repositories, DI, triage pipeline, API clients |
|
||||
| iOS App | SwiftUI + KMP | Native UI consuming shared Kotlin module |
|
||||
| Android App | Jetpack Compose + KMP | Native UI consuming shared Kotlin module |
|
||||
| Web Dashboard | Next.js 14 | Pages Router, CSS custom properties, landing + dashboard |
|
||||
| design-system/ | JSON + CSS | Canonical tokens, component specs |
|
||||
|
||||
### 2.3 learning_ai_common_plat (Common Platform)
|
||||
|
||||
**Purpose:** Shared npm packages consumed by both product repos.
|
||||
|
||||
| Package | Type | Primary Consumers |
|
||||
|---------|------|-------------------|
|
||||
| `@bytelyst/errors` | TypeScript | All 4 Fastify services, dashboards |
|
||||
| `@bytelyst/cosmos` | TypeScript | All 4 Fastify services, 2 Next.js dashboards |
|
||||
| `@bytelyst/config` | TypeScript | All 4 Fastify services |
|
||||
| `@bytelyst/auth` | TypeScript | All services + dashboards with JWT |
|
||||
| `@bytelyst/fastify-core` | TypeScript | All 4 Fastify services |
|
||||
| `@bytelyst/api-client` | TypeScript | All 3 Next.js dashboards |
|
||||
| `@bytelyst/react-auth` | TypeScript/React | All 3 Next.js dashboards |
|
||||
| `@bytelyst/design-tokens` | JSON → multi-format | MindLyst (all platforms), potentially LysnrAI dashboards |
|
||||
|
||||
---
|
||||
|
||||
## 3. Common Platform Packages — Detailed
|
||||
|
||||
### 3.1 `@bytelyst/errors` (P0)
|
||||
|
||||
**What it replaces:** 4 separate `errors.ts` files across services (157 LOC total).
|
||||
|
||||
```typescript
|
||||
// Unified error hierarchy
|
||||
export class ServiceError extends Error {
|
||||
constructor(public statusCode: number, message: string) { ... }
|
||||
}
|
||||
export class NotFoundError extends ServiceError { /* 404 */ }
|
||||
export class BadRequestError extends ServiceError { /* 400 */ }
|
||||
export class UnauthorizedError extends ServiceError { /* 401 */ }
|
||||
export class ForbiddenError extends ServiceError { /* 403 */ }
|
||||
export class ConflictError extends ServiceError { /* 409 */ }
|
||||
export class TooManyRequestsError extends ServiceError { /* 429 */ }
|
||||
```
|
||||
|
||||
**Design decision:** Superset of all error types currently used. Services import only what they need. The `details` property (used by billing-service's TooManyRequests) is added to the base class as optional.
|
||||
|
||||
**Dependencies:** None (leaf package).
|
||||
|
||||
---
|
||||
|
||||
### 3.2 `@bytelyst/cosmos` (P0)
|
||||
|
||||
**What it replaces:** 6 separate `cosmos.ts` files (306 LOC total).
|
||||
|
||||
```typescript
|
||||
// Core exports
|
||||
export function getCosmosClient(): CosmosClient;
|
||||
export function getDatabase(dbName?: string): Database;
|
||||
export function getContainer(name: string): Container;
|
||||
|
||||
// Container registry (dashboards use this)
|
||||
export function registerContainers(config: Record<string, ContainerConfig>): void;
|
||||
export function initializeAllContainers(): Promise<void>;
|
||||
|
||||
// Types
|
||||
export interface ContainerConfig {
|
||||
partitionKeyPath: string;
|
||||
defaultTtl?: number | null;
|
||||
}
|
||||
```
|
||||
|
||||
**Design decision:** The simple `getContainer(name)` used by microservices works alongside the full registry used by dashboards. Services call `getContainer()` directly; dashboards call `registerContainers()` first to set up the partition key map.
|
||||
|
||||
**Dependencies:** `@azure/cosmos` (peer dependency).
|
||||
|
||||
---
|
||||
|
||||
### 3.3 `@bytelyst/config` (P1)
|
||||
|
||||
**What it replaces:** 4 separate `config.ts` + 4 separate `product-config.ts` (142 LOC total).
|
||||
|
||||
```typescript
|
||||
// Base env schema — every Fastify service needs these
|
||||
export const baseEnvSchema = z.object({
|
||||
PORT: z.coerce.number().default(3000),
|
||||
HOST: z.string().default("0.0.0.0"),
|
||||
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
||||
CORS_ORIGIN: z.string().optional(),
|
||||
SERVICE_NAME: z.string(),
|
||||
COSMOS_ENDPOINT: z.string().min(1),
|
||||
COSMOS_KEY: z.string().min(1),
|
||||
COSMOS_DATABASE: z.string().default("lysnrai"),
|
||||
});
|
||||
|
||||
// Extend per-service
|
||||
export function loadConfig<T extends z.ZodRawShape>(extension: T) {
|
||||
return baseEnvSchema.extend(extension).parse(process.env);
|
||||
}
|
||||
|
||||
// Product identity
|
||||
export function loadProductIdentity(path?: string): ProductIdentity;
|
||||
export interface ProductIdentity {
|
||||
productId: string;
|
||||
displayName: string;
|
||||
licensePrefix: string;
|
||||
configDirName: string;
|
||||
envVarPrefix: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Design decision:** Each service calls `loadConfig({ STRIPE_SECRET_KEY: z.string(), ... })` to extend the base. The product identity reads from `shared/product.json` or falls back to env vars, eliminating the hardcoded `product-config.ts` copies.
|
||||
|
||||
**Dependencies:** `zod` (peer dependency).
|
||||
|
||||
---
|
||||
|
||||
### 3.4 `@bytelyst/auth` (P1)
|
||||
|
||||
**What it replaces:** 5 separate JWT/auth files (250 LOC total).
|
||||
|
||||
```typescript
|
||||
// JWT operations (configurable issuer + expiry)
|
||||
export function createJwtUtils(options: {
|
||||
issuer: string;
|
||||
accessTokenExpiry?: string; // default "1h"
|
||||
refreshTokenExpiry?: string; // default "30d"
|
||||
}): {
|
||||
createAccessToken(payload: TokenPayload): Promise<string>;
|
||||
createRefreshToken(payload: { sub: string }): Promise<string>;
|
||||
verifyToken(token: string): Promise<TokenPayload | null>;
|
||||
};
|
||||
|
||||
// Fastify middleware
|
||||
export function extractAuth(req: FastifyRequest): Promise<AuthPayload>;
|
||||
export function requireRole(...roles: string[]): FastifyHook;
|
||||
|
||||
// Next.js server-side
|
||||
export function getCurrentUser(authHeader: string | null, getUserById: Function): Promise<UserDoc | null>;
|
||||
|
||||
// Password hashing
|
||||
export function hashPassword(plain: string): Promise<string>;
|
||||
export function verifyPassword(plain: string, hash: string): Promise<boolean>;
|
||||
|
||||
// Types
|
||||
export interface AuthPayload { sub: string; email?: string; role?: string; productId?: string; type?: string; }
|
||||
export interface TokenPayload { sub: string; email: string; role: string; type: "access" | "refresh"; }
|
||||
```
|
||||
|
||||
**Design decision:** The `createJwtUtils()` factory pattern allows each service to configure its own issuer (`lysnrai-admin`, `lysnrai-user`, `lysnrai`) while sharing all the logic. Platform-service creates tokens; other services only verify. The `requireRole()` hook is a new addition that multiple services currently implement inline.
|
||||
|
||||
**Dependencies:** `jose`, `bcryptjs` (peer dependencies).
|
||||
|
||||
---
|
||||
|
||||
### 3.5 `@bytelyst/fastify-core` (P1)
|
||||
|
||||
**What it replaces:** 4 separate `server.ts` files (351 LOC total).
|
||||
|
||||
```typescript
|
||||
export async function createServiceApp(options: {
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
port?: number;
|
||||
corsOrigin?: string;
|
||||
}): Promise<FastifyInstance> {
|
||||
const app = Fastify({ logger: true });
|
||||
|
||||
// CORS
|
||||
await app.register(cors, { origin: ... });
|
||||
|
||||
// OpenAPI / Swagger
|
||||
await app.register(swagger, { openapi: { info: { title, version, description } } });
|
||||
|
||||
// Prometheus metrics
|
||||
await app.register(metricsPlugin, { endpoint: "/metrics" });
|
||||
|
||||
// x-request-id propagation
|
||||
app.addHook("onRequest", requestIdHook);
|
||||
|
||||
// Health endpoint
|
||||
app.get("/health", healthHandler(options.name, options.version));
|
||||
|
||||
// ServiceError-aware error handler
|
||||
app.setErrorHandler(serviceErrorHandler);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
export async function startService(app: FastifyInstance, port: number, host?: string): Promise<void>;
|
||||
```
|
||||
|
||||
**Design decision:** After calling `createServiceApp()`, each service just registers its route modules and calls `startService()`. The server.ts in each service shrinks from ~85 lines to ~15 lines. The `healthHandler` returns the standard `{ status, service, version, timestamp, requestId }` shape that the monitoring health-check script expects.
|
||||
|
||||
**Dependencies:** `fastify`, `@fastify/cors`, `@fastify/swagger`, `fastify-metrics`, `@bytelyst/errors`, `@bytelyst/config` (peer dependencies).
|
||||
|
||||
---
|
||||
|
||||
### 3.6 `@bytelyst/api-client` (P2)
|
||||
|
||||
**What it replaces:** 5+ separate fetch wrapper implementations (200 LOC total).
|
||||
|
||||
```typescript
|
||||
export function createApiClient(options: {
|
||||
baseUrl: string;
|
||||
getToken?: () => string | null;
|
||||
defaultHeaders?: Record<string, string>;
|
||||
}): {
|
||||
// Throws on error (for service-to-service)
|
||||
fetch<T>(path: string, options?: RequestInit): Promise<T>;
|
||||
// Returns { data, error } tuple (for UI components)
|
||||
safeFetch<T>(path: string, options?: RequestInit): Promise<{ data: T | null; error: string | null }>;
|
||||
};
|
||||
```
|
||||
|
||||
**Design decision:** Two fetch modes: throwing (used by server-side service clients like `billing-client.ts`, `growth-client.ts`) and safe (used by React components via `api.ts`). The `getToken()` callback handles auth header injection without coupling to any specific storage mechanism.
|
||||
|
||||
**Dependencies:** None (uses native `fetch`).
|
||||
|
||||
---
|
||||
|
||||
### 3.7 `@bytelyst/react-auth` (P2)
|
||||
|
||||
**What it replaces:** 3 separate `auth-context.tsx` files (450 LOC total).
|
||||
|
||||
```typescript
|
||||
export function createAuthProvider<TUser extends BaseUser>(config: {
|
||||
storagePrefix: string; // "admin" | "portal" | "tracker"
|
||||
loginEndpoint: string; // "/api/auth/login"
|
||||
mapResponseToUser: (data: any) => TUser;
|
||||
enableSSO?: boolean;
|
||||
}): {
|
||||
AuthProvider: React.FC<{ children: ReactNode }>;
|
||||
useAuth: () => AuthContextValue<TUser>;
|
||||
};
|
||||
|
||||
export interface BaseUser {
|
||||
id?: string;
|
||||
email: string;
|
||||
name: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface AuthContextValue<TUser> {
|
||||
user: TUser | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
login: (email: string, password: string) => Promise<boolean>;
|
||||
logout: () => void;
|
||||
getAccessToken: () => string | null;
|
||||
}
|
||||
```
|
||||
|
||||
**Design decision:** Factory pattern produces a fully-typed `AuthProvider` + `useAuth()` pair. Each dashboard provides its own `TUser` type and storage prefix. SSO cookie support (used by user-dashboard) is opt-in via `enableSSO`. Registration support (also user-dashboard only) can be added as an optional config callback.
|
||||
|
||||
**Dependencies:** `react` (peer dependency), `@bytelyst/api-client`.
|
||||
|
||||
---
|
||||
|
||||
### 3.8 `@bytelyst/design-tokens` (P3)
|
||||
|
||||
**What it replaces:** 5 manually-synced token files across MindLyst.
|
||||
|
||||
```
|
||||
packages/design-tokens/
|
||||
├── tokens/
|
||||
│ └── bytelyst.tokens.json ← SINGLE SOURCE OF TRUTH
|
||||
├── generated/ ← Auto-generated (committed or gitignored)
|
||||
│ ├── tokens.css ← CSS custom properties (--ml-*)
|
||||
│ ├── tokens.ts ← TypeScript constants
|
||||
│ ├── tokens.kt ← Kotlin object (MindLystTokens)
|
||||
│ └── tokens.swift ← Swift structs (MindLystColors/Spacing/Radius)
|
||||
├── scripts/
|
||||
│ └── generate.ts ← Reads JSON, emits all formats
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Design decision:** The canonical `bytelyst.tokens.json` (migrated from MindLyst's existing `mindlyst.tokens.json`) is the single source. A generation script produces platform-specific files. Consumers either:
|
||||
- **Import the generated file** directly (CSS, TS)
|
||||
- **Copy the generated file** into their project as a build step (Kotlin, Swift)
|
||||
|
||||
This eliminates the manual sync problem where token changes require updating 5 files.
|
||||
|
||||
**Dependencies:** None at runtime; `typescript` for the generation script.
|
||||
|
||||
---
|
||||
|
||||
## 4. Component & Service Inventory
|
||||
|
||||
### 4.1 All Services (Post-Refactor)
|
||||
|
||||
| Service | Repo | Port | Shared Packages Used |
|
||||
|---------|------|------|---------------------|
|
||||
| platform-service | LysnrAI | 4003 | errors, cosmos, config, auth, fastify-core |
|
||||
| billing-service | LysnrAI | 4002 | errors, cosmos, config, fastify-core |
|
||||
| growth-service | LysnrAI | 4001 | errors, cosmos, config, fastify-core |
|
||||
| tracker-service | LysnrAI | 4004 | errors, cosmos, config, auth, fastify-core |
|
||||
| FastAPI Backend | LysnrAI | 8000 | (Python — separate concern) |
|
||||
| admin-dashboard | LysnrAI | 3001 | cosmos, auth, api-client, react-auth |
|
||||
| user-dashboard | LysnrAI | 3002 | cosmos, auth, api-client, react-auth |
|
||||
| tracker-dashboard | LysnrAI | 3003 | api-client, react-auth |
|
||||
| MindLyst Web | MindLyst | 3050 | design-tokens |
|
||||
| MindLyst iOS | MindLyst | — | design-tokens (generated Swift) |
|
||||
| MindLyst Android | MindLyst | — | design-tokens (generated Kotlin) |
|
||||
|
||||
### 4.2 Azure Infrastructure
|
||||
|
||||
| Service | Resource | Used By |
|
||||
|---------|----------|---------|
|
||||
| Cosmos DB | cosmos-mywisprai (Serverless) | All TS services + dashboards |
|
||||
| Blob Storage | bytelystblobs (6 containers) | platform-service, desktop, backend |
|
||||
| Key Vault | kv-mywisprai | Desktop, mobile, backend |
|
||||
| Speech | mywisprai-speech (F0) | Desktop, LysnrAI mobile |
|
||||
| Azure OpenAI | mywisprai-openai-sweden (S0) | Desktop, backend |
|
||||
| OpenAI API | api.openai.com | MindLyst KMP (triage, Whisper) |
|
||||
| Stripe | Payments API | billing-service, growth-service |
|
||||
|
||||
### 4.3 DevOps Components
|
||||
|
||||
| Component | Location | Scope |
|
||||
|-----------|----------|-------|
|
||||
| Docker Compose | LysnrAI root | All services + Loki + Grafana + Traefik |
|
||||
| Health Check | services/monitoring/ | Aggregates all /health endpoints |
|
||||
| GitHub Actions | .github/workflows/ (9 files) | Per-service CI |
|
||||
| run-local-all-services.sh | LysnrAI root | Dev startup script |
|
||||
|
||||
---
|
||||
|
||||
## 5. Dependency Graph
|
||||
|
||||
```
|
||||
@bytelyst/design-tokens (standalone — no deps)
|
||||
│
|
||||
└─→ MindLyst iOS/Android/Web
|
||||
|
||||
@bytelyst/errors (standalone — no deps)
|
||||
│
|
||||
├─→ @bytelyst/fastify-core
|
||||
├─→ All 4 Fastify services
|
||||
└─→ All 3 Next.js dashboards (via API routes)
|
||||
|
||||
@bytelyst/cosmos (depends on: @azure/cosmos)
|
||||
│
|
||||
├─→ All 4 Fastify services
|
||||
└─→ admin-dashboard, user-dashboard
|
||||
|
||||
@bytelyst/config (depends on: zod)
|
||||
│
|
||||
├─→ @bytelyst/fastify-core
|
||||
├─→ @bytelyst/auth
|
||||
└─→ All 4 Fastify services
|
||||
|
||||
@bytelyst/auth (depends on: jose, bcryptjs, @bytelyst/config)
|
||||
│
|
||||
├─→ @bytelyst/api-client
|
||||
├─→ platform-service (creates tokens)
|
||||
├─→ tracker-service (verifies tokens)
|
||||
└─→ admin/user dashboards (server-side auth)
|
||||
|
||||
@bytelyst/fastify-core (depends on: fastify, @bytelyst/errors, @bytelyst/config)
|
||||
│
|
||||
└─→ All 4 Fastify services
|
||||
|
||||
@bytelyst/api-client (no runtime deps)
|
||||
│
|
||||
├─→ @bytelyst/react-auth
|
||||
└─→ All 3 dashboards
|
||||
|
||||
@bytelyst/react-auth (depends on: react, @bytelyst/api-client)
|
||||
│
|
||||
└─→ All 3 Next.js dashboards
|
||||
```
|
||||
|
||||
**Key insight:** The graph is a clean DAG (directed acyclic graph) with no circular dependencies. Leaf packages (`errors`, `cosmos`, `design-tokens`) can be extracted first with zero risk.
|
||||
|
||||
---
|
||||
|
||||
## 6. Migration Plan
|
||||
|
||||
### Phase 1: Foundation (Week 1)
|
||||
|
||||
1. **Initialize `learning_ai_common_plat`** as pnpm workspace monorepo
|
||||
- `pnpm init`, `pnpm-workspace.yaml`, `tsconfig.base.json`
|
||||
- Shared `vitest` config, `ruff.toml` (for future Python packages)
|
||||
|
||||
2. **Extract `@bytelyst/errors`** (P0, 1 hour)
|
||||
- Copy `platform-service/src/lib/errors.ts` as starting point
|
||||
- Add `ConflictError` (from tracker) and `TooManyRequestsError` (from billing)
|
||||
- Add optional `details` field to base `ServiceError`
|
||||
- Write tests (port from service tests)
|
||||
- Update all 4 services: `import { ServiceError, ... } from "@bytelyst/errors"`
|
||||
|
||||
3. **Extract `@bytelyst/cosmos`** (P0, 2 hours)
|
||||
- Merge service version (simple) + dashboard version (registry + TTL)
|
||||
- Parameterize default database name
|
||||
- Write tests (mock Cosmos client)
|
||||
- Update all 6 consumers
|
||||
|
||||
### Phase 2: Service Infrastructure (Week 2)
|
||||
|
||||
4. **Extract `@bytelyst/config`** (P1, 2 hours)
|
||||
- Create `baseEnvSchema` from common fields
|
||||
- Add `loadConfig()` extension function
|
||||
- Add `loadProductIdentity()` from `shared/product.json`
|
||||
- Update all 4 services + remove 4 `product-config.ts` files
|
||||
|
||||
5. **Extract `@bytelyst/auth`** (P1, 3 hours)
|
||||
- Create `createJwtUtils()` factory
|
||||
- Port `extractAuth()` Fastify hook
|
||||
- Port `getCurrentUser()` for Next.js
|
||||
- Port `hashPassword()` / `verifyPassword()`
|
||||
- Critical: test all auth flows end-to-end after migration
|
||||
|
||||
6. **Extract `@bytelyst/fastify-core`** (P1, 3 hours)
|
||||
- Create `createServiceApp()` factory
|
||||
- Port request-id hook, health handler, error handler
|
||||
- Refactor all 4 `server.ts` files to use factory
|
||||
- Verify Docker builds still pass
|
||||
|
||||
### Phase 3: Dashboard Libraries (Week 3)
|
||||
|
||||
7. **Extract `@bytelyst/api-client`** (P2, 2 hours)
|
||||
- Create `createApiClient()` with both fetch modes
|
||||
- Update dashboard client files to use shared client
|
||||
- Service-specific methods stay in their respective client files
|
||||
|
||||
8. **Extract `@bytelyst/react-auth`** (P2, 3 hours)
|
||||
- Create `createAuthProvider()` factory
|
||||
- Port SSO cookie logic as opt-in
|
||||
- Update all 3 dashboard auth contexts
|
||||
- Test login/logout flows in each dashboard
|
||||
|
||||
### Phase 4: Design System (Week 4+)
|
||||
|
||||
9. **Extract `@bytelyst/design-tokens`** (P3, 4 hours)
|
||||
- Migrate `mindlyst.tokens.json` as canonical source
|
||||
- Write generation script for CSS, TS, Kotlin, Swift
|
||||
- Add to MindLyst build pipeline
|
||||
- Consider adopting for LysnrAI dashboards (future)
|
||||
|
||||
---
|
||||
|
||||
## 7. Advantages
|
||||
|
||||
### 7.1 Immediate Benefits
|
||||
|
||||
| Benefit | Impact | Measurement |
|
||||
|---------|--------|-------------|
|
||||
| **~1,580 LOC eliminated** | Less code to maintain | 8 fewer files per service feature |
|
||||
| **Single-point security fixes** | JWT/auth vulnerability fixed once, applied everywhere | 1 PR instead of 5 |
|
||||
| **Consistent error handling** | All services return identical error shapes | 0 divergence |
|
||||
| **Faster new service creation** | `createServiceApp()` → 15 lines to launch | Minutes vs hours |
|
||||
| **Cosmos DB config in one place** | Container registry + TTL managed centrally | 1 file vs 6 |
|
||||
|
||||
### 7.2 Medium-Term Benefits
|
||||
|
||||
| Benefit | Impact |
|
||||
|---------|--------|
|
||||
| **MindLyst backend bootstrap** | When MindLyst adds backend services, it gets Fastify infra for free |
|
||||
| **Consistent design language** | Token generation ensures iOS/Android/Web never drift |
|
||||
| **Cross-product features** | Tracker service already product-agnostic; shared auth enables MindLyst to use it |
|
||||
| **Onboarding speed** | New developer learns one pattern, applies to all services |
|
||||
| **Testability** | Shared packages have isolated tests; consumer tests focus on business logic |
|
||||
|
||||
### 7.3 Architectural Benefits
|
||||
|
||||
| Benefit | Description |
|
||||
|---------|-------------|
|
||||
| **Separation of concerns** | Infrastructure (how) separated from business logic (what) |
|
||||
| **Dependency inversion** | Services depend on abstractions (`@bytelyst/auth`) not implementations |
|
||||
| **DRY principle** | No more copy-paste-modify cycle for new services |
|
||||
| **Contract stability** | Shared types ensure API contracts don't drift between services |
|
||||
| **Upgrade path** | Update `jose` or `@azure/cosmos` in one place |
|
||||
|
||||
---
|
||||
|
||||
## 8. Cautions & Risks
|
||||
|
||||
### 8.1 HIGH RISK — Tightly Coupled Releases
|
||||
|
||||
| Risk | Description | Mitigation |
|
||||
|------|-------------|------------|
|
||||
| **Breaking change cascade** | A breaking change in `@bytelyst/auth` breaks all services simultaneously | Semantic versioning + `file:` references pin to exact commits. Run all consumer tests before publishing. |
|
||||
| **Diamond dependency** | Two packages depend on different versions of a shared dep | Use peer dependencies + strict version ranges. `pnpm` handles this well. |
|
||||
| **Deployment ordering** | Must deploy common-plat before consuming repos can use new features | Use git tags/releases. Consumers reference a specific version or commit. |
|
||||
|
||||
### 8.2 MEDIUM RISK — Development Friction
|
||||
|
||||
| Risk | Description | Mitigation |
|
||||
|------|-------------|------------|
|
||||
| **Cross-repo PRs** | A feature spanning common-plat + LysnrAI requires 2 PRs | Use `file:../learning_ai_common_plat/packages/X` in dev, pin to version in CI. |
|
||||
| **Local dev setup** | Developers need all 3 repos cloned side-by-side | Document in README. Add a setup script. Consider git submodules as alternative. |
|
||||
| **IDE navigation** | "Go to definition" may not work across repos | TypeScript project references or workspace-level `tsconfig.json` can help. |
|
||||
| **Over-abstraction** | Temptation to extract things that aren't truly shared | Rule: **only extract when ≥2 consumers exist AND the code is >90% identical**. |
|
||||
|
||||
### 8.3 LOW RISK — Operational
|
||||
|
||||
| Risk | Description | Mitigation |
|
||||
|------|-------------|------------|
|
||||
| **npm registry dependency** | Publishing to GitHub Packages adds infra complexity | Start with `file:` references (zero infra). Upgrade to registry only if team grows. |
|
||||
| **Test isolation** | Shared package tests may not catch consumer-specific issues | Each consumer repo keeps integration tests. Shared packages only unit-test their own API. |
|
||||
| **Python exclusion** | The desktop app and FastAPI backend can't use TypeScript packages | Python remains independent. If patterns emerge (e.g., Cosmos client), create a Python equivalent later. |
|
||||
| **KMP exclusion** | MindLyst's Kotlin code can't use npm packages directly | Only `design-tokens` applies to KMP (via generated Kotlin). All other packages are TS-only. |
|
||||
|
||||
### 8.4 Things to AVOID
|
||||
|
||||
1. **Don't extract too early** — Wait until code is stable and duplicated in ≥2 places
|
||||
2. **Don't create a "utils" dumping ground** — Every package must have a clear, single responsibility
|
||||
3. **Don't abstract configuration** — Each service's `config.ts` should remain in the service; only the base schema is shared
|
||||
4. **Don't share React components** — UI components are product-specific; only infrastructure (auth, fetch) is shared
|
||||
5. **Don't break existing imports in one big PR** — Migrate one package at a time, keep old imports working temporarily
|
||||
|
||||
---
|
||||
|
||||
## 9. Versioning Strategy
|
||||
|
||||
### Recommended: File References (Phase 1)
|
||||
|
||||
```jsonc
|
||||
// In LysnrAI's services/platform-service/package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"@bytelyst/errors": "file:../../../learning_ai_common_plat/packages/errors",
|
||||
"@bytelyst/cosmos": "file:../../../learning_ai_common_plat/packages/cosmos"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:** Zero infrastructure, works locally, `pnpm install` resolves it.
|
||||
**Cons:** All repos must be cloned side-by-side. Docker builds need a multi-stage approach or volume mount.
|
||||
|
||||
### Future: GitHub Packages (Phase 2+)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"dependencies": {
|
||||
"@bytelyst/errors": "^1.0.0",
|
||||
"@bytelyst/cosmos": "^1.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:** True version pinning, CI-friendly, no local path dependency.
|
||||
**Cons:** Requires GitHub Packages setup, publish workflow, access tokens.
|
||||
|
||||
### Versioning Rules
|
||||
|
||||
- **MAJOR** (1.0 → 2.0): Breaking change to public API (renamed export, removed function, changed return type)
|
||||
- **MINOR** (1.0 → 1.1): New export, new optional parameter, new error subclass
|
||||
- **PATCH** (1.0.0 → 1.0.1): Bug fix, internal refactor, dependency update
|
||||
|
||||
---
|
||||
|
||||
## 10. Testing Strategy
|
||||
|
||||
### Package-Level Tests
|
||||
|
||||
Each `@bytelyst/*` package has its own `vitest` test suite:
|
||||
|
||||
| Package | Test Focus |
|
||||
|---------|-----------|
|
||||
| errors | Error class hierarchy, statusCode correctness, instanceof checks |
|
||||
| cosmos | Client singleton behavior, container registry, env var reading (mocked) |
|
||||
| config | Zod schema validation, defaults, extension merging |
|
||||
| auth | JWT sign/verify round-trip, expiry, issuer validation, bcrypt hashing |
|
||||
| fastify-core | App factory produces correct hooks/routes, health endpoint shape |
|
||||
| api-client | Fetch wrapper behavior with mock server, error handling, auth injection |
|
||||
| react-auth | Provider renders, login/logout flows, localStorage persistence |
|
||||
| design-tokens | Generated output matches expected format for each platform |
|
||||
|
||||
### Consumer-Level Tests
|
||||
|
||||
Each service/dashboard keeps its existing test suite. After migration:
|
||||
- Service tests verify that imported shared functions work correctly **in context**
|
||||
- No need to re-test JWT internals — that's the package's job
|
||||
- Focus on **integration**: "Does my route handler correctly call `extractAuth()` and get the right payload?"
|
||||
|
||||
### CI Matrix
|
||||
|
||||
```
|
||||
common-plat CI:
|
||||
→ Run all 8 package test suites
|
||||
→ Run type-check (tsc --noEmit)
|
||||
→ Run lint (ruff for future Python packages)
|
||||
|
||||
LysnrAI CI (per-service):
|
||||
→ Install common-plat packages (file: or registry)
|
||||
→ Run service tests
|
||||
→ Run Next.js build
|
||||
|
||||
MindLyst CI:
|
||||
→ Build KMP shared module
|
||||
→ Build Next.js web
|
||||
→ Verify generated tokens match source JSON
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. CI/CD Impact
|
||||
|
||||
### Docker Build Changes
|
||||
|
||||
Services currently build independently. After extraction:
|
||||
|
||||
**Option A: Multi-stage with copy** (recommended for `file:` references)
|
||||
```dockerfile
|
||||
# Copy common-plat packages into build context
|
||||
COPY ../learning_ai_common_plat/packages/errors /common/errors
|
||||
COPY ../learning_ai_common_plat/packages/cosmos /common/cosmos
|
||||
# Adjust package.json to point to /common/*
|
||||
```
|
||||
|
||||
**Option B: Pre-publish** (recommended for registry)
|
||||
```dockerfile
|
||||
# Common-plat packages are already on npm registry
|
||||
RUN npm install # resolves @bytelyst/* from GitHub Packages
|
||||
```
|
||||
|
||||
**Option C: Monorepo build context** (if repos are merged or submoduled)
|
||||
```dockerfile
|
||||
# Build context includes all repos
|
||||
COPY . /workspace
|
||||
WORKDIR /workspace/learning_voice_ai_agent/services/platform-service
|
||||
RUN npm install && npm run build
|
||||
```
|
||||
|
||||
### docker-compose.yml Changes
|
||||
|
||||
The existing `docker-compose.yml` may need an additional volume mount or build context adjustment:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
platform-service:
|
||||
build:
|
||||
context: . # May need to widen to parent directory
|
||||
dockerfile: services/platform-service/Dockerfile
|
||||
```
|
||||
|
||||
### GitHub Actions Changes
|
||||
|
||||
- **New workflow:** `ci-common-plat.yml` — tests all shared packages on push to `learning_ai_common_plat`
|
||||
- **Modified workflows:** Each service CI installs common-plat packages before running tests
|
||||
- **Potential:** A "consumer test" job in `ci-common-plat.yml` that runs key consumer tests after package changes
|
||||
|
||||
---
|
||||
|
||||
## 12. Decision Log
|
||||
|
||||
| Decision | Rationale | Alternatives Considered |
|
||||
|----------|-----------|------------------------|
|
||||
| **pnpm workspace monorepo** for common-plat | Simplest multi-package management, strict dep resolution | npm workspaces (less strict), Turborepo (overkill for 8 packages), Nx (too heavy) |
|
||||
| **`file:` references** initially | Zero infrastructure, works immediately | git submodules (complex merge), npm registry (needs setup), copy-paste (defeats purpose) |
|
||||
| **Factory pattern** for fastify-core, auth, react-auth | Services need slightly different config (port, issuer, user type) but identical boilerplate | Inheritance (fragile), config objects (less type-safe), template codegen (complex) |
|
||||
| **Peer dependencies** for heavy libs | Prevents version conflicts (e.g., two versions of `@azure/cosmos` in one service) | Direct deps (risk duplicate bundles), optional deps (too loose) |
|
||||
| **TypeScript-only** packages (no Python) | Python desktop/backend have less duplication and different patterns | Shared Python package (only 2 consumers, not worth the packaging complexity yet) |
|
||||
| **Design tokens as JSON → generated code** | JSON is the universal format; generation ensures consistency | Manual sync (error-prone), Figma API (adds dependency), CSS-only (excludes native) |
|
||||
| **NOT extracting React UI components** | UI is product-specific; only infrastructure is truly shared | Shared component library (premature — products have different UX) |
|
||||
| **NOT merging repos** | Three distinct products with different release cadences and teams | Monorepo (coupling), git submodules (merge conflicts) |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: File Counts Per Service (Before vs After)
|
||||
|
||||
### Before Refactor
|
||||
|
||||
```
|
||||
services/platform-service/src/lib/
|
||||
├── config.ts (32 lines)
|
||||
├── cosmos.ts (25 lines)
|
||||
├── errors.ts (38 lines)
|
||||
├── product-config.ts (9 lines)
|
||||
└── blob.ts (service-specific, stays)
|
||||
|
||||
services/billing-service/src/lib/
|
||||
├── config.ts (34 lines) ← DUPLICATE
|
||||
├── cosmos.ts (25 lines) ← DUPLICATE
|
||||
├── errors.ts (41 lines) ← DUPLICATE
|
||||
└── product-config.ts (9 lines) ← DUPLICATE
|
||||
|
||||
services/growth-service/src/lib/
|
||||
├── config.ts (25 lines) ← DUPLICATE
|
||||
├── cosmos.ts (25 lines) ← DUPLICATE
|
||||
├── errors.ts (35 lines) ← DUPLICATE
|
||||
└── product-config.ts (11 lines) ← DUPLICATE
|
||||
|
||||
services/tracker-service/src/lib/
|
||||
├── auth.ts (52 lines) ← DUPLICATE
|
||||
├── config.ts (24 lines) ← DUPLICATE
|
||||
├── cosmos.ts (25 lines) ← DUPLICATE
|
||||
├── errors.ts (44 lines) ← DUPLICATE
|
||||
└── product-config.ts (7 lines) ← DUPLICATE
|
||||
```
|
||||
|
||||
### After Refactor
|
||||
|
||||
```
|
||||
services/platform-service/src/lib/
|
||||
├── blob.ts (service-specific, stays)
|
||||
└── (all others → @bytelyst/*)
|
||||
|
||||
services/billing-service/src/lib/
|
||||
└── (empty — all moved to @bytelyst/*)
|
||||
|
||||
services/growth-service/src/lib/
|
||||
└── (empty — all moved to @bytelyst/*)
|
||||
|
||||
services/tracker-service/src/lib/
|
||||
└── (empty — all moved to @bytelyst/*)
|
||||
|
||||
Each server.ts: ~85 lines → ~15 lines
|
||||
Each config.ts: ~30 lines → ~5 lines (just the extension schema)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Ecosystem Metrics
|
||||
|
||||
| Metric | Before | After | Delta |
|
||||
|--------|--------|-------|-------|
|
||||
| Duplicated infrastructure LOC | ~1,856 | ~275 | **-85%** |
|
||||
| Number of `errors.ts` files | 4 | 1 | **-75%** |
|
||||
| Number of `cosmos.ts` files | 6 | 1 | **-83%** |
|
||||
| Number of `auth/jwt` files | 5 | 1 | **-80%** |
|
||||
| Number of `server.ts` boilerplate lines | 351 | ~60 | **-83%** |
|
||||
| Time to create new Fastify service | ~2 hours | ~20 min | **-83%** |
|
||||
| Time to fix a JWT vulnerability | 5 PRs | 1 PR | **-80%** |
|
||||
| Design token sync effort (MindLyst) | Manual (5 files) | Automated (1 JSON) | **-100%** |
|
||||
478
docs/ecosystem-after-refactor.drawio
Normal file
478
docs/ecosystem-after-refactor.drawio
Normal file
@ -0,0 +1,478 @@
|
||||
<mxfile host="app.diagrams.net" modified="2026-02-12T18:00:00.000Z" agent="Cascade" version="24.0.0" etag="common-plat-ecosystem" type="device">
|
||||
<diagram id="main" name="Ecosystem After Refactor">
|
||||
<mxGraphModel dx="3400" dy="2800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="4000" pageHeight="3200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- TITLE -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="title" value="<b>ByteLyst Ecosystem — Post-Refactor Architecture</b><br><i>3-Repo Monorepo Ecosystem with Shared Common Platform</i><br>v1.0 · Feb 2026" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;fontSize=22;fontFamily=Helvetica;" vertex="1" parent="1">
|
||||
<mxGeometry x="900" y="-60" width="700" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- LEGEND -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="legend_box" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;dashed=1;opacity=80;" vertex="1" parent="1">
|
||||
<mxGeometry x="1780" y="-60" width="360" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="legend_title" value="<b>LEGEND</b>" style="text;html=1;align=left;fontSize=12;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1800" y="-50" width="80" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg1" value="" style="rounded=1;whiteSpace=wrap;fillColor=#FF6E6E;strokeColor=#b85450;opacity=40;" vertex="1" parent="1">
|
||||
<mxGeometry x="1800" y="-20" width="18" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg1t" value="LysnrAI Repo (learning_voice_ai_agent)" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1826" y="-22" width="290" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg2" value="" style="rounded=1;whiteSpace=wrap;fillColor=#5A8CFF;strokeColor=#6c8ebf;opacity=40;" vertex="1" parent="1">
|
||||
<mxGeometry x="1800" y="4" width="18" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg2t" value="MindLyst Repo (learning_multimodal_memory_agents)" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1826" y="2" width="310" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg3" value="" style="rounded=1;whiteSpace=wrap;fillColor=#34D399;strokeColor=#82b366;opacity=40;" vertex="1" parent="1">
|
||||
<mxGeometry x="1800" y="28" width="18" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg3t" value="Common Platform (learning_ai_common_plat)" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1826" y="26" width="290" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg4" value="" style="rounded=1;whiteSpace=wrap;fillColor=#2EE6D6;strokeColor=#0097a7;opacity=40;" vertex="1" parent="1">
|
||||
<mxGeometry x="1800" y="52" width="18" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg4t" value="Azure Cloud Infrastructure" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1826" y="50" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg5" value="" style="rounded=1;whiteSpace=wrap;fillColor=#FFD166;strokeColor=#d6b656;opacity=40;" vertex="1" parent="1">
|
||||
<mxGeometry x="1800" y="76" width="18" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg5t" value="Client Applications (End User)" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1826" y="74" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg6_line" value="" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=2;dashed=1;" edge="1" parent="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1800" y="110" as="sourcePoint" />
|
||||
<mxPoint x="1860" y="110" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="leg6t" value="Depends on (npm/file: reference)" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1870" y="100" width="220" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg7_line" value="" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=2;" edge="1" parent="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1800" y="132" as="sourcePoint" />
|
||||
<mxPoint x="1860" y="132" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="leg7t" value="Network call (HTTP/REST)" style="text;html=1;align=left;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1870" y="122" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- LAYER 0: CLIENT APPLICATIONS (Top) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="clients_group" value="<b>LAYER 0 — CLIENT APPLICATIONS</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FFD166;strokeColor=#d6b656;opacity=15;verticalAlign=top;fontSize=13;fontStyle=1;align=left;spacingLeft=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="40" width="2400" height="160" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- LysnrAI Clients -->
|
||||
<mxCell id="client_desktop" value="<b>LysnrAI Desktop</b><br><font style='font-size:10px'>Python 3.12 + tkinter<br>macOS / Windows</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="80" width="160" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_lysnr_ios" value="<b>LysnrAI iOS</b><br><font style='font-size:10px'>Swift + SwiftUI</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="240" y="80" width="130" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_lysnr_android" value="<b>LysnrAI Android</b><br><font style='font-size:10px'>Kotlin + Compose</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="390" y="80" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_admin" value="<b>Admin Dashboard</b><br><font style='font-size:10px'>Next.js 16 :3001</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="550" y="80" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_user" value="<b>User Portal</b><br><font style='font-size:10px'>Next.js 16 :3002</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="80" width="130" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_tracker" value="<b>Tracker Dashboard</b><br><font style='font-size:10px'>Next.js :3003</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="860" y="80" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst Clients -->
|
||||
<mxCell id="client_ml_ios" value="<b>MindLyst iOS</b><br><font style='font-size:10px'>SwiftUI + KMP</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="1680" y="80" width="130" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_ml_android" value="<b>MindLyst Android</b><br><font style='font-size:10px'>Compose + KMP</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="1830" y="80" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="client_ml_web" value="<b>MindLyst Web</b><br><font style='font-size:10px'>Next.js 14 :3050</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="1990" y="80" width="130" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Separator label -->
|
||||
<mxCell id="sep_lysnr_clients" value="<font color='#FF6E6E'><b>LysnrAI Clients</b></font>" style="text;html=1;align=center;fontSize=10;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="380" y="155" width="120" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sep_ml_clients" value="<font color='#5A8CFF'><b>MindLyst Clients</b></font>" style="text;html=1;align=center;fontSize=10;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1830" y="155" width="120" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- LAYER 1: LYSNRAI REPO — SERVICES (Left) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="lysnr_repo" value="<b>LAYER 1 — learning_voice_ai_agent (LysnrAI)</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FF6E6E;strokeColor=#b85450;opacity=12;verticalAlign=top;fontSize=13;fontStyle=1;align=left;spacingLeft=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="240" width="960" height="620" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Python Backend -->
|
||||
<mxCell id="python_backend" value="<b>FastAPI Backend</b><br><font style='font-size:9px'>Python 3.12 :8000<br>auth · config · cloud</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=11;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="280" width="180" height="65" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Fastify Services -->
|
||||
<mxCell id="svc_group" value="<b>Fastify Microservices (TypeScript)</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#999999;dashed=1;verticalAlign=top;fontSize=11;fontStyle=1;align=left;spacingLeft=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="370" width="920" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="svc_platform" value="<b>platform-service</b><br><font style='font-size:9px'>:4003<br>Auth · Audit · Flags<br>Notifications · Blob<br>Rate Limiting</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="410" width="150" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="svc_billing" value="<b>billing-service</b><br><font style='font-size:9px'>:4002<br>Subscriptions · Plans<br>Usage · Licenses<br>Stripe Webhooks</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="250" y="410" width="150" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="svc_growth" value="<b>growth-service</b><br><font style='font-size:9px'>:4001<br>Invitations<br>Referrals · Promos</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="420" y="410" width="140" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="svc_tracker" value="<b>tracker-service</b><br><font style='font-size:9px'>:4004<br>Items · Comments<br>Votes · Public API</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="580" y="410" width="140" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Service internal lib — BEFORE (crossed out) -->
|
||||
<mxCell id="svc_old_lib" value="<font color='#999999'><s>src/lib/ (per-service duplicate)</s><br><s>cosmos.ts · errors.ts · config.ts</s><br><s>auth.ts · product-config.ts</s></font><br><font color='#34D399'><b>→ NOW: @bytelyst/* packages</b></font>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;fontSize=10;align=left;" vertex="1" parent="1">
|
||||
<mxGeometry x="740" y="400" width="220" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Dashboards internal lib — BEFORE (crossed out) -->
|
||||
<mxCell id="dash_group" value="<b>Next.js Dashboards (TypeScript)</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#999999;dashed=1;verticalAlign=top;fontSize=11;fontStyle=1;align=left;spacingLeft=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="595" width="920" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dash_admin" value="<b>admin-dashboard-web</b><br><font style='font-size:9px'>:3001 · Next.js 16<br>Users · Tokens · Audit<br>Usage · Invitations · Themes</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="630" width="180" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dash_user" value="<b>user-dashboard-web</b><br><font style='font-size:9px'>:3002 · Next.js 16<br>Profile · Billing<br>Settings · SSO</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="280" y="630" width="160" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dash_tracker" value="<b>tracker-dashboard-web</b><br><font style='font-size:9px'>:3003 · Next.js<br>Board · Roadmap<br>Public Voting</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="460" y="630" width="160" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dash_old_lib" value="<font color='#999999'><s>src/lib/ (per-dashboard duplicate)</s><br><s>cosmos.ts · auth-server.ts · auth-context.tsx</s><br><s>api.ts · utils.ts</s></font><br><font color='#34D399'><b>→ NOW: @bytelyst/* packages</b></font>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;fontSize=10;align=left;" vertex="1" parent="1">
|
||||
<mxGeometry x="640" y="630" width="320" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Monitoring -->
|
||||
<mxCell id="monitoring" value="<b>Monitoring</b><br><font style='font-size:9px'>Loki · Grafana<br>Health Checks</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="260" y="280" width="130" height="65" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Shared JSON -->
|
||||
<mxCell id="lysnr_product_json" value="<b>shared/product.json</b><br><font style='font-size:9px'>Product Identity</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="410" y="280" width="140" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Desktop app -->
|
||||
<mxCell id="desktop_app" value="<b>Desktop App</b><br><font style='font-size:9px'>Python 3.12 · tkinter<br>Audio · STT · LLM · Paste<br>Hotkey · Cloud Sync</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="570" y="270" width="180" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Mobile apps -->
|
||||
<mxCell id="lysnr_mobile" value="<b>Mobile Apps</b><br><font style='font-size:9px'>iOS (Swift) + Android (Kotlin)<br>Fully native (no KMP)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="770" y="280" width="180" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- LAYER 1: MINDLYST REPO (Right) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="ml_repo" value="<b>LAYER 1 — learning_multimodal_memory_agents (MindLyst)</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#5A8CFF;strokeColor=#6c8ebf;opacity=12;verticalAlign=top;fontSize=13;fontStyle=1;align=left;spacingLeft=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1560" y="240" width="580" height="620" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- KMP Shared -->
|
||||
<mxCell id="kmp_shared" value="<b>KMP Shared Module</b><br><font style='font-size:9px'>commonMain/ (Kotlin)<br>Models · Repositories<br>DI · Triage Pipeline<br>OpenAI + Whisper Client</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1580" y="290" width="180" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst Design Tokens -->
|
||||
<mxCell id="ml_tokens_old" value="<font color='#999999'><s>MindLystTokens.kt</s><br><s>MindLystTheme.swift</s><br><s>globals.css</s></font><br><font color='#34D399'><b>→ NOW: @bytelyst/design-tokens</b></font>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;fontSize=10;align=left;" vertex="1" parent="1">
|
||||
<mxGeometry x="1780" y="290" width="220" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst iOS -->
|
||||
<mxCell id="ml_ios" value="<b>iOS App</b><br><font style='font-size:9px'>SwiftUI<br>MindLystTheme.swift<br>HomeScreen · CaptureOrb</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1580" y="420" width="160" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst Android -->
|
||||
<mxCell id="ml_android" value="<b>Android App</b><br><font style='font-size:9px'>Jetpack Compose<br>MindLystTheme.kt<br>HomeScreen · CaptureOrb</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1760" y="420" width="160" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst Web -->
|
||||
<mxCell id="ml_web" value="<b>Web Dashboard</b><br><font style='font-size:9px'>Next.js 14 :3050<br>Pages Router · CSS vars<br>Landing · Dashboard</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1580" y="530" width="160" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst Design System -->
|
||||
<mxCell id="ml_design_sys" value="<b>design-system/</b><br><font style='font-size:9px'>tokens/ (JSON source)<br>web/ (mindlyst.css)<br>components/ (specs)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1760" y="530" width="160" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Future MindLyst services -->
|
||||
<mxCell id="ml_future" value="<font color='#999999'><b>Future: MindLyst Backend Services</b><br><font style='font-size:9px'>Will also use @bytelyst/cosmos,<br>@bytelyst/auth, @bytelyst/fastify-core</font></font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#6c8ebf;dashed=1;fontSize=10;" vertex="1" parent="1">
|
||||
<mxGeometry x="1580" y="650" width="340" height="55" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- LAYER 2: COMMON PLATFORM (Center) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="common_repo" value="<b>LAYER 2 — learning_ai_common_plat (@bytelyst/*)</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#34D399;strokeColor=#82b366;opacity=15;verticalAlign=top;fontSize=14;fontStyle=1;align=center;" vertex="1" parent="1">
|
||||
<mxGeometry x="1040" y="240" width="480" height="620" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- P0 packages -->
|
||||
<mxCell id="p0_label" value="<font color='#dc2626'><b>P0 — Drop-in (do first)</b></font>" style="text;html=1;align=left;fontSize=10;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1060" y="270" width="180" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_errors" value="<b>@bytelyst/errors</b><br><font style='font-size:9px'>ServiceError base class<br>NotFound · BadRequest<br>Unauthorized · Forbidden<br>Conflict · TooManyRequests</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1060" y="295" width="200" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_cosmos" value="<b>@bytelyst/cosmos</b><br><font style='font-size:9px'>CosmosClient singleton<br>getContainer(name)<br>Container registry + TTL<br>initializeAllContainers()</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="295" width="200" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- P1 packages -->
|
||||
<mxCell id="p1_label" value="<font color='#ea580c'><b>P1 — High impact</b></font>" style="text;html=1;align=left;fontSize=10;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1060" y="390" width="180" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_config" value="<b>@bytelyst/config</b><br><font style='font-size:9px'>Zod base env schema<br>(PORT, HOST, NODE_ENV,<br>COSMOS_*, SERVICE_NAME)<br>Product identity loader</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1060" y="415" width="200" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_auth" value="<b>@bytelyst/auth</b><br><font style='font-size:9px'>JWT create/verify (jose)<br>Fastify extractAuth hook<br>Next.js getCurrentUser<br>bcrypt password hashing</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="415" width="200" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_fastify" value="<b>@bytelyst/fastify-core</b><br><font style='font-size:9px'>createServiceApp() factory<br>CORS · Swagger · Metrics<br>x-request-id hook<br>/health · Error handler</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1060" y="510" width="200" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- P2 packages -->
|
||||
<mxCell id="p2_label" value="<font color='#ca8a04'><b>P2 — Medium impact</b></font>" style="text;html=1;align=left;fontSize=10;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="510" width="180" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_api_client" value="<b>@bytelyst/api-client</b><br><font style='font-size:9px'>createApiClient() factory<br>Typed fetch + auth headers<br>{ data, error } or throw</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="535" width="200" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_react_auth" value="<b>@bytelyst/react-auth</b><br><font style='font-size:9px'>AuthProvider&lt;TUser&gt;<br>useAuth() hook<br>localStorage token mgmt</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1060" y="610" width="200" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- P3 package -->
|
||||
<mxCell id="p3_label" value="<font color='#4f46e5'><b>P3 — Cross-platform</b></font>" style="text;html=1;align=left;fontSize=10;fontStyle=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="620" width="180" height="18" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="pkg_tokens" value="<b>@bytelyst/design-tokens</b><br><font style='font-size:9px'>Canonical JSON source<br>→ CSS vars (--ml-*)<br>→ TypeScript constants<br>→ Kotlin object<br>→ Swift structs</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="645" width="200" height="85" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- LAYER 3: AZURE INFRASTRUCTURE (Bottom) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<mxCell id="azure_layer" value="<b>LAYER 3 — Azure Cloud Infrastructure</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#2EE6D6;strokeColor=#0097a7;opacity=12;verticalAlign=top;fontSize=13;fontStyle=1;align=center;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="920" width="2100" height="180" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_cosmos" value="<b>Azure Cosmos DB</b><br><font style='font-size:9px'>cosmos-mywisprai<br>Serverless · NoSQL<br>DB: lysnrai<br>13+ containers</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="960" width="170" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_blob" value="<b>Azure Blob Storage</b><br><font style='font-size:9px'>bytelystblobs<br>6 containers: audio,<br>transcripts, attachments,<br>avatars, releases, backups</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="280" y="960" width="180" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_keyvault" value="<b>Azure Key Vault</b><br><font style='font-size:9px'>kv-mywisprai<br>Secrets management</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="490" y="960" width="150" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_speech" value="<b>Azure Speech</b><br><font style='font-size:9px'>mywisprai-speech<br>STT (F0 tier)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="670" y="960" width="140" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_openai" value="<b>Azure OpenAI</b><br><font style='font-size:9px'>mywisprai-openai-sweden<br>swedencentral · S0<br>gpt-4o-mini</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="840" y="960" width="170" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_stripe" value="<b>Stripe</b><br><font style='font-size:9px'>Payments · Webhooks<br>Subscriptions</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1040" y="960" width="140" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_openai_public" value="<b>OpenAI API</b><br><font style='font-size:9px'>api.openai.com<br>gpt-4o-mini · Whisper<br>(MindLyst triage)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1210" y="960" width="150" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_docker" value="<b>Docker Compose</b><br><font style='font-size:9px'>Traefik · Loki · Grafana<br>All services</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1390" y="960" width="150" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="az_gh_actions" value="<b>GitHub Actions</b><br><font style='font-size:9px'>CI pipelines<br>9 workflows (LysnrAI)<br>1 workflow (MindLyst)</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0f7fa;strokeColor=#0097a7;fontSize=10;shadow=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1570" y="960" width="160" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- DEPENDENCY ARROWS: Services → Common Platform -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Platform service → errors -->
|
||||
<mxCell id="arr_plat_err" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=2;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="svc_platform" target="pkg_errors">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Billing service → errors -->
|
||||
<mxCell id="arr_bill_err" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=1;exitY=0.3;exitDx=0;exitDy=0;entryX=0;entryY=0.7;entryDx=0;entryDy=0;" edge="1" parent="1" source="svc_billing" target="pkg_errors">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Growth service → fastify-core -->
|
||||
<mxCell id="arr_grow_fc" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.3;entryDx=0;entryDy=0;" edge="1" parent="1" source="svc_growth" target="pkg_fastify">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Tracker service → auth -->
|
||||
<mxCell id="arr_trk_auth" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="svc_tracker" target="pkg_auth">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Admin dashboard → cosmos -->
|
||||
<mxCell id="arr_admin_cos" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=1;exitY=0.3;exitDx=0;exitDy=0;entryX=0;entryY=0.9;entryDx=0;entryDy=0;" edge="1" parent="1" source="dash_admin" target="pkg_cosmos">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- User dashboard → react-auth -->
|
||||
<mxCell id="arr_user_ra" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="dash_user" target="pkg_react_auth">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Tracker dashboard → api-client -->
|
||||
<mxCell id="arr_trkd_api" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="dash_tracker" target="pkg_api_client">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- DEPENDENCY ARROWS: MindLyst → Common Platform -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- MindLyst web → design-tokens -->
|
||||
<mxCell id="arr_mlweb_tok" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=2;dashed=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="ml_web" target="pkg_tokens">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst design-system → design-tokens -->
|
||||
<mxCell id="arr_mlds_tok" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;exitX=0;exitY=0.7;exitDx=0;exitDy=0;entryX=1;entryY=0.8;entryDx=0;entryDy=0;" edge="1" parent="1" source="ml_design_sys" target="pkg_tokens">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst future services → fastify-core -->
|
||||
<mxCell id="arr_mlfut_fc" style="endArrow=classic;html=1;strokeColor=#34D399;strokeWidth=1.5;dashed=1;dashPattern=8 4;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.7;entryDx=0;entryDy=0;" edge="1" parent="1" source="ml_future" target="pkg_fastify">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- NETWORK ARROWS: Services → Azure -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Services → Cosmos -->
|
||||
<mxCell id="arr_svc_cosmos" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1.5;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="svc_group" target="az_cosmos">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Desktop → Azure Speech -->
|
||||
<mxCell id="arr_desk_speech" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="desktop_app" target="az_speech">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Dashboards → Cosmos -->
|
||||
<mxCell id="arr_dash_cosmos" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1.5;exitX=0.3;exitY=1;exitDx=0;exitDy=0;entryX=0.7;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="dash_group" target="az_cosmos">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Billing → Stripe -->
|
||||
<mxCell id="arr_bill_stripe" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="svc_billing" target="az_stripe">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- KMP → OpenAI public -->
|
||||
<mxCell id="arr_kmp_openai" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="kmp_shared" target="az_openai_public">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- CLIENT → SERVICE ARROWS -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Desktop client → Desktop app -->
|
||||
<mxCell id="arr_c_desk" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.3;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="client_desktop" target="desktop_app">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Admin client → admin dashboard -->
|
||||
<mxCell id="arr_c_admin" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="client_admin" target="dash_admin">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- User client → user dashboard -->
|
||||
<mxCell id="arr_c_user" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="client_user" target="dash_user">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- MindLyst clients → KMP -->
|
||||
<mxCell id="arr_c_ml_ios" style="endArrow=classic;html=1;strokeColor=#666666;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.3;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="client_ml_ios" target="kmp_shared">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- INTRA-PACKAGE DEPENDENCY ARROWS (within common plat) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- fastify-core → errors -->
|
||||
<mxCell id="arr_fc_err" style="endArrow=classic;html=1;strokeColor=#22c55e;strokeWidth=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="pkg_fastify" target="pkg_errors">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- fastify-core → config -->
|
||||
<mxCell id="arr_fc_cfg" style="endArrow=classic;html=1;strokeColor=#22c55e;strokeWidth=1;exitX=0.3;exitY=0;exitDx=0;exitDy=0;entryX=0.3;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="pkg_fastify" target="pkg_config">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- auth → config -->
|
||||
<mxCell id="arr_auth_cfg" style="endArrow=classic;html=1;strokeColor=#22c55e;strokeWidth=1;exitX=0.3;exitY=0;exitDx=0;exitDy=0;entryX=0.7;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="pkg_auth" target="pkg_config">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- api-client → auth -->
|
||||
<mxCell id="arr_apic_auth" style="endArrow=classic;html=1;strokeColor=#22c55e;strokeWidth=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="pkg_api_client" target="pkg_auth">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- react-auth → api-client -->
|
||||
<mxCell id="arr_ra_apic" style="endArrow=classic;html=1;strokeColor=#22c55e;strokeWidth=1;exitX=1;exitY=0.3;exitDx=0;exitDy=0;entryX=0;entryY=0.7;entryDx=0;entryDy=0;" edge="1" parent="1" source="pkg_react_auth" target="pkg_api_client">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
Loading…
Reference in New Issue
Block a user