# Tara — Architecture

> Meditation and mindfulness application domain. Named after the Buddhist
> bodhisattva of compassion and liberation.

---

Tara is the Oshun platform's consumer-facing meditation product — a
fully-featured mindfulness app available on the web (installable PWA) and as
native iOS and Android applications. It solves the problem of making
evidence-based meditation practices accessible across devices: a person who
meditates on their phone during a commute, on a laptop at home, and offline on a
flight should have a consistent, high-quality experience everywhere.

The domain is structured around a deliberate layering decision: all
platform-agnostic meditation logic (audio playback, breathing patterns, timers,
session tracking, offline storage, progress aggregation) lives in the
`libs/meditation/` libraries, which know nothing about Tara's database schema,
authentication, or billing. Tara's own libraries (`libs/tara/`) and applications
(`apps/tara/`) sit on top of this engine and add the platform-specific concerns
— a Hono API, a Next.js web app, an Expo mobile app, and a suite of UI
components, analytics, and configuration libraries. This boundary means the
meditation engine can in principle be reused by other products without dragging
Tara's backend along.

A new engineer joining Tara needs to understand two things immediately: (1) the
`libs/meditation/` libraries are the engine — they own audio, breathing, timing,
sessions, progress, and offline storage; and (2) `libs/tara/` and `apps/tara/`
are the product — they own the API, the UI, billing, analytics, configuration,
and the Drizzle/Prisma database schemas.

---

## 1. Domain Summary

| Type                                             | Count |
| ------------------------------------------------ | ----- |
| Applications                                     | 3     |
| Domain libraries (`libs/tara/`)                  | 8     |
| Meditation engine libraries (`libs/meditation/`) | 8     |

---

## 2. System Architecture

The diagram below shows the full request path from a user's device down to
storage. Every client (mobile, web, future admin) talks exclusively to the Hono
API over HTTPS. The API handles business logic and writes to PostgreSQL; it uses
Redis for session caching. Audio files are never streamed through the API —
premium audio is delivered via time-limited CloudFront signed URLs that the API
generates, keeping audio delivery latency at CDN speed rather than API speed.

```
┌───────────────────────────────────────────────────────────┐
│                       CLIENT LAYER                         │
├─────────────────┬─────────────────┬─────────────────────  │
│  Mobile App     │   Web App       │   Admin Dashboard      │
│  (Expo/RN)      │   (Next.js 14)  │   (Future)            │
│  iOS + Android  │   SSR + PWA     │                       │
└────────┬────────┴────────┬────────┴──────────────┬────────┘
         │                 │                        │
         │   HTTPS / WSS   │                        │
         ▼                 ▼                        ▼
┌───────────────────────────────────────────────────────────┐
│                       API LAYER                            │
│   Hono 4 OpenAPIHono Server  (Port 3001, OpenAPI 3.1)      │
│   16 /api/v1 route groups: auth, users, meditations,       │
│   courses, teachers, collections, search, sessions,        │
│   progress, achievements, favorites, history, downloads,   │
│   subscription, notifications, analytics(+dashboard);      │
│   plus an inline /api/v1/recommendations handler           │
└─────────┬──────────────────────────────┬──────────────────┘
          │                              │
          ▼                              ▼
┌────────────────────┐        ┌────────────────────┐
│   DATA LAYER       │        │   CACHE LAYER      │
│   PostgreSQL       │        │   Redis             │
│   (Drizzle ORM)    │        │   - Session cache  │
│   - Users / Auth   │        │   (@oshun/cache)   │
│   - Content        │        └────────────────────┘
│   - Sessions       │
│   - Subscriptions  │
│   - Analytics      │
└────────────────────┘
          │
          ▼
┌───────────────────────────────────────────────────────────┐
│                     STORAGE LAYER                          │
│   AWS S3  ──────────▶  AWS CloudFront                      │
│   Avatar uploads       Signed-URL audio delivery,          │
│   (@aws-sdk/client-s3) signed cookies, static assets       │
└───────────────────────────────────────────────────────────┘
```

