Domain · Specifications

Tara — Technical Specifications

The table below provides a quick reference for the domain's key dimensions.

19sections36 minread

On this page

Meditation and mindfulness platform. Named after the Buddhist bodhisattva of compassion and liberation.

This specification documents what is implemented in the tara codebase: the @tara/api Hono backend, the @tara/web Next.js PWA, the @tara/mobile Expo app, the eight libs/tara/* libraries, and the eight libs/meditation/* platform-agnostic engine libraries that Tara consumes. Every schema, enum, endpoint, and field below is traceable to source.


This document is the engineering reference for the Tara domain. It is organized to move from the macro (what exists, what it runs on) to the micro (exact table fields, enum values, Zod schema constraints). Engineers implementing a feature should start with §5 (API Surface) for endpoint contracts, then §4 or §7 for the database schemas behind those endpoints, and §9–§15 for the library-level type definitions consumed by clients.

A critical structural fact for anyone new to this domain: two independent database layers co-exist. The @tara/api Hono server defines and migrates its own Drizzle schema (§4). A separate @tara/database Prisma package (§7) serves library tooling and provides a richer domain model. They are not the same schema — §4 and §7 document each independently. Never assume that a table present in one also exists in the other with the same fields.


1. Domain Overview#

The table below provides a quick reference for the domain's key dimensions.

Property Value
Domain name tara
Scope App-store-safe meditation and mindfulness platform
Applications 3 — @tara/api, @tara/web, @tara/mobile (apps/tara/)
Domain libraries 8 (libs/tara/)
Meditation engine 8 platform-agnostic libraries (libs/meditation/)
Content / docs trees apps/tara/content, apps/tara/docs, top-level tara/content, tara/assets, tara/config
API backend Hono 4 + Drizzle ORM, OpenAPI 3.1, port 3001
API persistence PostgreSQL via Drizzle (@tara/api owns its own schema in src/db/schema.ts)
Separate ORM library @tara/database ships an independent Prisma schema (TARA_DATABASE_URL)
Cache Redis (session cache, used by API)
Object storage AWS S3 (@aws-sdk/client-s3) for avatar uploads; CloudFront signed URLs for premium content

Two persistence layers exist. @tara/api defines and migrates its own Drizzle schema (apps/tara/api/src/db/schema.ts, migrations in apps/tara/api/drizzle/). @tara/database is a separate Prisma package (libs/tara/database/prisma/schema.prisma) with its own table and enum set. They are not the same schema; §4 and §7 document each independently.


2. Service Inventory#

Applications (apps/tara/)#

The three Tara applications are the deployed, runnable products. Each has its own build target and runtime.

Project Path Type
@tara/api apps/tara/api Backend REST API — Hono 4, @hono/zod-openapi, OpenAPI 3.1, port 3001
@tara/web apps/tara/web Next.js 14 PWA (App Router, [locale] i18n routing)
@tara/mobile apps/tara/mobile Expo 51 / React Native (Expo Router 3.5)

apps/tara/content (audio/course/teacher/sound asset trees + JSON schemas) and apps/tara/docs (help-center, legal, marketing, app-store checklists) are asset and documentation directories, not build targets.

Domain Libraries (libs/tara/)#

These eight libraries are the reusable, individually-buildable packages that the Tara applications depend on. Each has its own package.json and test suite.

Project Path Responsibility
@tara/analytics libs/tara/analytics Typed event tracking, pluggable providers, A/B experiments, feature flags
@tara/api-client libs/tara/api-client Typed HTTP client + generated OpenAPI types for the Tara API
@tara/config libs/tara/config Runtime config, environment parsing, ten typed feature flags
@tara/content libs/tara/content Content type definitions, API client, cache, filters, search, React hooks
@tara/database libs/tara/database Prisma schema, generated client, seed scripts (independent of @tara/api)
@tara/features libs/tara/features Feature-state selectors: progress/streak summaries, rituals, taxonomies
@tara/monitoring libs/tara/monitoring Error tracking, breadcrumbs, performance monitoring, pluggable providers
@tara/ui libs/tara/ui Cross-platform UI component library, design tokens, theme provider

Meditation Engine Libraries (libs/meditation/, consumed by Tara)#

These eight libraries are platform-agnostic — they know nothing about Tara's API, database, or billing. They own all audio, breathing, timer, session, and offline logic. Tara declares direct dependencies on only @oshun/meditation-player and @oshun/meditation-breathing; the remaining engine libraries are reached transitively through those two.

Project Path Responsibility
@oshun/meditation-core libs/meditation/core Shared primitives, content models, date/duration/format utils
@oshun/meditation-player libs/meditation/player Audio playback, mixer, queue, streaming, background, cache
@oshun/meditation-timer libs/meditation/timer Countdown timer, bells, intervals, ambient sounds, background
@oshun/meditation-session libs/meditation/session Session lifecycle, persistence, scheduling, analytics
@oshun/meditation-progress libs/meditation/progress Streaks, statistics, achievements, milestones, export, sync
@oshun/meditation-breathing libs/meditation/breathing Breathing exercise engine, patterns, guidance, haptics
@oshun/meditation-offline libs/meditation/offline Download queue, storage, versioning, suggestions
@oshun/meditation-analytics libs/meditation/analytics Privacy-conscious session analytics primitives

@tara/web directly depends on @oshun/meditation-breathing and @oshun/meditation-player. @tara/mobile directly depends on the same two. The remaining meditation libraries are part of the engine but are not declared as direct dependencies of the Tara apps' package.json.


3. Technology Stack#

The following tables list the exact package versions and technologies in use across each application. Version constraints are taken directly from the package.json files in the repository.

Backend API (@tara/api)#

Component Technology / package
Framework Hono ^4.0.0 via OpenAPIHono (@hono/zod-openapi ^0.14.0)
Server @hono/node-server
API docs OpenAPI 3.1 doc + @hono/swagger-ui at /api/docs
Language TypeScript
ORM Drizzle ORM ^0.38.0, Drizzle Kit ^0.30.0, pg
Auth @oshun/auth, @oshun/auth-primitives; JWT issued in-domain
Cache @oshun/cache (Redis)
Validation Zod (@hono/zod-openapi re-export)
Storage @aws-sdk/client-s3, @aws-sdk/s3-request-presigner
Billing stripe ^17.7.0
ID generation nanoid
Monitoring @tara/monitoring
Testing Vitest (unit + integration configs); k6 load tests in test/load

Web Application (@tara/web)#

Component Technology
Framework Next.js ^14 (App Router, [locale] segment, standalone build)
Language TypeScript
UI Radix UI primitives, Headless UI, Tailwind CSS, lucide-react
State Zustand ^4.4.6
Data fetching TanStack Query ^5.89
i18n next-intl ^3.19 (+ @formatjs/intl-localematcher, negotiator)
Animation Framer Motion ^10.16
Meditation @oshun/meditation-player, @oshun/meditation-breathing
Tara libs @tara/ui, @tara/content, @tara/features
Testing Vitest, Playwright, @axe-core/playwright, Storybook, Lighthouse

Mobile Application (@tara/mobile)#

Component Technology
Framework Expo ~51.0 / React Native 0.74.5
Routing Expo Router ~3.5 + React Navigation
Audio Expo AV ~14.0
State Zustand
Storage @react-native-async-storage/async-storage, expo-secure-store
Subscriptions react-native-purchases (RevenueCat) ^8.0
Notifications expo-notifications
Auth expo-apple-authentication
Meditation @oshun/meditation-player, @oshun/meditation-breathing
Tara libs @tara/features
Testing Jest (jest-expo); Maestro E2E flows in e2e/flows/

4. Domain Objects — @tara/api (Drizzle Schema)#

This section documents the tables in apps/tara/api/src/db/schema.ts. Every field, type, and constraint below is taken directly from that file. The API runtime exclusively uses this schema at runtime — it does not read from the Prisma schema in @tara/database.

4.1 users#

The central user identity record. passwordHash is nullable to accommodate OAuth-only accounts. The lockedUntil / failedLoginAttempts pair implements account lockout after repeated failed logins. deletedAt is a soft-delete marker; hard deletion is performed separately.

Field Type Notes
id uuid, PK defaultRandom()
email varchar(255), notNull unique index
username varchar(30) unique index
passwordHash varchar(255) nullable (OAuth-only accounts)
displayName varchar(100)
avatarUrl varchar(500)
role user_role enum default user
authProvider auth_provider enum default email
authProviderId varchar(255) indexed with authProvider
emailVerified boolean default false
emailVerifiedAt timestamptz
lastLoginAt timestamptz
lockedUntil timestamptz account lockout
failedLoginAttempts integer default 0
createdAt timestamptz default now
updatedAt timestamptz default now
deletedAt timestamptz soft-delete marker

4.2 user_preferences#

Per-user app preferences stored as a single row per user. goals is a JSONB array of goal strings gathered during onboarding. Times are stored as HH:MM strings in varchar(5) to avoid timezone ambiguity in reminder scheduling.

id (uuid PK), userId (FK → users, cascade), goals (jsonb string[]), experienceLevel (experience_level enum, default beginner), preferredDuration (integer minutes, default 10), preferredTime (varchar(5), HH:MM), notificationsEnabled (bool, default true), dailyReminderEnabled (bool, default false), dailyReminderTime (varchar(5)), soundEnabled (bool, default true), hapticEnabled (bool, default true), darkModeEnabled (bool, nullable), timezone (varchar(50), default UTC), createdAt, updatedAt.

4.3 subscriptions#

One subscription row per user (unique index on userId). The tier and status enums determine access to premium content. Stripe fields are nullable because mobile subscriptions go through RevenueCat/App Store/Google Play instead of Stripe.

id (uuid PK), userId (FK, cascade, unique index), tier (subscription_tier enum, default free), status (subscription_status enum, default active), stripeCustomerId, stripeSubscriptionId, stripePriceId (varchar(255)), currentPeriodStart, currentPeriodEnd, cancelAtPeriodEnd (bool, default false), canceledAt, trialStart, trialEnd, createdAt, updatedAt.

4.4 meditations#

The primary content table. audioUrl is a relative path or CDN key — the API generates a signed CloudFront URL at request time rather than storing a signed URL directly. instructor is denormalized from the teacher record for performance in list queries. deletedAt enables soft deletion without breaking foreign key references in sessions.

Field Type Notes
id uuid, PK
title varchar(200), notNull
description text
category meditation_category enum notNull, indexed
contentType content_type enum notNull, default guided
durationSeconds integer, notNull
audioUrl varchar(500), notNull
imageUrl varchar(500)
instructor varchar(100) denormalized teacher name
instructorBio text
instructorImageUrl varchar(500)
experienceLevel experience_level enum default beginner
isPremium boolean notNull, default false, indexed
isFeatured boolean notNull, default false, indexed
tags jsonb string[] default []
metadata jsonb Record<string,unknown> default {}
playCount integer notNull, default 0
averageRating decimal(3,2) nullable
ratingCount integer notNull, default 0
publishedAt timestamptz indexed; null = unpublished
createdAt / updatedAt / deletedAt timestamptz deletedAt is soft-delete

4.5 courses#

A course groups ordered meditations into a multi-day program. totalDurationSeconds and sessionCount are maintained as aggregated counters to avoid expensive joins on every course list request.

id, title (varchar(200) notNull), description (text), shortDescription (varchar(500)), imageUrl, instructor (varchar(100)), category (meditation_category enum, notNull), experienceLevel (experience_level enum, default beginner), totalDurationSeconds (integer, default 0), sessionCount (integer, default 0), isPremium (bool, default false), isFeatured (bool, default false), sortOrder (integer, default 0), tags (jsonb string[]), metadata (jsonb), publishedAt, createdAt, updatedAt, deletedAt.

4.6 course_meditations (junction)#

The many-to-many join between courses and meditations. dayNumber assigns a meditation to a specific day in the course; sortOrder controls ordering within a day if multiple meditations are scheduled on the same day.

Composite PK (courseId, meditationId). courseId (FK, cascade), meditationId (FK, cascade), dayNumber (integer, notNull), sortOrder (integer, default 0), isOptional (bool, default false).

4.7 sessions (meditation listening sessions)#

Records every time a user starts a meditation. completedSeconds vs durationSeconds tracks partial completions; completionPercentage is the derived ratio. moodBefore / moodAfter enable before-and-after mood tracking.

Field Type Notes
id uuid, PK
userId uuid, FK (cascade) indexed
meditationId uuid, FK (cascade) indexed
courseId uuid, FK (set null) nullable — set when session is in a course
durationSeconds integer, notNull scheduled duration
completedSeconds integer, notNull actual listened seconds
completionPercentage decimal(5,2), notNull 0–100
isCompleted boolean notNull, default false
rating integer nullable, 1–5
feedback text free-form notes
mood varchar(50)
moodBefore varchar(50)
moodAfter varchar(50)
startedAt timestamptz, notNull indexed
completedAt timestamptz
createdAt timestamptz default now

4.8 user_progress (aggregated stats)#

A single denormalized row per user holding cumulative lifetime statistics. This avoids aggregating across the entire sessions table on every progress page load. weeklyGoalMinutes and weeklyMinutesCompleted are reset at the start of each week via weekStartDate.

id, userId (FK, cascade), totalMinutes, totalSessions, currentStreak, longestStreak (all integer, default 0), streakStartDate, lastMeditationDate (timestamptz), lastMeditationId (FK → meditations), completedCourses / completedMeditations / achievements (jsonb string[]), weeklyGoalMinutes (integer, default 60), weeklyMinutesCompleted (integer, default 0), weekStartDate (timestamptz), createdAt, updatedAt.

4.9 course_progress#

Tracks a single user's progress through a single course. completedDays is a JSONB array of day numbers already completed, allowing sparse completion (skipped days). The unique index on (userId, courseId) enforces one progress row per user-course pair.

id, userId (FK, cascade), courseId (FK, cascade), currentDay (integer, default 1), completedDays (jsonb number[]), isCompleted (bool, default false), completedAt, startedAt (default now), lastActivityAt (default now). Unique index on (userId, courseId).

4.10 favorites#

A simple join table recording user-meditation favorites. The composite PK enforces uniqueness — favoriting the same meditation twice is a no-op at the database level.

Composite PK (userId, meditationId). userId (FK, cascade), meditationId (FK, cascade), createdAt.

4.11 downloads#

Records user-meditation download relationships. fileSize (integer bytes) is stored so the UI can report storage usage. expiresAt enables time-limited download licenses for DRM-light scenarios.

Composite PK (userId, meditationId). userId (FK, cascade), meditationId (FK, cascade), downloadedAt (default now), expiresAt (timestamptz), fileSize (integer bytes).

4.12 collections#

Curated groups of meditations (e.g., "Morning Routines," "Sleep Toolkit"). coverColor (varchar(7), hex) enables per-collection branded colors in the UI. isDaily marks the collection surfaced as the "daily meditation."

id, title (varchar(200) notNull), description (text), shortDescription (varchar(500)), imageUrl, coverColor (varchar(7), hex), collectionType (collection_type enum, default curated), category (meditation_category enum, nullable), isPremium / isFeatured / isDaily (bool, default false), sortOrder (integer, default 0), tags (jsonb), metadata (jsonb), publishedAt, createdAt, updatedAt, deletedAt.

4.13 collection_meditations (junction)#

The many-to-many join between collections and meditations. addedAt records when a meditation was added to the collection for changelog and freshness tracking.

Composite PK (collectionId, meditationId). sortOrder (integer, default 0), addedAt (timestamptz, default now).

4.14 user_devices (push notifications)#

Registers devices for push notification delivery. Each device has a platform (ios/android/web) and a pushToken from the platform's push service (APNs, FCM, or Web Push). The unique index on (userId, deviceId) prevents duplicate registrations for the same physical device.

id, userId (FK, cascade), deviceId (varchar(255), notNull), deviceName (varchar(100)), platform (device_platform enum, notNull), osVersion, appVersion (varchar(50)), pushToken (varchar(500)), pushProvider (push_provider enum), pushEnabled (bool, default true), lastActiveAt (default now), createdAt, updatedAt. Unique index on (userId, deviceId).

4.15 Auth Tables#

Four tables collectively implement the full auth lifecycle: token rotation, password reset, email verification, and login audit.

  • refresh_tokensid, userId (FK, cascade), token (varchar(500), notNull, unique), family (varchar(100), notNull — token-rotation family), expiresAt, revokedAt, userAgent, ipAddress (varchar(45)), createdAt.
  • password_resetsid, userId (FK, cascade), tokenHash (varchar(255)), expiresAt, usedAt, createdAt.
  • email_verifications — same shape as password_resets.
  • login_attemptsid, identifier (varchar(255) — email or username), ipAddress, userAgent, success (bool, notNull), failureReason (varchar(100)), timestamp (default now).

4.16 Analytics Tables#

Four tables back the analytics, experimentation, and feature-flag systems. The analytics event table is append-only and heavily indexed for dashboard queries.

  • analytics_eventsid, eventName (varchar(100), notNull), properties (jsonb), userId (uuid), anonymousId, sessionId, platform, deviceType, appVersion, osVersion, locale, timezone, eventTimestamp (notNull), receivedAt (default now). Indexed on name, user, anonymous, session, both timestamps, and platform.
  • experimentsid (varchar(100) PK), name (varchar(200)), description, status (experiment_status enum, default draft), variants (jsonb array of {id, name, weight, config?}), targetAudience (jsonb {subscriptionTiers?, platforms?, minAppVersion?, maxAppVersion?, countries?, languages?, customSegments?}), trafficPercentage (integer, default 100), startDate, endDate, createdAt, updatedAt.
  • experiment_assignmentsid, experimentId (FK → experiments, cascade), variantId (varchar(100)), userId (varchar(100)), source (varchar(50), default deterministic), assignedAt. Unique on (experimentId, userId).
  • feature_flagsid (varchar(100) PK), name (varchar(200)), description, enabled (bool, default false), defaultValue (jsonb boolean | string | number | object), rules (jsonb array of {conditions[], percentage?, value}), createdAt, updatedAt.

4.17 Drizzle Enums#

These enums are defined in apps/tara/api/src/db/schema.ts and are the canonical vocabulary for the API runtime's own types. They are distinct from the Prisma enums in §7.2.

Enum Values
subscription_tier free, premium, lifetime
subscription_status active, canceled, expired, past_due, trialing
meditation_category sleep, stress, focus, anxiety, morning, evening, breathwork, body_scan, visualization, gratitude, self_compassion, relationships, work, creativity, general (15)
experience_level beginner, intermediate, advanced
content_type guided, unguided, music, soundscape, story
auth_provider email, google, apple, facebook
user_role user, premium, admin
device_platform ios, android, web
push_provider apns, fcm, web_push
collection_type featured, category, mood, time_of_day, seasonal, event, curated
experiment_status draft, running, paused, completed

5. API Surface — @tara/api#

The Hono OpenAPIHono app (apps/tara/api/src/app.ts) listens on port 3001 (PORT env, host 0.0.0.0). The OpenAPI document is served at /api/v1/openapi.json (OpenAPI 3.1) and Swagger UI at /api/docs.

5.1 Global Middleware#

Middleware is registered in a fixed order in app.ts. The order matters: for example, requestIdMiddleware runs before errorHandler so every error response carries a request ID. authMiddleware() is applied per route group rather than globally, so public endpoints never incur JWT validation overhead.

The seven middleware steps in registration order:

  1. secureHeaders() — security headers on all routes
  2. cors() — origin from CORS_ORIGIN (default *), methods GET/POST/PUT/ PATCH/DELETE/OPTIONS, credentials enabled
  3. logger(), timing(), prettyJSON() (dev only)
  4. requestIdMiddleware — assigns/propagates X-Request-ID
  5. errorHandler — RFC 7807 Problem Details error responses
  6. rateLimitMiddleware on /api/* — anonymous tier (see §8.2)
  7. authMiddleware() applied per route group (see §5.3)

5.2 Operational Routes#

These routes exist outside the /api/v1 prefix and serve infrastructure concerns — health checks, readiness probes, and API introspection.

Method Path Purpose
GET / API name, version, endpoint index
GET /health DB + Redis health checks, uptime, environment
GET /ready Readiness probe; 200 if all checks pass, else 503
GET /api/v1 API version + endpoint list
GET /api/v1/openapi.json OpenAPI 3.1 document
GET /api/docs Swagger UI

5.3 Route Groups and Auth#

All API route groups are mounted under /api/v1. Auth is enforced by authMiddleware() on protected prefixes. Clients send a Bearer JWT in the Authorization header. Note that some route groups are mixed — for example, /courses allows public listing but requires auth for progress sub-routes, and /analytics allows public event ingestion but requires auth for dashboard and experiment endpoints.

Prefix Auth
/api/v1/auth Public (login/register issue tokens)
/api/v1/users Authenticated (/api/v1/users/*)
/api/v1/meditations Public listing
/api/v1/courses Public listing; progress sub-routes require auth
/api/v1/teachers Public
/api/v1/collections Public
/api/v1/search Public
/api/v1/sessions Authenticated
/api/v1/progress Authenticated
/api/v1/achievements Authenticated
/api/v1/favorites Authenticated
/api/v1/history Authenticated
/api/v1/downloads Authenticated
/api/v1/subscription Authenticated except POST /webhook (public for Stripe)
/api/v1/notifications Authenticated
/api/v1/analytics /events public; experiments/flags/user/dashboard require auth
/api/v1/recommendations Public

5.4 Auth Endpoints (/api/v1/auth)#

These endpoints handle user registration, login, token lifecycle, email verification, password reset, and OAuth. The Google and Apple OAuth callbacks follow the standard OAuth 2.0 authorization code flow.

Method Path Purpose
POST /register Register a new user
POST /login Login with email + password
POST /refresh Exchange refresh token for new pair
POST /logout Logout (one device or all)
POST /forgot-password Request a password-reset email
POST /reset-password Reset password with token
POST /verify-email Verify email address with token
POST /resend-verification Resend email verification
GET /google Initialize Google OAuth
GET /google/callback Handle Google OAuth callback
GET /apple Initialize Apple OAuth
POST /apple/callback Handle Apple OAuth callback

5.5 Users Endpoints (/api/v1/users, all bearer-auth)#

User profile and device management. Avatar upload goes to S3 via the API; the API returns a URL pointing to the uploaded object. Device registration is used for push notification token management.

Method Path Purpose
GET /me Get current user profile
PATCH /me Update profile
POST /me/change-password Change password
DELETE /me Delete account
POST /me/avatar Upload avatar (S3-backed)
DELETE /me/avatar Remove avatar
GET /me/settings Get user settings
PATCH /me/settings Update user settings
GET /me/devices List registered devices
POST /me/devices Register a device
PATCH /me/devices/:id Update a device
DELETE /me/devices/:id Delete a device

5.6 Content Endpoints#

Content endpoints are public — unauthenticated users can browse and search the catalog. Premium content is visible in listings but returns 403 for audio URLs when requested without a valid premium subscription.

Meditations (/api/v1/meditations):

Method Path Purpose
GET / List meditations (paginated, filterable, sorted)
GET /featured Featured meditations
GET /categories List meditation categories
GET /:id Get meditation by ID (detail incl. audioUrl)
GET /{id}/audio Get audio URLs for a meditation

Courses (/api/v1/courses):

Method Path Purpose
GET / List courses
GET /:id Get course (with lessons)
GET /:id/lessons List course lessons
GET /:id/lessons/:lessonId Get a specific lesson
GET /:id/progress Get course progress (auth)
POST /:id/start Start a course (auth)
POST /:id/lessons/:lessonId/complete Complete a lesson (auth)

Teachers (/api/v1/teachers):

Method Path Purpose
GET / List teachers
GET /:id Get teacher by ID
GET /:id/meditations Teacher's meditations
GET /:id/courses Teacher's courses

Collections (/api/v1/collections): GET / (list), GET /:id (by ID), GET /daily (daily meditation).

Search (/api/v1/search): GET / — full-text search across meditations, courses, and teachers with a type filter (meditation | course | teacher | all).

5.7 Activity Endpoints#

Activity endpoints are all authenticated. Sessions are the core usage record; they power progress, streaks, and achievements.

Sessions (/api/v1/sessions, auth):

Method Path Purpose
POST / Start a meditation session
GET / List sessions (paginated, filterable)
GET /{id} Get session details
PATCH /{id} Update session (progress, status, rating)

Progress (/api/v1/progress, auth):

Method Path Purpose
GET /streak Practice-rhythm (streak) information
GET /statistics Aggregate meditation statistics
GET /history Meditation history (grouped by granularity)

Achievements (/api/v1/achievements, auth): GET / (list achievements with unlock state), GET /:id (achievement details).

Favorites (/api/v1/favorites, auth): GET / (list), GET /:meditationId (check), POST /:meditationId (add), DELETE /:meditationId (remove).

History (/api/v1/history, auth): GET / (list), DELETE / (clear all), DELETE /:id (delete one entry).

Downloads (/api/v1/downloads, auth): GET / (list), GET /:meditationId (check status), POST /:meditationId (register download), DELETE /:meditationId (remove record).

5.8 Subscription Endpoints (/api/v1/subscription)#

The Stripe webhook endpoint (POST /webhook) must remain public because Stripe delivers events without a bearer token — its security comes from the Stripe-Signature header validated against STRIPE_WEBHOOK_SECRET.

Method Path Purpose Auth
GET / Get current subscription yes
POST /checkout Create a Stripe checkout session yes
POST /cancel Cancel subscription yes
POST /restore Restore a canceled subscription yes
POST /portal Get Stripe customer-portal URL yes
POST /webhook Stripe webhook handler public
POST /ios/verify Verify iOS App Store receipt yes
POST /android/verify Verify Android Google Play purchase yes

5.9 Notifications Endpoints (/api/v1/notifications, auth)#

Push notification preference management and device token administration.

GET /preferences, PATCH /preferences, GET /devices, POST /devices, DELETE /devices/:deviceId.

5.10 Analytics Endpoints (/api/v1/analytics)#

The /events ingestion endpoint is intentionally public so clients can record events before a user has logged in (anonymous sessions use anonymousId). All other analytics endpoints require authentication because they return or modify per-user experiment state.

Method Path Purpose Auth
POST /events Track a batch of analytics events public
POST /experiments/assignment Get experiment variant assignment yes
POST /flags/evaluate Evaluate a feature flag yes
POST /user/experiments Get all experiment assignments yes

Dashboard sub-router (/api/v1/analytics/dashboard, auth): GET /summary, GET /trends, GET /realtime, GET /funnel.

5.11 Recommendations (/api/v1/recommendations)#

GET /api/v1/recommendations/* returns up to limit (1–50, default 12) published meditations, optionally filtered by category. The ranking strategy is category_affinity when a category is given, otherwise featured_popularity; the endpoint falls back to featured + popular content when no category matches and reports fallbackUsed: true in the response.


6. Request / Response Validation (Zod Schemas)#

All Zod schemas live in apps/tara/api/src/routes/<group>/schemas.ts, one file per route group. Every request body and query parameter is validated through these schemas before the handler runs; validation failures produce RFC 7807 Problem Details error responses rather than 500s.

The standard success envelope is { success, data, meta: { requestId, timestamp } }. Error responses follow RFC 7807: { type, title, status, detail, instance, requestId?, timestamp, code? }.

6.1 Auth Schemas#

These schemas define the security constraints on user identity fields. The password policy (@tara/api/auth) enforces the same rules as passwordSchema.

  • emailSchema — valid email, max 255 chars.
  • passwordSchema — 8–128 chars; must contain ≥1 uppercase, ≥1 lowercase, ≥1 digit, ≥1 special character.
  • usernameSchema — 3–30 chars; [a-zA-Z0-9_-] only.
  • registerRequestSchemaemail, password, optional username, optional displayName (1–100), acceptTerms (must be true).
  • loginRequestSchemaemail, password, optional deviceInfo (deviceTypemobile|tablet|desktop|tv|unknown, os?, browser?).
  • tokenPairSchemaaccessToken, refreshToken, expiresIn (seconds), tokenType literal Bearer.
  • userResponseSchemaid, email, username?, displayName?, avatarUrl?, roleuser|premium|admin, emailVerified, createdAt.
  • OAuth: oauthProviderSchemagoogle|apple|facebook; oauthStateSchema, oauthCallbackQuerySchema, appleCallbackRequestSchema.

6.2 Session Schemas#

Session schemas capture the full lifecycle of a listening session, including mood tracking before and after. The moodSchema values drive the mood-before / mood-after fields in the sessions table.

  • sessionStatusSchemaactive, paused, completed, abandoned.
  • moodSchemacalm, relaxed, peaceful, focused, anxious, stressed, tired, energized, happy, sad, neutral (11 values).
  • createSessionRequestSchemameditationId (uuid), optional courseId (uuid), optional moodBefore.
  • updateSessionRequestSchema — optional completedSeconds (≥0), statuspaused|completed|abandoned, moodAfter, rating (int 1–5), feedback (≤1000 chars).
  • sessionsQuerySchemapage (≥1, default 1), limit (1–100, default 20), optional meditationId/courseId/status/startDate/endDate, sortBystartedAt|completedAt|durationSeconds (default startedAt), sortOrderasc|desc (default desc).

6.3 Meditation Schemas#

These schemas mirror the Drizzle enums (§4.17) and are used for both filtering list queries and validating content creation payloads.

  • meditationCategorySchema — same 15 values as the meditation_category Drizzle enum.
  • contentTypeSchemaguided, unguided, music, soundscape, story.
  • experienceLevelSchemabeginner, intermediate, advanced.
  • meditationsQuerySchemapage/limit (string-coerced, clamped 1–100), filters category, contentType, experienceLevel, isPremium, isFeatured, instructor, tag, minDuration, maxDuration; sortBytitle|duration|playCount|rating|publishedAt|createdAt; sortOrderasc|desc; search.

6.4 Progress Schemas#

The progress schemas define the exact response shapes clients receive for streak and statistics endpoints. daysUntilStreakLoss enables the "streak at risk" notification logic — when this value reaches 0, the streak will be lost at midnight.

  • streakResponseSchemacurrentStreak, longestStreak, streakStartDate?, lastMeditationDate?, isStreakActive, daysUntilStreakLoss?.
  • statisticsResponseSchematotalMinutes, totalSessions, averageSessionMinutes, completedMeditations, completedCourses, currentStreak, longestStreak, weeklyGoalMinutes, weeklyMinutesCompleted, weeklyGoalProgress (0–100), categoryCounts (record of category → count), memberSince.
  • historyQuerySchemagranularityday|week|month (default day).

6.5 Achievement Schemas#

The @tara/api achievement vocabulary uses a four-tier system (bronzesilvergoldplatinum) and a six-category taxonomy. This differs from the @tara/database Prisma AchievementRarity (which uses COMMONUNCOMMONRAREEPICLEGENDARY) — the two schemas model achievements independently.

  • achievementCategorySchemastreak, time, sessions, courses, exploration, special.
  • achievementTierSchemabronze, silver, gold, platinum.
  • achievementSchemaid, name, description, category, tier, iconUrl?, requirement (typestreak|total_minutes|total_sessions| courses_completed|categories_tried|special, value int >0, metadata?), points (≥0), isSecret.

6.6 Subscription Schemas#

These schemas cover the full billing surface: plan definitions, checkout, and store receipt verification. pricingPlanSchema.price.interval being null is the signal for a lifetime purchase (no recurring interval).

  • subscriptionTierSchemafree, premium, lifetime.
  • subscriptionStatusSchemaactive, canceled, expired, past_due, trialing.
  • pricingPlanSchemaid, name, description, tier, price (amount cents, currency default usd, intervalmonth|year|lifetime or null), features[], isPopular, trialDays?.
  • Request schemas: checkoutRequestSchema, iosReceiptRequestSchema, androidPurchaseRequestSchema.

6.7 Notification Schemas#

  • timeSchemaHH:MM 24-hour regex, used for dailyReminderTime and quiet-hours boundaries.
  • notificationPreferencesSchemanotificationsEnabled, dailyReminderEnabled, dailyReminderTime?, streakReminderEnabled, newContentEnabled, courseProgressEnabled, weeklyDigestEnabled, marketingEnabled, timezone (IANA).
  • registerDeviceSchemadeviceId (1–255), deviceName? (≤100), platformios|android|web, osVersion?, appVersion?, pushToken (≤500), pushProviderapns|fcm|web_push.

6.8 Search and Analytics Schemas#

  • searchTypeSchemameditation, course, teacher, all.
  • searchResultSchema — discriminated union on type over meditation/course/ teacher result shapes.
  • Analytics: analyticsEventSchema, batchEventsRequestSchema, deviceInfoSchema (platformios|android|web, deviceTypemobile|tablet|desktop|watch|tv), userContextSchema (subscriptionTierfree|premium|lifetime), experimentAssignmentRequestSchema, featureFlagRequestSchema (response sourcedefault|rule|override).

7. @tara/database — Prisma Schema (Separate Layer)#

@tara/database is a standalone library package whose Prisma schema lives in libs/tara/database/prisma/schema.prisma. It is not used by the @tara/api runtime at all — the API uses Drizzle exclusively. The Prisma schema exists to serve library tooling, richer domain modeling, and the seed scripts. Its datasource URL is TARA_DATABASE_URL, which may point to a different database instance than the one the API reads from.

The Prisma schema enables four PostgreSQL extensions: uuid-ossp, pgcrypto, vector, and pg_trgm. The initial migration is in prisma/migrations/20260506000000_initial/.

7.1 Prisma Models (30)#

The Prisma schema defines 30 models. Several of these represent domain concepts that the Drizzle schema does not model independently (e.g., Teacher as a first-class row, Ritual, AmbientSound, Feedback).

User, Profile, Subscription, Payment, Device, Teacher, Meditation, RelatedMeditation, AmbientSound, Course, Lesson, LessonContent, Ritual, MeditationSession, UserProgress, Streak, Achievement, UserAchievement, Favorite, Download, HistoryEntry, Notification, UserSettings, Feedback.

Notable Prisma-only constructs not present in the @tara/api Drizzle schema:

  • Payment — payment history per Subscription (amount cents, currency, status, platform, store references, refund fields).
  • Teacher — first-class teacher records (name, slug, bio, fullBio, credentials[], specializations[], social URLs, featured). The Drizzle schema denormalizes teacher data into the meditations table (instructor varchar); this Prisma model provides the canonical teacher record.
  • AmbientSound — sound library (name, slug, category, audioUrl, isLoop, isPremium); meditations may reference one as backgroundMusic.
  • Lesson / LessonContent — courses decompose into ordered Lessons, each holding ordered LessonContent items (meditation / text / exercise). This provides a richer course structure than the Drizzle course_meditations junction table.
  • Ritual — canonical ritual records (slug, title, summary, primaryDomain default tara, domains[], origin, kind, status, visibility, JSON schedule / context / components / intendedOutcomes, estimatedDurationMinutes).
  • Streak — dedicated streak record (current/longest streak, total days/ minutes/sessions, weekly/monthly minute rollups). More granular than the inline streak fields in the Drizzle user_progress table.
  • UserSettings — notification, playback, privacy, and display settings, including reminderDays Int[] and theme.
  • Feedback — bug reports, feature requests, content ratings, app reviews, with admin response fields and status.

7.2 Prisma Enums#

The Prisma enums use UPPER_CASE by convention, in contrast to the Drizzle snake_case enums. Concept overlap (e.g., subscription tiers) is intentional but the exact values differ — for example Prisma SubscriptionStatus includes GRACE_PERIOD which has no equivalent in the Drizzle subscription_status enum.

Enum Values
UserStatus ACTIVE, SUSPENDED, DELETED, PENDING_VERIFICATION
ExperienceLevel BEGINNER, INTERMEDIATE, ADVANCED
SubscriptionStatus FREE, TRIAL, ACTIVE, CANCELLED, EXPIRED, GRACE_PERIOD
SubscriptionPlan FREE, MONTHLY, YEARLY, LIFETIME
Platform IOS, ANDROID, WEB
PaymentStatus PENDING, COMPLETED, FAILED, REFUNDED
MeditationCategory MEDITATION, SLEEP, BREATHING, FOCUS, STRESS, MORNING, SELF_COMPASSION, GRATITUDE, BODY_SCANS, WALKING, KIDS, SOUNDS (12)
DifficultyLevel BEGINNER, INTERMEDIATE, ADVANCED
SoundCategory NATURE, RAIN, OCEAN, FOREST, AMBIENT, WHITE_NOISE, MUSIC
LessonContentType MEDITATION, TEXT, EXERCISE
ProgressStatus NOT_STARTED, IN_PROGRESS, COMPLETED
AchievementType STREAK_DAYS, TOTAL_SESSIONS, TOTAL_MINUTES, COURSES_COMPLETED, CATEGORY_MASTERY, SPECIAL
AchievementRarity COMMON, UNCOMMON, RARE, EPIC, LEGENDARY
DownloadStatus PENDING, DOWNLOADING, COMPLETED, FAILED, EXPIRED
NotificationType REMINDER, STREAK, ACHIEVEMENT, NEW_CONTENT, PROMOTION, SYSTEM
Theme LIGHT, DARK, SYSTEM
FeedbackType BUG_REPORT, FEATURE_REQUEST, CONTENT_RATING, APP_REVIEW, GENERAL
FeedbackStatus PENDING, REVIEWED, IN_PROGRESS, RESOLVED, CLOSED

The Drizzle (§4) and Prisma (§7) schemas overlap conceptually but differ in table names, field naming (snake_case maps), enum casing, and coverage. The @tara/api runtime uses Drizzle. @tara/database is a standalone package.


8. Authentication and Authorization#

@tara/api issues and validates JWTs in-domain (apps/tara/api/src/auth/). The auth layer depends on @oshun/auth primitives for token signing but owns its own database tables and business rules.

8.1 JWT Configuration (auth/service.ts)#

The following table shows the environment variables that control token behavior. All TTL values are in seconds. The TARA_JWT_SECRET / JWT_SECRET fallback order allows the secret to be shared across services if needed.

Setting Env var Default
Signing secret TARA_JWT_SECRET / JWT_SECRET dev fallback secret
Issuer TARA_JWT_ISSUER tara-api
Audience TARA_JWT_AUDIENCE tara-app
Access token TTL TARA_ACCESS_TOKEN_TTL (seconds) 900 (15 minutes)
Refresh token TTL TARA_REFRESH_TOKEN_TTL (seconds) 604800 (7 days)

Refresh tokens use rotation families (refresh_tokens.family). Email verification and password-reset tokens are hashed before storage; the default verification/reset token lifetime is 24 hours. Login attempts are recorded in login_attempts; accounts lock via lockedUntil after repeated failures.

8.2 Rate Limiting (middleware/rate-limit.ts)#

Rate limiting uses an in-memory sliding-window algorithm over 60-second windows. Each authenticated request's tier is determined from the JWT claims — anonymous requests use the lowest tier. Headers inform clients of their remaining budget.

Tier Requests / minute
anonymous 60
free 300
premium 1000
admin 5000

The default rateLimitMiddleware on /api/* uses the anonymous tier; a tieredRateLimitMiddleware selects a tier from the request's userTier context. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset; a 429 includes Retry-After.

8.3 Premium Content Access — CloudFront Signed URLs#

Premium audio is never streamed through the API. Instead, apps/tara/api/src/services/cdn/signed-urls.ts provides SignedUrlService, which generates time-limited CloudFront signed URLs (and signed cookies, optionally IP-restricted). The API checks the requesting user's subscription tier before generating a URL; free-tier users receive a 403 for premium content.

Configuration: TARA_CLOUDFRONT_DOMAIN, TARA_CLOUDFRONT_KEY_PAIR_ID, TARA_CLOUDFRONT_PRIVATE_KEY, TARA_SIGNED_URL_EXPIRATION (seconds, default 3600). Content tiers recognized: free, premium, exclusive.


9. @tara/analytics — Events, Experiments, Flags#

@tara/analytics (libs/tara/analytics) is the typed telemetry library for the entire Tara platform. It provides a strongly-typed event map, pluggable provider architecture, experiment management, and feature flags.

9.1 Typed Event Map (src/types/events.ts)#

TaraEventMap defines 45 typed events, each with a strongly-typed property interface. Typing events prevents misnamed or missing properties from reaching the analytics pipeline. Events are grouped by feature area:

  • Meditationmeditation_started, meditation_completed, meditation_paused, meditation_resumed, meditation_skipped
  • Coursecourse_started, course_completed, lesson_completed, course_progress
  • Timertimer_started, timer_completed, timer_extended
  • Breathingbreathing_started, breathing_completed
  • Subscriptionsubscription_started, subscription_renewed, subscription_cancelled, subscription_trial_started, subscription_trial_ended
  • Contentcontent_downloaded, content_deleted, content_favorited, content_unfavorited, content_rated, content_shared
  • Searchsearch_performed, search_result_clicked
  • Notificationnotification_received, notification_opened, notification_dismissed
  • Streak/Achievementstreak_milestone, achievement_unlocked
  • Onboardingonboarding_started, onboarding_step_completed, onboarding_completed, onboarding_skipped
  • App lifecycleapp_opened, app_backgrounded, app_crashed
  • Screenscreen_viewed
  • Errorerror_occurred

Each event extends BaseEventProperties (timestamp?, sessionId?, userId?, anonymousId?). For example, MeditationCompletedProperties carries scheduledDuration, actualDuration, completionPercentage, pauseCount, totalPauseDuration, backgroundedDuration, wasInterrupted, and an optional interruptionReasonphone_call|notification|user_pause|app_background|system. AchievementUnlockedProperties.achievementTierbronze|silver|gold|platinum.

9.2 Common Analytics Types (src/types/common.ts)#

Shared vocabulary types used across event properties:

Platform (ios|android|web), DeviceType (phone|tablet|desktop|tv|watch), SubscriptionTier (free|premium|lifetime), ContentType (8 values incl. sleep_story, ambient_sound), MeditationTechnique (10 values incl. zen, transcendental, mantra), MeditationCategory (12 values), TimeGranularity (hour|day|week|month|quarter|year), MetricType (counter|gauge|histogram|summary).

9.3 Experiments and Feature Flags (src/types/experiments.ts)#

These types define the full A/B experiment lifecycle, from draft through completion. Variant assignments are deterministic using murmurhash3js so a given user always sees the same variant across sessions.

  • ExperimentStatusdraft, running, paused, completed, archived.
  • ExperimentDefinitionid, name, description, hypothesis, status, variants[], primaryMetric, secondaryMetrics?, targetAudience?, trafficPercentage, dates, timestamps.
  • ExperimentVariantid, name, description?, weight (0–100), config.
  • ExperimentAssignment.sourcedeterministic, random, override.
  • ExperimentConfighashSeed, stickinessAttributeuserId|deviceId|sessionId, defaultTrafficPercentage, minimumSampleSize, confidenceLevel, minimumDetectableEffect.
  • FeatureFlagValueboolean | string | number | object.
  • FeatureFlagCondition.operatorequals, not_equals, contains, not_contains, gt, lt, gte, lte, in, not_in.

9.4 Providers and Tracker#

@tara/analytics exports TaraTracker / createTracker as the main tracking surface. The ExperimentManager (createExperimentManager) ships with an InMemoryExperimentStore for testing. Three built-in providers are exported: InternalAnalyticsProvider, ConsoleAnalyticsProvider, and MemoryAnalyticsProvider. Event utilities include type guards, event builders (buildMeditationStarted, etc.), validateEventProperties, and createEventId. murmurhash3js handles deterministic variant hashing.


10. @tara/monitoring — Error Tracking and Performance#

@tara/monitoring (libs/tara/monitoring) provides production observability. It uses a pluggable provider architecture so the underlying backend (Sentry, a custom log aggregator, or in-memory for tests) can be swapped without changing call sites.

  • Error trackingTaraErrorTracker with captureException(), captureMessage(), addBreadcrumb(), setUser(), setTag(), setExtra(), withScope(). ErrorSeverity covers fatal, error, warning, info, debug.
  • Tara-specific contextsMeditationErrorContext, SubscriptionErrorContext, AudioErrorContext, and a TaraBreadcrumbCategory taxonomy for structured breadcrumb labeling.
  • PerformancePerformanceMonitor with startTransaction, trace, traceSync, Span/Transaction, Measurement, and WebVitalName.
  • ProvidersConsoleErrorProvider, MemoryErrorProvider (testing), and ServerErrorProvider (with InMemoryErrorStore). @sentry/node and @sentry/react are optional peer dependencies — the library compiles and runs without them.
  • @tara/api wires monitoring through middleware/error-tracking.ts.

11. @tara/config — Runtime Configuration and Feature Flags#

@tara/config (libs/tara/config) provides type-safe runtime configuration and a feature flag system for all Tara applications. It reads environment variables at startup and exposes a structured config object rather than raw process.env access scattered through the codebase.

  • TaraEnvironmentdevelopment, staging, production, test.
  • TaraPlatformweb, ios, android, api.
  • TaraRuntimeConfig — structured config covering:
    • environment: the active TaraEnvironment
    • app: name (Tara), version, bundleIds (com.oshun.tara), deepLinkSchemes ([tara, com.oshun.tara]), universalLinksDomain (tara.oshun.app)
    • endpoints: apiBaseUrl, webBaseUrl, cdnBaseUrl
    • analytics: enabled, providerinternal|console|disabled
    • crashReporting: enabled, providersentry|none
  • createTaraConfig() / configFromEnvironment() — build a config object; analytics is automatically disabled in test, crash reporting is enabled in staging and production. Env keys: TARA_ENV, TARA_API_URL, TARA_WEB_URL, TARA_CDN_URL, TARA_ANALYTICS_ENABLED, TARA_CRASH_REPORTING_ENABLED.

11.1 Feature Flags (TaraFeatureFlagKey, 10 flags)#

Ten typed feature flags control platform-specific feature availability. Flags default to true in all environments unless the platform is not in the flag's allow-list. This means features like enableOfflineMode are on by default for iOS and Android but simply not evaluated for web.

Flag Default Platforms
enableOfflineMode true ios, android
enableSocialFeatures true web, ios, android
enablePremiumContent true web, ios, android, api
enableNotifications true ios, android, api
enableAnalytics true web, ios, android, api
enableCrashReporting true web, ios, android, api
enableBreathingExercises true web, ios, android
enableTimerMode true web, ios, android
enableBackgroundSounds true web, ios, android
enableProgressInsights true web, ios, android

isFeatureEnabled() returns false when the requesting platform or environment is not in a flag's allow-list; otherwise it honors an explicit override or the flag default. createFeatureResolver() and listEnabledFeatures() build context-bound resolvers for use in app initialization code.


12. @tara/content — Content Model and Hooks#

@tara/content (libs/tara/content) is the richest of the domain libraries. It combines TypeScript type definitions for all content objects, a typed HTTP client with error hierarchy, a multi-layer cache (in-memory, persistent, stale-while-revalidate), filter utilities, a full-text search engine, WebVTT accessibility helpers, and React data hooks.

12.1 Core Content Types (src/types/)#

The types in this library are more expressive than the API's Drizzle-backed types — they model the full content object graph including audio sets, responsive images, transcript markers, chapters, and teacher profiles with specialties.

  • Meditation — full content object: id, slug, title, shortDescription, description, type, categories[], difficulty, duration, teacher, audio (AudioSet), images (ResponsiveImageSet), optional visual, markers[], chapters[], transcript, settings, meta, stats, relatedIds, tags, isFeatured, isNew.
  • MeditationType (12) — guided, unguided, sleep, focus, breathwork, body-scan, visualization, mantra, mindfulness, loving-kindness, walking, movement.
  • MeditationCategory (25) — stress, sleep, focus, anxiety, depression, self-esteem, relationships, gratitude, productivity, creativity, morning, evening, commute, work, exercise, pain, healing, grief, anger, happiness, calm, energy, emergency, beginner, intermediate, advanced.
  • DifficultyLevelbeginner, intermediate, advanced, all-levels.
  • AudioFormatmp3, aac, ogg, wav, flac, m4a.
  • AudioQualitylow, standard, high, lossless.
  • AccessLevelfree, premium, subscriber, purchase.
  • ContentStatusdraft, published, archived, scheduled.
  • Courseformat (daily|weekly|self-paced|scheduled|live), sections[] of CourseSection, lessonCount, outcomes[], prerequisites?, certificate?. Lesson is a union of seven lesson types: MeditationLesson, VideoLesson, ArticleLesson, ExerciseLesson, QuizLesson, ReflectionLesson, DiscussionLesson. LessonStatuslocked|available|in-progress|completed. EnrollmentStatusnot-enrolled|enrolled|in-progress|completed|abandoned.
  • Teacherbio (TeacherBio short/medium/full), photo, specialties[] (TeacherSpecialty, 25 values incl. trauma-informed, sound-healing), credentials[], socialProfiles[], teachingStyle, featuredContent. TeacherProfile extends with availability, rates, testimonials, FAQ, gallery.
  • Collectiontype (CollectionType, 9 values), items[] of CollectionItem. Program — multi-day type (ProgramType: challenge|journey|foundation|deep-dive|seasonal|retreat|live), structure (daily/weekly), days[]/weeks[], milestones[], ProgramProgress/ProgramDayProgress. DayStatuslocked|available|completed|skipped.
  • Sound typesAmbientSound (AmbientCategory 10, AmbientType ~60 values), BackgroundMusic (MusicMood 17), BellSound (BellType 12), BinauralBeat (BinauralFrequency delta/theta/alpha/beta/gamma), SoundMix, SoundPreferences.

12.2 API Client, Cache, Search, and Hooks#

These are the runtime-facing exports that application code actually calls.

  • ContentClient — typed HTTP client with presets (createProductionClient, createDevelopmentClient, createTestClient, resilient/fast-fail variants). A full error hierarchy: ContentApiError, NetworkError, TimeoutError, NotFoundError, UnauthorizedError, ForbiddenError, RateLimitError, ValidationError, ServerError, ServiceUnavailableError, CacheError.
  • CacheInMemoryContentCache, PersistentContentCache, SWRCacheManager (stale-while-revalidate) with prefetching.
  • Filters — composable filter/sort utilities plus meditation- and course-specific filters (sleepMeditations, beginnerCourses, etc.).
  • SearchContentSearchEngine, ContentSearchIndex, tokenizer, spelling correction (levenshteinDistance), trending-search calculation.
  • Accessibility — WebVTT caption parsing/validation (parseWebVtt, validateCaptionCues, calculateCaptionCoverage).
  • Hooks — React data hooks: useMeditations, useInfiniteMeditations, useMeditation, useCourses, useCourseProgress, useTeachers, useCollections, usePrograms, useDailyContent, plus async primitives (useAsync, useMutation). React is an optional peer dependency.

13. @tara/features — Feature-State and Ritual Logic#

@tara/features (libs/tara/features) derives product-level state from raw progress data, gates features by subscription and platform, and models the concept of "rituals" — structured practice sequences composed of multiple steps. It depends on @tara/config and is consumed by both @tara/web and @tara/mobile.

13.1 Core Types (src/types.ts)#

These types form the input/output contract for progress and streak calculations. TaraProgressSnapshot is the input — a client builds it from locally-cached session data — and ProgressSummary / StreakSummary are the derived outputs.

  • TaraSessionRecordid, startedAt, durationSeconds, completed, typetimer|breathing|guided|ambient|sleep|custom, contentId?.
  • TaraProgressSnapshotsessions[], weeklyGoalMinutes, timezone?.
  • ProgressSummarytotalSessions, completedSessions, totalMinutes, weeklyGoalMinutes, weeklyMinutes, weeklyGoalProgress.
  • StreakSummarycurrentStreak, longestStreak, completedToday.
  • FeatureGateResult{ key: TaraFeatureFlagKey, enabled }.

13.2 Modules (src/index.ts re-exports)#

@tara/features re-exports from a collection of focused modules. The cross-domain helpers at the end of this list are the integration points where Tara surfaces content from other Oshun domains:

progress, streak, gates (feature gating), ritual-template, ritual-session, ritual-completion-event, continuation-state, humane-recovery, resume-memory, library-actions, duration-buckets, context-tags, and taxonomy modules (mood-taxonomy, theme-taxonomy, modality-taxonomy, lineage-taxonomy), plus a triggers/ subsystem (trigger-engine, scheduling-rules) and cross-domain helpers (nisaba-passage-companions, arete-next-steps, nyx-perspective-prompts, veritas-sophia-explanatory-notes, assistant-follow-ups).

Key grounded constants and types:

  • RITUAL_COMPLETION_EVENT_NAME = tara.ritual.completed.
  • RitualStepKindmeditation, breathwork, sound, movement, posture, visualization, prayer, devotional-reading, passage, journaling, nyx-perspective.
  • BreathworkPatternbox, 4-7-8, coherent, alternate-nostril.
  • RitualTemplateStatusdraft, review, active, retired.
  • MoodTaxonomy — 12 values (anxious, scattered, restless, heavy, low, neutral, curious, joyful, agitated, grieving, fearful, peaceful); MoodDistressLevelnone|low|moderate|high plus MoodDistressSignal adds crisis.

14. @tara/ui — Component Library#

@tara/ui (libs/tara/ui) is a React component library (react/react-dom peer deps) with design tokens and a theme provider. It is the visual building block shared across @tara/web and any future Tara applications.

14.1 Components (src/components/)#

There are 29 component directories, each with an implementation file, tests, and an index export:

AchievementBadge, AudioPlayer, Avatar, Badge, BottomSheet, Breadcrumb, BreathingVisualizer, Button, Card, Chip, CourseProgress, Divider, Grid, Header, IconButton, Input, MeditationCard, MiniPlayer, Modal, ProgressChart, SafeArea, ScrollView, SegmentedControl, SessionComplete, Skeleton, SoundMixer, StreakDisplay, TabBar, TeacherCard, TimerDisplay, Toast, Typography.

14.2 Tokens, Theme, and Animations#

  • Tokens (src/tokens/) — five token modules: colors, typography, spacing, effects, breakpoints.
  • Theme (src/theme/) — a provider component exposing design tokens for both light and dark mode.
  • Animations (src/animations/) — primitives, micro, transitions. Every animation provides a reduced-motion variant as required by the WCAG 2.1 AA accessibility target.
  • An accessibility spec (__tests__/accessibility.spec.tsx) uses jest-axe to validate component-level accessibility.

15. @tara/api-client and @tara/database#

These two libraries provide the type-safe data access layer for code outside the API server itself.

  • @tara/api-client (libs/tara/api-client) — a typed HTTP client (client.ts) plus generated OpenAPI types (src/generated/openapi.ts); re-exports components, operations, paths, webhooks from the generated types. Use this library — not hand-rolled fetch calls — when accessing the Tara API from any TypeScript client.
  • @tara/database (libs/tara/database) — the Prisma schema documented in §7, generated client, client.ts connection helper, and seed scripts (prisma/seed.ts, prisma/seed-test.ts). Peer dependency: @prisma/client ^6.0.0.

16. Configuration#

16.1 @tara/api Environment Variables#

The following environment variables configure the @tara/api runtime. All variables without defaults must be set in production; the Drizzle migration tool reads DATABASE_URL or TARA_DATABASE_URL.

Variable Purpose
PORT / HOST API listen port (3001) / host (0.0.0.0)
NODE_ENV Environment
CORS_ORIGIN Allowed CORS origin (default *)
TARA_JWT_SECRET / JWT_SECRET JWT signing secret
TARA_JWT_ISSUER / TARA_JWT_AUDIENCE JWT issuer / audience claims
TARA_ACCESS_TOKEN_TTL / TARA_REFRESH_TOKEN_TTL Token TTLs in seconds
TARA_CLOUDFRONT_DOMAIN CloudFront distribution domain
TARA_CLOUDFRONT_KEY_PAIR_ID / TARA_CLOUDFRONT_PRIVATE_KEY CloudFront signing key
TARA_SIGNED_URL_EXPIRATION Signed-URL lifetime (seconds, def. 3600)

Drizzle migrations are configured via drizzle.config.ts; @tara/database uses TARA_DATABASE_URL.

16.2 @tara/config Environment Variables#

These variables are read by configFromEnvironment() in @tara/config. They control which analytics and crash-reporting providers are activated at runtime.

TARA_ENV (or NODE_ENV), TARA_API_URL, TARA_WEB_URL, TARA_CDN_URL, TARA_ANALYTICS_ENABLED, TARA_CRASH_REPORTING_ENABLED.


17. Integration Points#

Understanding which shared platform packages Tara depends on — and which external services it calls — is essential for onboarding and for security reviews.

17.1 Internal Oshun Dependencies#

These are the monorepo-internal packages from libs/shared/ and libs/oshun/ that Tara consumes. The boundary matters: Tara never owns these packages — bug fixes and upgrades flow from the shared library teams.

Dependency Used by Role
@oshun/auth, -auth-primitives @tara/api, @tara/web JWT auth primitives
@oshun/cache @tara/api Redis cache
@oshun/database @tara/api Database client primitives
@oshun/logging @tara/monitoring, @tara/web Structured logging
@oshun/contracts @tara/ui Shared contracts
@oshun/http-client, -errors, -types @tara/web HTTP client / shared errors / types
@oshun/meditation-player @tara/web, @tara/mobile Audio playback engine
@oshun/meditation-breathing @tara/web, @tara/mobile Breathing exercise engine

17.2 External Services#

Tara integrates with eight external services. Engineers touching billing or notification code should ensure they understand which service handles which platform — for example, web billing uses Stripe directly while mobile billing uses RevenueCat as an abstraction layer over App Store and Google Play.

Service Purpose
Stripe Web checkout, customer portal, billing webhooks (@tara/api)
RevenueCat Cross-platform IAP validation (react-native-purchases)
Apple App Store iOS receipt verification (/subscription/ios/verify)
Google Play Android purchase verification (/subscription/android/verify)
AWS S3 Avatar upload storage (@aws-sdk/client-s3)
AWS CloudFront Signed-URL delivery of premium audio
APNs / FCM Push notifications (apns.ts, fcm.ts services)
Expo EAS Mobile build and OTA pipeline (eas.json)

18. Testing and Acceptance Criteria#

18.1 Test Tooling#

Each application and library has its own test setup. Load tests for the API use k6, which is not part of the standard Vitest/Playwright suite.

App / lib Tooling
@tara/api Vitest (vitest.config.ts + vitest.integration.config.ts); k6 load tests in test/load/api-load.k6.js
@tara/web Vitest, Playwright (+ @axe-core/playwright), Storybook, Lighthouse CI
@tara/mobile Jest (jest-expo); Maestro flows (e2e/flows/)
libs/tara/* Vitest co-located *.spec.ts(x) per package

18.2 Acceptance Criteria#

The following criteria define a passing deployment and test run for the Tara domain:

  • API boots on port 3001, serves /health and /ready, and exposes OpenAPI 3.1 at /api/v1/openapi.json with Swagger UI at /api/docs.
  • All 16 /api/v1 route groups (plus the inline /recommendations handler) mount and validate inputs through their Zod schemas; protected groups reject unauthenticated requests.
  • Auth issues an access+refresh token pair; refresh rotation works via token families; password and email-verification tokens are stored hashed.
  • Rate limiting attaches X-RateLimit-* headers and returns 429 + Retry-After past the per-tier limit.
  • Drizzle migrations in apps/tara/api/drizzle/ apply cleanly; the @tara/ database Prisma migration applies cleanly.
  • @tara/analytics, @tara/monitoring, @tara/config, @tara/content, @tara/features, and @tara/ui build, type-check, and pass their co-located Vitest suites.
  • Web app builds (next build, standalone) and serves [locale]-prefixed routes; mobile app type-checks and runs Maestro smoke flows.

19. Source-of-Truth Index#

This table maps documentation concerns to the actual source files. Use it when you need to find the canonical definition of a type, schema, or piece of configuration.

Concern Path
API app + middleware apps/tara/api/src/app.ts, .../middleware/
API Drizzle schema apps/tara/api/src/db/schema.ts
API Drizzle migrations apps/tara/api/drizzle/
API routes + Zod schemas apps/tara/api/src/routes/<group>/{routes,schemas}.ts
API auth (JWT) apps/tara/api/src/auth/
CDN signed URLs apps/tara/api/src/services/cdn/
Prisma schema (@tara/database) libs/tara/database/prisma/schema.prisma
Analytics events / experiments libs/tara/analytics/src/types/
Monitoring libs/tara/monitoring/src/
Runtime config + flags libs/tara/config/src/
Content types / hooks libs/tara/content/src/
Feature-state / rituals libs/tara/features/src/
UI components / tokens libs/tara/ui/src/
Web app routes apps/tara/web/src/app/[locale]/
Mobile app routes apps/tara/mobile/app/
Meditation engine libs/meditation/*/src/