Oshun Platform · Features

Product Surfaces

A focused page within the Oshun Platform Features documentation. The full map and every sibling page live in the Features hub.

7sections17 minread2tables

On this page

This page is the customer-facing inventory of where Oshun V1 is touched: the web app and its two coordinated namespaces, the phone-first mobile app, the shared shell every customer app composes from, the desktop companion, the offline substrate, and the public distribution funnel. It serves product and design leads who need the real list of what ships versus what is reachable but launch-gated, and engineers deciding which surface a customer capability belongs in. It is the feature-side companion to the architecture catalog at Product Surfaces, which documents how the same surfaces are assembled from shared libraries and the single BFF; the hub for this set is ../features.md.

The defining property of the V1 surface layer is composition over duplication: there is one customer web app, one customer mobile app, and one shared shell, and almost every customer-facing domain renders through those two apps. These are real, not aspirational — every named app has its own package.json and a substantial src/; the PWA stack is implemented and test-covered; and the mobile apps are real Expo / Expo Router projects with end-to-end Maestro suites and a store pipeline. Where a surface is reachable but not yet launch-enabled (Metis) or fail-loud without credentials (content-service), this page says so plainly rather than implying it ships.

The customer surface map#

Eight named customer-and-operator apps live under apps/oshun/, plus a separate Metis stack under apps/metis/. Package names below were confirmed against each app's package.json name field.

Surface Path Package Audience / role
Customer Web apps/oshun/web/ @oshun/web Everyday and power-user customers
Customer Mobile apps/oshun/mobile/ @oshun/mobile Phone-first customers
Oshun BFF apps/oshun/bff/ @oshun/bff The single API front door behind both
Admin Web apps/oshun/admin/ @oshun/admin Platform operators (review/safety/gov)
Admin Mobile apps/oshun/admin-mobile/ @oshun/admin-mobile On-call operators
Tenant Console apps/oshun/tenant-admin/ @oshun/tenant-admin Tenant admins (§20)
Telegram Bot apps/oshun/telegram-bot/ @oshun/telegram-bot Messaging channel (§26)
Telegram Mini App apps/oshun/telegram-miniapp/ @oshun/telegram-miniapp Embedded web channel surface (§26)
Content Service apps/oshun/content-service/ @oshun/content-service-app Deployable agentic-content service (§3.2)
Legal (docs) apps/oshun/legal/ none — markdown only privacy-policy.md, terms-of-service.md

Two of these entries are not described in the prose elsewhere in V1 and are worth naming explicitly, because earlier drafts of this document omitted them entirely:

  • apps/oshun/content-service (@oshun/content-service-app) is a real, deployable HTTP service surface — not a UI page. Its src/main.ts boots createContentHttpServer / createDurableContentService from @oshun/content-service over a real Iris-routed creative writer and a calibrated three-member JudgePanel (@oshun/content-quality-judge). It is deliberately fail-loud: with no model provider configured (ANTHROPIC_API_KEY absent) it throws a NotConfiguredError at startup rather than fabricate generations, and its cloud deploy is honestly marked [~] in the backlog. See Isis Generation Control and Sophia Grounding for the content pipeline behind it.
  • apps/oshun/legal is a markdown-only docs surface (privacy-policy.md, terms-of-service.md) with no package.json. It is the home of legal copy but not a buildable app; the marketing and landing experience itself lives in the web app under apps/oshun/web/src/app/landing/. The customer-facing privacy controls those documents describe are detailed in Privacy, Consent, Data Portability, and User Controls.

Metis is the only domain with its own app stack. Every other customer-facing domain renders through @oshun/web and @oshun/mobile via the shared shell. The nuance for mobile: a separate apps/metis/mobile/ (@metis/mobile) exists alongside apps/metis/{web,admin,api-gateway,worker}, so on mobile Metis is not solely the Oshun mobile app either. This is the only exception to the one-web-one-mobile rule.