---

## 3. Project Structure

The directory tree below shows how code is organized across applications and
libraries. The `apps/tara/` tree contains the three runnable applications; the
`libs/tara/` tree contains the eight domain libraries consumed by those
applications. Static content assets and documentation live alongside the apps
but are not build targets themselves.

```
oshun/
├── apps/tara/
│   ├── api/                    # Hono backend API server
│   │   ├── src/
│   │   │   ├── app.ts          # Hono app setup, middleware registration
│   │   │   ├── index.ts        # Server entry point
│   │   │   ├── db/             # Database schema and query builders
│   │   │   ├── middleware/     # Auth, rate limiting, error handling
│   │   │   ├── routes/         # Route handlers by domain area
│   │   │   └── services/       # Business logic services
│   │   └── Dockerfile
│   │
│   ├── web/                    # Next.js 14 PWA
│   │   └── src/
│   │       ├── app/            # App Router pages
│   │       │   └── [locale]/   # i18n routing
│   │       ├── components/     # React components
│   │       ├── features/       # Feature-scoped modules
│   │       ├── hooks/          # Custom React hooks
│   │       ├── lib/            # Utilities and API client
│   │       └── i18n/           # Localization strings
│   │
│   ├── mobile/                 # Expo React Native
│   │   └── src/
│   │       ├── screens/        # App screens
│   │       ├── components/     # React Native components
│   │       ├── services/       # Native services (audio, notifications)
│   │       ├── store/          # Zustand state stores
│   │       └── hooks/          # Custom hooks
│   │
│   └── content/                # Static content assets
│       ├── meditations/        # Audio files
│       ├── courses/            # Course definitions
│       ├── teachers/           # Teacher profiles
│       └── sounds/             # Ambient sounds and bells
│
└── libs/tara/
    ├── ui/                     # Cross-platform UI component library + tokens
    ├── content/                # Content types, API client, cache, search, hooks
    ├── api-client/             # Typed API client + generated OpenAPI types
    ├── config/                 # Runtime config, environment parsing, feature flags
    ├── features/               # Feature-state selectors, rituals, taxonomies
    ├── database/               # Prisma schema, generated client, seed scripts
    ├── analytics/              # Event tracking, providers, experiments, flags
    └── monitoring/             # Error tracking, breadcrumbs, performance
```

> **Two independent database schemas exist.** `@tara/database` ships a
> **separate** Prisma schema used by tooling and the library layer. The
> `@tara/api` runtime defines and migrates its own Drizzle schema
> (`apps/tara/api/src/db/`, migrations in `apps/tara/api/drizzle/`). These are
> two distinct schemas — neither is a subset of the other. Both are documented
> fully in `specifications.md` (§4 for Drizzle, §7 for Prisma).

---

## 4. Layer Responsibilities

### API Layer (`apps/tara/api`)

The Hono `OpenAPIHono` server is the single backend for all Tara clients. It
owns:

- Authentication and authorization (JWT issued in-domain, `@oshun/auth`
  primitives, Google/Apple OAuth)
- Content catalog endpoints (meditations, courses, teachers, collections,
  search)
- User session creation, update, and history
- Progress aggregation and streak calculation
- Subscription billing: Stripe checkout, customer portal, and the Stripe
  webhook; iOS App Store and Android Google Play receipt verification
- Analytics event ingestion, experiment assignment, and feature-flag evaluation
- Avatar uploads to S3 and CloudFront signed-URL generation for premium audio

The API does **not** contain audio streaming — audio files are served directly
from CloudFront via signed URLs generated by the API. This keeps the API
stateless with respect to media and eliminates the cost and latency of proxying
large audio files through the application server.

### Web Application (`apps/tara/web`)

Next.js 14 with App Router and server-side rendering. Key architectural choices:

- **TanStack Query** manages server state with caching, background refetch, and
  optimistic updates
- **Zustand** manages client-only UI state (timer running state, sound mixer
  volumes, player state)
