Domain · Architecture

Tara — Architecture

The diagram below shows the full request path from a user's device down to storage.

10sections8 minread

On this page

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.

text
┌───────────────────────────────────────────────────────────┐
│                       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.

text
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)

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.