The operator apps (Admin Web, Admin Mobile, Tenant Console) and the Telegram channel surfaces are covered in depth on their own pages — Admin Products — Web and Mobile, Tenant, Institution, and Operator Toolkit, and Telegram Surfaces. The rest of this page focuses on the customer experience: web, mobile, the shared shell, the desktop companion, and distribution.

Customer Web#

The customer web app (@oshun/web, a Next.js App-Router project) is the flagship surface. V1 web includes:

  • A redesigned dashboard (src/app/page.tsx) that reflects the V1 home model and the Tara-centered product narrative — calm by default, with mixed-domain recommendations and reason labels.
  • A desktop-grade multi-panel workspace (src/app/(workspace)/) for continuity, search, reading, evidence inspection, study, note taking, and assistant use.
  • A collapsible assistant dock with persistent context handoff from the current screen — see Assistant Experience.
  • Mixed-domain recommendations with reason labels and suppression rules — see Search, Discovery, Recommendations, and Knowledge Graph.
  • Domain modules for Tara ritual continuation, Arete weekly review, Veritas grounded briefing, Nyx events, Nisaba passages, and Metis study continuation.
  • Universal search (src/app/search/page.tsx) across rituals, practices, concepts, passages, claims, sources, notebooks, collections, programs, sky events, courses, lessons, and learning artifacts — with advanced filters, result grouping, saved searches, recent searches, and assistant-assisted query refinement.
  • Split-view reading, compare, source inspection, evidence panels, citation trails, and notebook entry points.
  • A global command palette and quick-action surface for navigation, assistant invocation, search, recent items, creation actions, agent invocation, and jump-to-anything across customer surfaces.
  • Multi-window and multi-tab support with per-tab continuity, draggable side panels, persistent reading and study tabs, and stable deep-link state.
  • Hand-off and continuity between desktop and mobile — resumable reading, resumable study, resumable assistant transcripts, and notebook sync (the continuation state lives in Iris; see Iris Memory and Identity).
  • Multi-format export, print, share-card, and embed flows for stories, claims, passages, lessons, transcripts, ritual scripts, and notebook entries where rights permit.
  • A genuine PWA: install, update, relaunch, offline shell, recent-content fallback, service-worker caching, and web performance budgets (detailed below).
  • Keyboard-only, accessibility, visual-regression, Lighthouse, Playwright, and resilience coverage for critical web journeys — see Analytics, Observability, Testing, and Security.

The dual-namespace pattern — and the truth about all six domains#