- **next-intl** handles i18n with locale-based URL routing (`/en/`, `/es/`,
  etc.)
- **Meditation engine libraries** (`@oshun/meditation-*`) are used directly in
  the browser for audio playback, timer management, and breathing exercises
- **Service Worker** (via `@oshun/meditation-offline`) caches audio files for
  offline PWA use

### Mobile Application (`apps/tara/mobile`)

Expo 51 React Native app targeting iOS and Android. Key choices:

- **Expo AV** wraps `@oshun/meditation-player` for native audio
- **Expo Notifications** delivers streak alerts and session reminders
- **RevenueCat** manages in-app purchases across platforms
- **Expo Router 3.5** provides file-based navigation
- **Maestro** E2E testing for critical user flows

---

## 5. Meditation Engine Integration

The eight `@oshun/meditation-*` libraries handle all platform-agnostic logic and
form the foundation on which Tara is built. The boundary between these libraries
and the Tara applications is explicit: engine libraries receive platform
adapters at initialization time and never import anything Tara-specific. This is
why the same breathing exercise engine code runs in both the browser and React
Native without changes.

`@tara/web` and `@tara/mobile` each declare direct dependencies on
`@oshun/meditation-player` and `@oshun/meditation-breathing`; the remaining
engine libraries are part of the engine surface but accessed through the two
declared dependencies.

| Library                       | Integration point                                                 |
| ----------------------------- | ----------------------------------------------------------------- |
| `@oshun/meditation-core`      | Shared primitives, content models, date/duration/format utilities |
| `@oshun/meditation-player`    | Web: Web Audio API adapter; Mobile: Expo AV adapter               |
| `@oshun/meditation-timer`     | Shared logic; Web and mobile background handlers differ           |
| `@oshun/meditation-breathing` | Shared; haptics use Web Vibration API / Expo Haptics              |
| `@oshun/meditation-session`   | Session lifecycle, persistence, scheduling                        |
| `@oshun/meditation-progress`  | Streaks, statistics, achievements, milestones, export, sync       |
| `@oshun/meditation-offline`   | Web: IndexedDB + Service Worker; Mobile: Expo FileSystem          |
| `@oshun/meditation-analytics` | Privacy-conscious session analytics primitives                    |

Each library uses platform-abstracted factory functions so the calling code
looks identical regardless of platform — only the factory argument changes:

```typescript
// Web
const player = createBrowserOfflineManager();
const bgHandler = createBackgroundAudioHandler('web');

// Mobile (React Native)
const player = createMobileOfflineManager();
const bgHandler = createBackgroundAudioHandler('native');
```

---

## 6. State Management Architecture

Tara uses three distinct state management approaches for three different kinds
of state: server data, ephemeral UI state, and offline/durable client state.
Mixing these up is a common source of bugs; the architecture keeps them clearly
separated.

### Server State (TanStack Query — Web)

All API data (meditation catalog, session history, user progress) is managed by
TanStack Query. Cache keys are namespaced by resource type and ID. Optimistic
updates are used for favorites toggling and session rating submission, so
interactions feel instant even before the API confirms the change.

### Client State (Zustand — Both Platforms)

UI-only state that does not need server persistence is held in Zustand stores:

- Active player state (current track, position, playback status)
- Timer configuration and running state
- Sound mixer volumes and active sounds
- Breathing exercise state

### Offline State (Meditation Libraries)

`@oshun/meditation-offline` and `@oshun/meditation-session` maintain local state
with multiple storage backends, chosen per platform:

- **Web**: IndexedDB for downloaded content metadata; Service Worker cache for
  audio files
- **Mobile**: Expo FileSystem for audio files; AsyncStorage for metadata

---

## 7. Authentication and Authorization

Authentication uses JWT tokens issued in-domain by `@tara/api`
(`apps/tara/api/src/auth/`), built on `@oshun/auth` primitives. The flow from
initial login to protected-resource access proceeds as follows:

