Meditation and mindfulness platform. Named after the Buddhist bodhisattva of compassion and liberation.
This specification documents what is implemented in the
taracodebase: the@tara/apiHono backend, the@tara/webNext.js PWA, the@tara/mobileExpo app, the eightlibs/tara/*libraries, and the eightlibs/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/apidefines and migrates its own Drizzle schema (apps/tara/api/src/db/schema.ts, migrations inapps/tara/api/drizzle/).@tara/databaseis 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_tokens—id,userId(FK, cascade),token(varchar(500), notNull, unique),family(varchar(100), notNull — token-rotation family),expiresAt,revokedAt,userAgent,ipAddress(varchar(45)),createdAt.password_resets—id,userId(FK, cascade),tokenHash(varchar(255)),expiresAt,usedAt,createdAt.email_verifications— same shape aspassword_resets.login_attempts—id,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_events—id,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.experiments—id(varchar(100) PK),name(varchar(200)),description,status(experiment_statusenum, defaultdraft),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_assignments—id,experimentId(FK → experiments, cascade),variantId(varchar(100)),userId(varchar(100)),source(varchar(50), defaultdeterministic),assignedAt. Unique on(experimentId, userId).feature_flags—id(varchar(100) PK),name(varchar(200)),description,enabled(bool, default false),defaultValue(jsonbboolean | 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:
secureHeaders()— security headers on all routescors()— origin fromCORS_ORIGIN(default*), methods GET/POST/PUT/ PATCH/DELETE/OPTIONS, credentials enabledlogger(),timing(),prettyJSON()(dev only)requestIdMiddleware— assigns/propagatesX-Request-IDerrorHandler— RFC 7807 Problem Details error responsesrateLimitMiddlewareon/api/*— anonymous tier (see §8.2)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.registerRequestSchema—email,password, optionalusername, optionaldisplayName(1–100),acceptTerms(must betrue).loginRequestSchema—email,password, optionaldeviceInfo(deviceType∈mobile|tablet|desktop|tv|unknown,os?,browser?).tokenPairSchema—accessToken,refreshToken,expiresIn(seconds),tokenTypeliteralBearer.userResponseSchema—id,email,username?,displayName?,avatarUrl?,role∈user|premium|admin,emailVerified,createdAt.- OAuth:
oauthProviderSchema∈google|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.
sessionStatusSchema—active,paused,completed,abandoned.moodSchema—calm,relaxed,peaceful,focused,anxious,stressed,tired,energized,happy,sad,neutral(11 values).createSessionRequestSchema—meditationId(uuid), optionalcourseId(uuid), optionalmoodBefore.updateSessionRequestSchema— optionalcompletedSeconds(≥0),status∈paused|completed|abandoned,moodAfter,rating(int 1–5),feedback(≤1000 chars).sessionsQuerySchema—page(≥1, default 1),limit(1–100, default 20), optionalmeditationId/courseId/status/startDate/endDate,sortBy∈startedAt|completedAt|durationSeconds(defaultstartedAt),sortOrder∈asc|desc(defaultdesc).
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 themeditation_categoryDrizzle enum.contentTypeSchema—guided,unguided,music,soundscape,story.experienceLevelSchema—beginner,intermediate,advanced.meditationsQuerySchema—page/limit(string-coerced, clamped 1–100), filterscategory,contentType,experienceLevel,isPremium,isFeatured,instructor,tag,minDuration,maxDuration;sortBy∈title|duration|playCount|rating|publishedAt|createdAt;sortOrder∈asc|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.
streakResponseSchema—currentStreak,longestStreak,streakStartDate?,lastMeditationDate?,isStreakActive,daysUntilStreakLoss?.statisticsResponseSchema—totalMinutes,totalSessions,averageSessionMinutes,completedMeditations,completedCourses,currentStreak,longestStreak,weeklyGoalMinutes,weeklyMinutesCompleted,weeklyGoalProgress(0–100),categoryCounts(record of category → count),memberSince.historyQuerySchema—granularity∈day|week|month(defaultday).
6.5 Achievement Schemas#
The @tara/api achievement vocabulary uses a four-tier system (bronze →
silver → gold → platinum) and a six-category taxonomy. This differs from
the @tara/database Prisma AchievementRarity (which uses COMMON →
UNCOMMON → RARE → EPIC → LEGENDARY) — the two schemas model achievements
independently.
achievementCategorySchema—streak,time,sessions,courses,exploration,special.achievementTierSchema—bronze,silver,gold,platinum.achievementSchema—id,name,description,category,tier,iconUrl?,requirement(type∈streak|total_minutes|total_sessions| courses_completed|categories_tried|special,valueint >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).
subscriptionTierSchema—free,premium,lifetime.subscriptionStatusSchema—active,canceled,expired,past_due,trialing.pricingPlanSchema—id,name,description,tier,price(amountcents,currencydefaultusd,interval∈month|year|lifetimeor null),features[],isPopular,trialDays?.- Request schemas:
checkoutRequestSchema,iosReceiptRequestSchema,androidPurchaseRequestSchema.
6.7 Notification Schemas#
timeSchema—HH:MM24-hour regex, used fordailyReminderTimeand quiet-hours boundaries.notificationPreferencesSchema—notificationsEnabled,dailyReminderEnabled,dailyReminderTime?,streakReminderEnabled,newContentEnabled,courseProgressEnabled,weeklyDigestEnabled,marketingEnabled,timezone(IANA).registerDeviceSchema—deviceId(1–255),deviceName?(≤100),platform∈ios|android|web,osVersion?,appVersion?,pushToken(≤500),pushProvider∈apns|fcm|web_push.
6.8 Search and Analytics Schemas#
searchTypeSchema—meditation,course,teacher,all.searchResultSchema— discriminated union ontypeover meditation/course/ teacher result shapes.- Analytics:
analyticsEventSchema,batchEventsRequestSchema,deviceInfoSchema(platform∈ios|android|web,deviceType∈mobile|tablet|desktop|watch|tv),userContextSchema(subscriptionTier∈free|premium|lifetime),experimentAssignmentRequestSchema,featureFlagRequestSchema(responsesource∈default|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 perSubscription(amountcents,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 themeditationstable (instructorvarchar); this Prisma model provides the canonical teacher record.AmbientSound— sound library (name,slug,category,audioUrl,isLoop,isPremium); meditations may reference one asbackgroundMusic.Lesson/LessonContent— courses decompose into orderedLessons, each holding orderedLessonContentitems (meditation / text / exercise). This provides a richer course structure than the Drizzlecourse_meditationsjunction table.Ritual— canonical ritual records (slug,title,summary,primaryDomaindefaulttara,domains[],origin,kind,status,visibility, JSONschedule/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 Drizzleuser_progresstable.UserSettings— notification, playback, privacy, and display settings, includingreminderDays Int[]andtheme.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/apiruntime uses Drizzle.@tara/databaseis 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:
- Meditation —
meditation_started,meditation_completed,meditation_paused,meditation_resumed,meditation_skipped - Course —
course_started,course_completed,lesson_completed,course_progress - Timer —
timer_started,timer_completed,timer_extended - Breathing —
breathing_started,breathing_completed - Subscription —
subscription_started,subscription_renewed,subscription_cancelled,subscription_trial_started,subscription_trial_ended - Content —
content_downloaded,content_deleted,content_favorited,content_unfavorited,content_rated,content_shared - Search —
search_performed,search_result_clicked - Notification —
notification_received,notification_opened,notification_dismissed - Streak/Achievement —
streak_milestone,achievement_unlocked - Onboarding —
onboarding_started,onboarding_step_completed,onboarding_completed,onboarding_skipped - App lifecycle —
app_opened,app_backgrounded,app_crashed - Screen —
screen_viewed - Error —
error_occurred
Each event extends BaseEventProperties (timestamp?, sessionId?, userId?,
anonymousId?). For example, MeditationCompletedProperties carries
scheduledDuration, actualDuration, completionPercentage, pauseCount,
totalPauseDuration, backgroundedDuration, wasInterrupted, and an optional
interruptionReason ∈
phone_call|notification|user_pause|app_background|system.
AchievementUnlockedProperties.achievementTier ∈ bronze|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.
ExperimentStatus—draft,running,paused,completed,archived.ExperimentDefinition—id,name,description,hypothesis,status,variants[],primaryMetric,secondaryMetrics?,targetAudience?,trafficPercentage, dates, timestamps.ExperimentVariant—id,name,description?,weight(0–100),config.ExperimentAssignment.source—deterministic,random,override.ExperimentConfig—hashSeed,stickinessAttribute∈userId|deviceId|sessionId,defaultTrafficPercentage,minimumSampleSize,confidenceLevel,minimumDetectableEffect.FeatureFlagValue—boolean | string | number | object.FeatureFlagCondition.operator—equals,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 tracking —
TaraErrorTrackerwithcaptureException(),captureMessage(),addBreadcrumb(),setUser(),setTag(),setExtra(),withScope().ErrorSeveritycoversfatal,error,warning,info,debug. - Tara-specific contexts —
MeditationErrorContext,SubscriptionErrorContext,AudioErrorContext, and aTaraBreadcrumbCategorytaxonomy for structured breadcrumb labeling. - Performance —
PerformanceMonitorwithstartTransaction,trace,traceSync,Span/Transaction,Measurement, andWebVitalName. - Providers —
ConsoleErrorProvider,MemoryErrorProvider(testing), andServerErrorProvider(withInMemoryErrorStore).@sentry/nodeand@sentry/reactare optional peer dependencies — the library compiles and runs without them. @tara/apiwires monitoring throughmiddleware/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.
TaraEnvironment—development,staging,production,test.TaraPlatform—web,ios,android,api.TaraRuntimeConfig— structured config covering:environment: the activeTaraEnvironmentapp: name (Tara), version,bundleIds(com.oshun.tara),deepLinkSchemes([tara, com.oshun.tara]),universalLinksDomain(tara.oshun.app)endpoints:apiBaseUrl,webBaseUrl,cdnBaseUrlanalytics:enabled,provider∈internal|console|disabledcrashReporting:enabled,provider∈sentry|none
createTaraConfig()/configFromEnvironment()— build a config object; analytics is automatically disabled intest, crash reporting is enabled instagingandproduction. 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), optionalvisual,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.DifficultyLevel—beginner,intermediate,advanced,all-levels.AudioFormat—mp3,aac,ogg,wav,flac,m4a.AudioQuality—low,standard,high,lossless.AccessLevel—free,premium,subscriber,purchase.ContentStatus—draft,published,archived,scheduled.Course—format(daily|weekly|self-paced|scheduled|live),sections[]ofCourseSection,lessonCount,outcomes[],prerequisites?,certificate?.Lessonis a union of seven lesson types:MeditationLesson,VideoLesson,ArticleLesson,ExerciseLesson,QuizLesson,ReflectionLesson,DiscussionLesson.LessonStatus∈locked|available|in-progress|completed.EnrollmentStatus∈not-enrolled|enrolled|in-progress|completed|abandoned.Teacher—bio(TeacherBioshort/medium/full),photo,specialties[](TeacherSpecialty, 25 values incl.trauma-informed,sound-healing),credentials[],socialProfiles[],teachingStyle,featuredContent.TeacherProfileextends withavailability,rates, testimonials, FAQ, gallery.Collection—type(CollectionType, 9 values),items[]ofCollectionItem.Program— multi-daytype(ProgramType:challenge|journey|foundation|deep-dive|seasonal|retreat|live),structure(daily/weekly),days[]/weeks[],milestones[],ProgramProgress/ProgramDayProgress.DayStatus∈locked|available|completed|skipped.- Sound types —
AmbientSound(AmbientCategory10,AmbientType~60 values),BackgroundMusic(MusicMood17),BellSound(BellType12),BinauralBeat(BinauralFrequencydelta/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.- Cache —
InMemoryContentCache,PersistentContentCache,SWRCacheManager(stale-while-revalidate) with prefetching. - Filters — composable filter/sort utilities plus meditation- and
course-specific filters (
sleepMeditations,beginnerCourses, etc.). - Search —
ContentSearchEngine,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.
TaraSessionRecord—id,startedAt,durationSeconds,completed,type∈timer|breathing|guided|ambient|sleep|custom,contentId?.TaraProgressSnapshot—sessions[],weeklyGoalMinutes,timezone?.ProgressSummary—totalSessions,completedSessions,totalMinutes,weeklyGoalMinutes,weeklyMinutes,weeklyGoalProgress.StreakSummary—currentStreak,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.RitualStepKind—meditation,breathwork,sound,movement,posture,visualization,prayer,devotional-reading,passage,journaling,nyx-perspective.BreathworkPattern—box,4-7-8,coherent,alternate-nostril.RitualTemplateStatus—draft,review,active,retired.MoodTaxonomy— 12 values (anxious,scattered,restless,heavy,low,neutral,curious,joyful,agitated,grieving,fearful,peaceful);MoodDistressLevel∈none|low|moderate|highplusMoodDistressSignaladdscrisis.
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/) — aprovidercomponent 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) usesjest-axeto 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-exportscomponents,operations,paths,webhooksfrom 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.tsconnection 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
/healthand/ready, and exposes OpenAPI 3.1 at/api/v1/openapi.jsonwith Swagger UI at/api/docs. - All 16
/api/v1route groups (plus the inline/recommendationshandler) 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-Afterpast the per-tier limit. - Drizzle migrations in
apps/tara/api/drizzle/apply cleanly; the@tara/ databasePrisma migration applies cleanly. @tara/analytics,@tara/monitoring,@tara/config,@tara/content,@tara/features, and@tara/uibuild, 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/ |