V1 web ships two coordinated customer-surface namespaces per domain that share BFF data and design tokens but serve distinct audiences. Both are V1, and neither is a deprecation candidate for the other — they are an intentional audience split:

  1. /<domain> and /<domain>/* — the consumer hub plus presentational depth, the Tara-centered home for everyday customers. These are the calm Lilith-design-system "rooms" (TaraRoom, AreteRoom, VeritasRoom, NyxRoom, NisabaRoom, and the Metis surfaces), optimized for first-time and returning customers, reading, ritual continuation, and contemplative tone.
  2. /domains/<domain>/* — heavier client workspaces for power users, scholars, operators in customer mode, and editorial / curation roles. Examples: /domains/arete/journal, /domains/veritas/fact-check, /domains/nyx/star-chart, /domains/tara/teachers. These are reachable from the consumer surface through deep links and context-specific entry points, but are not the canonical first read of the domain.

Correction to earlier documentation. Prior drafts claimed that "Metis and Nisaba ship only at /<domain>/* today; their power-user surfaces are mounted inside the consumer hub rather than at a parallel namespace." That carve-out is stale and contradicts both the general dual-namespace pattern and the code. In reality the /domains/* namespace covers all six domains, not four:

  • The static directories under apps/oshun/web/src/app/domains/ are arete, nyx, tara, and veritas, alongside a dynamic catch-all [domainId]/ and a shared layout.tsx (which renders DomainPreviewBannerForPath). The catch-all is what extends coverage to nisaba and metis.
  • apps/oshun/web/src/navigation/routes.ts declares WEB_DOMAIN_IDS = ['tara','veritas','nyx','arete','nisaba'] (line 52) and WEB_NAVIGABLE_DOMAIN_IDS = [...WEB_DOMAIN_IDS, 'metis'] (line 53).
  • apps/oshun/web/src/app/domains/[domainId]/page.tsx gates on isWebNavigableDomainId and ships per-domain SEO metadata in DOMAIN_META for all six ids, including nisaba ("Nisaba — Primary Texts & Scholarship") and metis ("Metis — Learning & Tutoring").
  • apps/oshun/web/src/components/DomainRouteExperience.tsx explicitly lists 'nisaba' and 'metis' (lines 66–67) and carries Metis-specific feature routing (resolveMetisFeatureRouteTarget, resolveMetisShellEntryFlowFromPath).

So /domains/nisaba and /domains/metis are reachable web routes — served by the dynamic catch-all rather than static directories. The consumer hubs are real too: apps/oshun/web/src/app/nisaba/ already carries deep subroutes (compare, daily, graph, lexicon, manuscript, notebook, notebooks, plan, scholar), and apps/oshun/web/src/app/metis/ carries assessment, byom, courses, ingest, lesson, lessons, session, tutor, and upload. See Nisaba — Scholarly Study and Metis — Education and Tutoring.

Reachable route ≠ launched domain. What is true — and is a launch-status fact, not a routing gap — is that Metis is availability: 'planned' in libs/oshun/domain-registry/src/registry.ts, so getAvailableDomains() filters it out of the launch-enabled set. The 2026-06-23 triage (recorded in V1/AUDIT_2026-06-24.md) confirms the live shell's shellDomainCount drops from 5 to 4 post-hydration when nisaba is disconnected and metis is planned. The Metis domain route copy reflects this honestly — its description says learner surfaces "will surface here once Metis shell launch readiness is complete."

PWA — real, implemented, test-covered#

The web app is a genuine Progressive Web App, not a manifest stub:

  • apps/oshun/web/public/manifest.json declares name: "OSHUN", short_name: "OSHUN", start_url: "/?surface=pwa", scope: "/", display: "standalone" (with display_override of window-controls-overlay then standalone), and theme_color: "#f1ebdd" — the parchment tone of the Lilith design system. Its app shortcuts map directly to the six domains (Meditate/domains/tara, Read/domains/veritas, Sky Tonight/domains/nyx, Check In/domains/arete, Study/domains/nisaba, Learn/domains/metis, each with ?origin=home).
  • apps/oshun/web/public/sw.js is the real service worker (≈20 KB).
  • apps/oshun/web/src/lib/pwa-service-worker.ts implements registration, update, and offline plumbing. Real exports include registerServiceWorker(options), applyWaitingServiceWorker(...), resolvePwaUpdateRouteContext(currentPath) (returns a PwaUpdateRouteContext with a PwaUpdateRouteMode of 'reading' | 'standard', so an update prompt offers "Keep reading" and stages the update behind the current reading flow instead of interrupting a deep-read), queueBackgroundAction(...) for background sync, and requestServiceWorkerMemberDataClear() for privacy teardown.
  • Coverage lives in apps/oshun/web/src/lib/pwa-service-worker.test.ts and the policy-level pwa-service-worker-policy.test.ts. Performance budgets are enforced in lighthouse-budget.json.

Customer Mobile#

@oshun/mobile is a real Expo + Expo Router project, not a thin wrapper. Oshun mobile V1 includes:

  • A phone-first IA with home, domain switching, library, assistant, and profile reachable with one-handed use. The tab layout is real Expo Router under app/(tabs)/: index, explore, activity, library, and profile (with _layout.tsx and co-located tests like index.test.ts and profile.test.tsx).
  • Deep-link routing into all top-level domains and high-value object types. app.json sets expo.scheme: "oshun", ios.bundleIdentifier: "com.oshun.mobile" (Android package: "com.oshun.mobile"), and Universal-Link associatedDomains including applinks:oshun.app, applinks:www.oshun.app, and applinks:app.oshun.com. Android App Links auto-verify (autoVerify: true) over pathPrefix values /d, /domains, /app, /home, /explore, /search, /activity, and more.
  • Start-of-day and return-session home behavior with quick actions for meditation, check-in, briefing, tonight, study, and assistant follow-up.
  • Contextual assistant invocation from primary screens — see Assistant Experience.
  • Haptic, motion, and transition behavior for major mobile flows.
  • Push routing and notification controls by domain, persona, quiet hours, and sensitivity.
  • Offline and retry support for recent meditations, audio assets, passages, notes, allowed Veritas briefings, and shell continuity (on the @oshun/offline substrate described below).
  • Mobile continuation cards for Tara, Arete, Veritas, Nyx, Nisaba, and Metis, plus recovery-after-drift logic for habits, study, routines, and interrupted sessions.
  • A mobile command surface (search / sheet / quick-action) for navigation, assistant, agent invocation, capture, and jump-to-anything.
  • Hand-off to and from desktop for reading, study, journaling, voice sessions, and assistant transcripts.
  • Calendar integrations (Google, Apple, Outlook) for Tara rituals, Arete reminders, Nyx events, and Metis study sessions, with two-way sync where authorized.
  • Camera, microphone, and photo-library capture for journaling, evidence uploads, source ingestion, voice notes, and assistant attachments — with explicit permission strings in infoPlist (NSCameraUsageDescription, NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription) and per-domain redaction policies.
  • Share extension targets for ingesting URLs, articles, and media into notebooks, sources, and the assistant (declared via the iOS CFBundleDocumentTypes for public.url / public.image / com.adobe.pdf and NSUserActivityTypes like com.oshun.mobile.share-url).
  • Lock screen, widgets, watch surface, and Live-Activity-style controls for active rituals, study sessions, and voice tutoring where the platform supports it (NSSupportsLiveActivities: true). The wearable/widget surface is composed from @oshun/shell-wearable (see the shell section).
  • Mobile accessibility, crash reporting, performance, battery, memory, caching, device-matrix, and mobile E2E coverage.

The phrase "mobile E2E" understates what actually ships. Under apps/oshun/mobile/scripts/ the real test and release tooling includes Maestro-based end-to-end suites (run-maestro-suite.sh, run-maestro-device-matrix.sh), Android performance gates (validate-android-fps.sh, parse-gfxinfo.mjs), and store-submission tooling (render-store-assets.mjs, validate-store-submission.mjs, capture-store-submission-assets.sh), backed by EAS build/submit configuration for the actual store pipeline.

Shared Consumer Shell#

The two customer apps above do not each reimplement navigation, the assistant, routines, or wearable surfaces — they compose them from shell libraries under libs/oshun/. The shared shell provides:

  • Home, explore, activity, library, notifications, profile, settings, and assistant entry points.
  • Domain-aware navigation for Tara, Arete, Veritas, Nyx, Nisaba, and Metis.
  • Current-domain persistence, deep links, route-level analytics, feature flags, experiments, and onboarding education across all domains.
  • Tara-led home ordering, blended exploration, cross-domain quick actions, and mixed-domain recommendations.
  • Cross-domain activity timelines with assistant highlights, study continuation, notebook continuation, provenance state, and grounding state.
  • Library support for meditations, claims, sources, passages, notebooks, collections, programs, rituals, sky events, courses, and generated artifacts.
  • Notification-center behavior for mixed-domain, admin, support, study, grounding, privacy, and safety events, plus a unified message center with filters, history, delivery receipts, deep links, snooze, and unsubscribe.
  • Profile and settings for account, billing entry, memory controls, disclosure, privacy, notifications, data export, data deletion, sessions, devices, locale, timezone, and personalization.
  • A unified command surface (palette on web, sheet on mobile) for navigation, search, assistant, agent invocation, creation, recent items, and shortcuts.
  • Universal sharing, invite, and referral flows with deep-linkable invitations, per-link permissions, attribution, expiry, and revocation.
  • Public profiles for opted-in customers, creators, scholars, and teachers — with curated artifacts, audience-appropriate disclosure, follow controls, and preserved synthetic-content indicators.
  • Cross-device hand-off and session continuity for reading, study, voice, assistant transcripts, ritual sessions, journaling, and authoring drafts.
  • A multi-account switcher for individuals holding multiple identities (e.g. personal account and tenant-scoped account) with explicit identity indicator and isolation guarantees.
  • In-app changelog / "what's new" feed, contextual help center, walkthrough overlays, keyboard-shortcut reference, in-app feedback / bug reporting / feature requests with screenshot and trace-id attachment.
  • Bookmarks, reading list, saved-for-later, tasks, and queue surface with cross-domain aggregation, smart sorting, due/snooze, and resume.
  • Calendar entry points with two-way sync across major providers.
  • Loading, empty, offline, partial-outage, degraded, retry, and traceable BFF partial-response states.
  • A banner system for tenant-, region-, or feature-scoped status messages with severity, audience scoping, locale, dismissal rules, and accessibility.
  • Auth, onboarding, session refresh, re-authentication, account recovery, profile sync, cold-start personalization, and public-web-to-app conversion.

Which libraries the shell is built from#

The shell is assembled from a small set of shared libraries — see the architecture companion page for the full lib-by-lib breakdown. The core members are @oshun/shell-core (entry points and the command surface, e.g. command-surface.ts, activity-timeline.ts, account-switcher.ts, current-domain-persistence.ts, deep-links.ts), @oshun/shell-assistant (the assistant dock and context hand-off — assistant-engine.ts, action-router.ts, context-handoff.ts, cross-domain-continuity.ts), @oshun/shell-routines (routine and continuation cards — routine-engine.ts, routine-recommender.ts, step-executors.ts), @oshun/shell-wearable (the watch/widget/complication surface — wearable-engine.ts, complication-engine.ts, active-session-surfaces.ts, haptic-patterns.ts), @oshun/navigation, and the design system (@oshun/design-tokens, @oshun/ui).

Two real shell libraries earlier docs omitted. That composition list looked exhaustive but was not — two additional real, code-bearing shell libraries exist and represent surface capabilities neither legacy doc named:

  • @oshun/shell-desktop (libs/oshun/shell-desktop/) is an entire Electron-class desktop companion surface — a whole surface category that goes unmentioned in the source feature list. Its src/ contains desktop-engine.ts (DesktopEngine), window-manager.ts (WindowManager), tray-companion.ts (buildTrayMenu, computeTrayIconState), protocol-handler.ts (ProtocolHandler, buildDeepLink, buildShellLink), update-manager.ts (UpdateManager), widget-engine.ts (WidgetEngine, getWidgetTemplate), notification-bridge.ts (NotificationBridge, buildStreakAtRiskNotification, buildDailySummaryNotification), and shortcut-manager.ts (ShortcutManager, normalizeAccelerator). In plain terms: window/tray lifecycle, OS-level protocol (deep-link) handling, an auto-update path, desktop widgets, native notification bridging, and global shortcuts.
  • @oshun/shell-achievements (libs/oshun/shell-achievements/) supplies the cross-domain accomplishment layer. Its src/ contains achievement-engine.ts (AchievementEngine, InMemoryAchievementStore), achievement-definitions.ts (getAchievementsByDomain, getCrossDomainAchievements, getSecretAchievements), challenge-templates.ts (getChallengesByDomain, getCrossDomainChallenges), and social-accountability.ts (analyzePartnershipHealth, computeCheckInSchedule, computeEngagementScore, generateAccountabilityInsights).

Offline substrate — @oshun/offline#

The PWA and mobile offline behavior is not bespoke per app — it rides on the real @oshun/offline library (libs/oshun/offline/), which the legacy shell-composition list also omitted. Its src/index.ts re-exports types, storage, cache, retry, queue, and connectivity. The concrete building blocks are the OfflineSyncQueue class (queue.ts) and the OfflineCache class (cache.ts), over typed contracts SyncQueueItem<TPayload>, RetryPolicy, and ConnectivityState (types.ts). This is the substrate the service worker's queueBackgroundAction and the mobile recovery-after-drift logic both lean on, so offline retry and sync behave consistently across web and mobile.

One route map, six domains#

Deep links and per-platform paths are defined once in libs/oshun/navigation/src/routes.ts and consumed by every surface. The scheme is fixed (OSHUN_SCHEME = 'oshun'), and the domain block of OSHUN_ROUTE_MAP maps all six domains uniformly to a webPath, a mobilePath, and a deepLinkBase (lines 96–129):

Domain webPath mobilePath deepLinkBase
tara /domains/tara /domains/tara oshun://tara
veritas /domains/veritas /domains/veritas oshun://veritas
nyx /domains/nyx /domains/nyx oshun://nyx
arete /domains/arete /domains/arete oshun://arete
nisaba /domains/nisaba /domains/nisaba oshun://nisaba
metis /domains/metis /domains/metis oshun://metis

That nisaba and metis carry the same shape as the other four is the navigation-layer corroboration that the /domains/* namespace is genuinely six-wide. A single edit to routes.ts keeps web links, mobile tab paths (OSHUN_TAB_PATHS), and deep links coherent — which is exactly why a new domain becomes routes plus a DomainRouteExperience branch, not a new application. The full assembly story is in the architecture companion at Product Surfaces.

Public Web and Distribution#

V1 includes the public distribution surface:

  • A public marketing website with product positioning, conversion paths, and semantic SEO. Marketing / landing lives at apps/oshun/web/src/app/landing/; legal copy lives at the markdown-only apps/oshun/legal/.
  • Open Graph, social-sharing metadata, preview assets, and public-to-auth app funnel tracking (per-domain OG images such as /og/domain-<id>.png are emitted from DOMAIN_META in the [domainId] route).
  • Apple App Store and Google Play submission assets, listing content, screenshots, privacy labels, and review preparation — wired through the render-store-assets.mjs / validate-store-submission.mjs mobile tooling.
  • Universal Links, Android App Links, TestFlight, Play Store internal/beta testing, a minimum-version strategy, forced-update rules, and external deep-link E2E coverage — the App-Links side declared in app.json associatedDomains (iOS) and the auto-verified intentFilters (Android).

Why it works this way#

  • One web app, one mobile app, one shell. Centralizing navigation, the assistant, routines, achievements, and the offline substrate in libs/oshun/shell-* and @oshun/offline means adding a domain is a routing and experience change, not a new app. The six-wide routes.ts map is what keeps a single deep-link edit coherent across web, mobile, and the BFF.
  • Reachability and launch status are separate concerns. A /domains/<id> route can exist and render while the domain registry still reports the domain as beta or planned. Encoding launch state in domain-registry/src/registry.ts (and filtering it via getAvailableDomains()) keeps the routing layer honest: the shell can show fewer domains post-hydration without any route being deleted.
  • Fail loud over fake. The content-service surface throws NotConfiguredError rather than fabricate generations when no provider is configured, and its cloud deploy is honestly marked [~]. That candor is the intended posture across surfaces — a reachable-but-gated surface says so rather than pretending to ship.