1. Client submits credentials to `POST /api/v1/auth/login`.
2. API validates the submitted password against the stored `passwordHash` in the
   PostgreSQL `users` table.
3. API returns a `{ accessToken, refreshToken }` pair in a `tokenPairSchema`
   response.
4. Access token (default 15-minute TTL, `TARA_ACCESS_TOKEN_TTL`) is attached to
   all subsequent API requests in the `Authorization: Bearer <token>` header.
5. When the access token expires, the client exchanges its refresh token at
   `POST /api/v1/auth/refresh` for a new pair. Refresh token rotation uses token
   families (`refresh_tokens.family`) to detect replay attacks: using a token
   from an already-rotated family invalidates the entire family.
6. Google and Apple OAuth flows are also supported for social sign-in.

Premium content authorization is layered on top: the access token's claims
include the user's subscription tier. Content endpoints check the tier claim
before returning premium audio URLs — unauthenticated or free-tier users receive
HTTP 403 for premium content rather than a signed URL.

---

## 8. Design Patterns

### Hook Factory Pattern (Meditation Libraries)

All meditation engine libraries export `createUse*` factory functions rather
than direct React hooks. This design allows React dependency injection and
avoids requiring React as a hard dependency in the engine libraries, which must
also work in non-React environments (e.g., Node.js service workers or test
runners).

```typescript
// In the app's setup file
import { setReactHooks } from '@oshun/meditation-player';
import { useState, useEffect, useCallback, useRef } from 'react';
setReactHooks({ useState, useEffect, useCallback, useRef });

// Then use the bound hooks
const { usePlayer, usePlaybackProgress } = createPlayerHooks();
```

### Platform Abstraction (Three-tier)

Libraries needing platform APIs follow a three-tier pattern: abstract base class
→ concrete web implementation → noop/native stub → factory function that selects
the right implementation for the current environment. This enables the same
library code to run in browser, React Native, and test environments without
`if (Platform.OS === 'ios')` scattered throughout business logic.

### Branded Types

Session IDs, content IDs, and other identifiers use TypeScript branded types
(e.g., `type SessionId = string & { _brand: 'SessionId' }`) to prevent
accidental ID type confusion at compile time. Passing a `MeditationId` where a
`SessionId` is expected is a compile error, not a runtime bug.

---

## 9. Technology Stack Summary

| Layer             | Technology                                                                 |
| ----------------- | -------------------------------------------------------------------------- |
| API               | Hono 4 (`@hono/zod-openapi`), TypeScript, Drizzle ORM, PostgreSQL, Redis   |
| Web               | Next.js 14, React 18, Zustand, TanStack Query, Tailwind CSS, Framer Motion |
| Mobile            | Expo 51, React Native, Expo AV, Expo Router, RevenueCat                    |
| Meditation engine | TypeScript (`libs/meditation/*`), IndexedDB, Service Worker                |
| Build             | Nx, esbuild, tsc                                                           |
| Testing           | Vitest, Playwright (web E2E), Maestro (mobile E2E), k6 (load)              |
| Auth              | JWT issued in-domain, `@oshun/auth` primitives, Google/Apple OAuth         |
| Billing / IAP     | Stripe (web), RevenueCat / App Store / Google Play (mobile)                |
| Storage           | AWS S3 (avatar uploads) + AWS CloudFront (signed-URL audio delivery)       |

---

## 10. Related Domains

The table below explains not just what related domains exist but why the
boundaries are drawn where they are.

| Domain             | Relationship and boundary rationale                                                                                                                                                     |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `libs/meditation/` | Platform-agnostic engine libraries consumed entirely by Tara. The boundary exists so the engine can be reused by other products without coupling to Tara's backend.                     |
| Arete              | Habit tracking and goal setting complement the meditation practice loop. Arete owns habit streaks and goals; Tara owns session streaks. Cross-domain data flows via shared user IDs.    |
| Kuanyin            | Compassion and ethics domain provides wellness frameworks that align with Tara's mindfulness mission. Kuanyin owns the framework definitions; Tara references them as content metadata. |
