This page is the catalog of every place a human actually touches Oshun V1 — the web app, the mobile apps, the operator consoles, the messaging channels, the desktop companion, and the supporting service and content surfaces — and how they are assembled from a small set of shared shell libraries rather than rebuilt per surface. It serves engineers deciding which app or lib a feature belongs in, and product/design leads who need the real inventory of what ships and what is still gated. It sits one layer below the High-Level Architecture (which shows the request path) and beside Customer-Facing Domains (which the surfaces render); it is part of the V1 architecture set hubbed at ../ARCHITECTURE.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 via the shared shell and the single BFF. The exceptions are explicit
and small (Metis runs its own app/microservice stack), and the operator world is
deliberately split into its own apps with strict isolation. Surfaces are
overwhelmingly real code: every named app below 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.
Surface Inventory#
The customer-facing and operator apps live under apps/oshun/ (plus the Metis
stack under apps/metis/). Every entry below was confirmed against the real
package.json name field.
| Surface | Path | Package | Notes |
|---|---|---|---|
| Customer Web | apps/oshun/web/ |
@oshun/web |
Next.js app; PWA, all six domains, Studio subroute |
| Customer Mobile | apps/oshun/mobile/ |
@oshun/mobile |
Expo + Expo Router; scheme: oshun |
| Oshun BFF | apps/oshun/bff/ |
@oshun/bff |
Fastify; single API front door (§ in BFF page) |
| Admin Web | apps/oshun/admin/ |
@oshun/admin |
Operator review/safety/governance console |
| Admin Mobile | apps/oshun/admin-mobile/ |
@oshun/admin-mobile |
Expo Router; on-call operator app |
| Tenant Console | apps/oshun/tenant-admin/ |
@oshun/tenant-admin |
Standalone Next app (§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 surface (§26) |
| Content Service | apps/oshun/content-service/ |
@oshun/content-service-app |
Deployable HTTP service shell for agentic content (§3.2) |
| Legal (docs) | apps/oshun/legal/ |
none — markdown only | privacy-policy.md, terms-of-service.md; no app |
| Metis Web / Admin / API / Worker | apps/metis/{web,admin,api-gateway,worker}/ |
(Metis stack) | Only domain with its own dedicated app stack |
| Metis Mobile | apps/metis/mobile/ |
(Metis stack) | A separate Metis mobile app distinct from Oshun mobile |
Two of these are not mentioned in the prose elsewhere in V1 and deserve calling out explicitly:
apps/oshun/content-service(@oshun/content-service-app) is a real, deployable surface, not a UI page. Itssrc/main.tsbootscreateContentHttpServer/createDurableContentServicefrom@oshun/content-serviceover a real Iris-routed writer and a calibratedJudgePanel(@oshun/content-quality-judge). It is fail-loud: with no model provider configured (ANTHROPIC_API_KEYabsent) it throws aNotConfiguredErrorat startup rather than fabricating generations, and the cloud deploy of it is honestly marked[~]in the backlog.apps/oshun/legalis a markdown-only surface (privacy-policy.md,terms-of-service.md) with nopackage.json. It is correctly referenced as the home of legal copy, but it is a docs folder, not a buildable app — the marketing/landing experience itself lives in the web app.
Metis is the only domain with its own apps. Every other customer-facing domain renders through
apps/oshun/webandapps/oshun/mobilevia the shared shell and the BFF. The one nuance to the older "renders solely throughapps/oshun/mobile" framing is that a separateapps/metis/mobile/exists — so on mobile, Metis is not solely the Oshun mobile app either.
Customer Web — apps/oshun/web#
The customer web app (@oshun/web) is the flagship surface and the reference
implementation of the dual-namespace pattern described below.
- The V1 dashboard at
apps/oshun/web/src/app/page.tsxis Tara-centered with mixed-domain recommendations and reason labels. - A desktop-grade multi-panel workspace at
apps/oshun/web/src/app/(workspace)/for continuity, search, reading, evidence inspection, study, notes, and assistant work. - A collapsible assistant dock (
src/components/assistant-dock/), a command palette (src/components/command-palette/), and mixed-domain recommendations (src/components/recommendations/). - Universal search at
src/app/search/page.tsxspanning rituals, practices, concepts, passages, claims, sources, notebooks, collections, programs, sky events, courses, lessons, and generated artifacts. - Other top-level customer routes live under
src/app/{lilith,operator,studio,events,explore,library,activity,landing,…}/. - Performance budgets are enforced at
apps/oshun/web/lighthouse-budget.json(LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1), with Playwright coverage for keyboard-only flows, accessibility, visual regression, and resilience on every critical journey.
The dual-namespace pattern — and the truth about all six domains#
Customer domains ship two coordinated namespaces, both V1, for distinct audiences:
- Consumer hub plus depth at
src/app/{tara,arete,veritas,nyx,nisaba,metis}/*. These are the Lilith-design-system "rooms" (TaraRoom,AreteRoom,VeritasRoom,NyxRoom,NisabaRoom, and Metis surfaces) — presentational, contemplative, optimized for first-time and returning consumers. The Nisaba hub atsrc/app/nisaba/already carries deep subroutes (compare,daily,graph,lexicon,manuscript,notebook,notebooks,plan,scholar), and the Metis hub atsrc/app/metis/carriesassessment,byom,courses,ingest,lesson,lessons,session,tutor, andupload. - Power-user deep tools at
src/app/domains/{…}/*— heavier client workspaces for scholars, power users, customer-mode operators, and editorial/curation roles. They are reachable through deep links and in-context entry points from the consumer hub, not the canonical first read of a domain.
The important correction to earlier documentation: 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 the DomainPreviewBannerForPath). The catch-all extends coverage to
nisaba and metis:
apps/oshun/web/src/navigation/routes.tsdeclaresWEB_DOMAIN_IDS = ['tara','veritas','nyx','arete','nisaba']andWEB_NAVIGABLE_DOMAIN_IDS = [...WEB_DOMAIN_IDS, 'metis'].apps/oshun/web/src/app/domains/[domainId]/page.tsxgates onisWebNavigableDomainIdand ships per-domain SEO metadata inDOMAIN_METAfor all six ids, includingnisaba("Primary Texts & Scholarship") andmetis("Learning & Tutoring").apps/oshun/web/src/components/DomainRouteExperience.tsxexplicitly 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 older claim that
"Nisaba and Metis do not yet ship parallel /domains/* namespaces" is stale and
contradicted both by this code and by the general dual-namespace pattern itself.
What is true — and is a 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." Reachable route ≠ launched domain.
PWA — real, implemented, test-covered#
The web app is a genuine Progressive Web App, not a manifest stub:
apps/oshun/web/public/manifest.jsondeclaresname: "OSHUN",short_name: "OSHUN",start_url: "/?surface=pwa",scope: "/",display: "standalone", andtheme_color: "#f1ebdd"(the parchment tone of the Lilith design system). Its app shortcuts map directly to the six domains ("Meditate" → Tara, "Read" → Veritas, "Sky Tonight" → Nyx, "Check In" → Arete, "Study" → Nisaba, "Learn" → Metis).apps/oshun/web/public/sw.jsis the real service worker.apps/oshun/web/src/lib/pwa-service-worker.tsimplements the registration, update, and offline plumbing. Its real exports includeregisterServiceWorker(options),applyWaitingServiceWorker(...), andresolvePwaUpdateRouteContext(currentPath)(which returns aPwaUpdateRouteContextwith aPwaUpdateRouteModeof'reading' | 'standard'so an update prompt never interrupts a deep-read flow). It also exportsqueueBackgroundActionfor background sync andrequestServiceWorkerMemberDataClear()for privacy teardown.- It is covered by
apps/oshun/web/src/lib/pwa-service-worker.test.tsand a policy-level test atapps/oshun/web/src/__tests__/pwa-service-worker-policy.test.ts.
Customer Mobile — apps/oshun/mobile#
@oshun/mobile is a real Expo + Expo Router project, not a thin wrapper:
app.jsonsetsexpo.name: "OSHUN",slug: "oshun-mobile",scheme: "oshun"(the deep-link scheme shared with the navigation lib),ios.bundleIdentifier: "com.oshun.mobile", and Universal-LinkassociatedDomainsincludingapplinks:oshun.app,applinks:www.oshun.app, andapplinks:app.oshun.com.- The tab IA is real Expo Router under
app/(tabs)/:index,explore,activity,library, andprofile(with_layout.tsxand co-located tests such asindex.test.tsandprofile.test.tsx). - Deep-link routing for all top-level domains and high-value object types
runs through
@oshun/navigation(see the shared route map below). - Per-domain UX includes continuation cards, recovery-after-drift logic, two-way calendar sync (Google / Apple / Outlook), and camera/microphone capture with per-domain redaction.
- Wears, widgets, and Live-Activity-style controls come from
@oshun/shell-wearable. - Hand-off between mobile and desktop runs through Iris-backed continuation state (see Iris — Assistant Memory Substrate).
The mobile app's test and release tooling is concrete and worth naming
specifically — the older "mobile E2E" framing understates it. Under
apps/oshun/mobile/scripts/ you'll find 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). EAS build/submit configuration backs the
actual store pipeline.
Shared Consumer Shell — libs/oshun/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 shell is built from:
@oshun/shell-core— home / explore / activity / library / notifications / profile / settings / assistant entry points.@oshun/shell-assistant— the collapsible assistant dock and context hand-off.@oshun/shell-routines— cross-domain routine and continuation cards.@oshun/shell-wearable— the watch / widget surface.@oshun/navigation— current-domain persistence, deep links, route-level analytics, and command-surface registration.@oshun/design-tokensand@oshun/ui— the design system and component library.
Two real shell libs the older docs omit#
The composition list above looked exhaustive but was not. Two additional real shell libraries exist and ship code; neither was named in the earlier architecture or feature docs:
@oshun/shell-desktop(libs/oshun/shell-desktop/) is an entire Electron-class desktop companion surface — a whole surface category neither legacy doc mentions. Itssrc/containsdesktop-engine.ts,window-manager.ts,tray-companion.ts,protocol-handler.ts,update-manager.ts,widget-engine.ts,notification-bridge.ts, andshortcut-manager.ts. 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. Itssrc/containsachievement-engine.ts,achievement-definitions.ts,challenge-templates.ts, andsocial-accountability.ts.
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 (enqueue, getAll, and a retry-aware
flush(handler, policy) in queue.ts) and the OfflineCache class, over typed
contracts SyncQueueItem, RetryPolicy, and ConnectivityState. This is the
substrate the service worker's queueBackgroundAction and the mobile
recovery-after-drift logic both lean on.
Shared Route Map — @oshun/navigation#
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 maps all six
domains uniformly to a webPath, a mobilePath, and a deepLinkBase:
| 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 |
The fact that nisaba and metis carry the same webPath/deepLinkBase shape
as the other four is the navigation-layer corroboration that the /domains/*
namespace is genuinely six-wide. (The deep-link prefix is mirrored on the BFF
side too: libs/oshun/domain-registry/src/registry.ts records the matching
deep-link-prefix such as oshun://tara and the BFF base path
/api/oshun/domains/tara per domain.) Mobile tab paths derive from the same map
via OSHUN_TAB_PATHS, so a single edit to routes.ts keeps web links, mobile
tabs, and deep links coherent.
Operator Surfaces#
Operator tooling is deliberately split from customer surfaces and from each other, with strict isolation between platform operators and per-tenant admins.
- Oshun Admin (Web) —
apps/oshun/admin/(@oshun/admin). Review queues, safety/support, privacy/DSAR, persona/model/rights/provenance, incidents, and launch governance. It depends on@oshun/contracts,@oshun/design-tokens,@oshun/navigation, and@oshun/shell-assistant— reusing the same shell primitives as the customer apps rather than a separate component stack. - Oshun Admin (Mobile) —
apps/oshun/admin-mobile/(@oshun/admin-mobile). This is a full Expo Router app in its own right, not an afterthought. Itsapp/carrieslogin.tsx,step-up.tsx,unauthorized.tsx, a(operator)/tab group (inbox,incidents,index,more), and aworkspaceroute; itssrc/spansauth,incidents,offline,review,urgent-queue,notifications,support,telemetry,accessibility,feedback, and more. It is built for on-call review, approvals, incident triage, and step-up-authenticated smaller-screen workflows. - Oshun Studio — lives under
apps/oshun/web/src/app/studio/, not as a separateapps/oshun/studioapp. Authoring, editorial, asset, taxonomy, localization, and versioning UIs share the Oshun shell and design system. See Oshun Studio — Authoring, Editorial, Curation. - Oshun Tenant Console —
apps/oshun/tenant-admin/(@oshun/tenant-admin), a standalone Next app (§20). Tenant-scoped admin shell with strict isolation from operator-only Admin: members, roles, content scopes, SSO/SCIM, audit, integrations, and lifecycle comms. The tenancy graph and policy inheritance are described in Data Architecture and Tenancy.
Messaging Channels#
Two surfaces extend Oshun into Telegram, tracked under §26 and detailed in Messaging Channels:
- Telegram Bot —
apps/oshun/telegram-bot/(@oshun/telegram-bot). - Telegram Mini App —
apps/oshun/telegram-miniapp/(@oshun/telegram-miniapp), an embedded web surface.
Public Web and Distribution#
- Marketing / landing lives at
apps/oshun/web/src/app/landing/; legal copy lives atapps/oshun/legal/(the markdown-only docs surface noted above). - Distribution covers app-store assets, Universal Links / Android App Links,
TestFlight, Play internal/beta tracks, a minimum-version strategy, and
forced-update rules — the mobile side of which is wired through the
validate-store-submission.mjs/render-store-assets.mjstooling and theassociatedDomainsdeclared inapp.json.
Why It Works This Way#
- One web app, one mobile app, one shell. Centralizing navigation, the
assistant, routines, and the offline substrate in
libs/oshun/shell-*and@oshun/offlinemeans a new domain becomes routes and aDomainRouteExperiencebranch, not a new application. The six-wideroutes.tsmap is what lets a single deep-link change stay 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 asbetaorplanned. Encoding launch state indomain-registry/src/registry.ts(and filtering it viagetAvailableDomains()) keeps the routing layer honest: the shell can show fewer domains post-hydration without any route being deleted. - Operators are isolated by app, not by route guard alone. Splitting Admin Web, Admin Mobile, and the Tenant Console into distinct apps — with platform operators and tenant admins never sharing a console — is the surface-level expression of the tenancy isolation model.
- Fail loud over fake. The content-service surface throws
NotConfiguredErrorrather than fabricating generations when no provider is configured, and the docs mark its cloud deploy[~]. That candor is the intended posture across surfaces: a reachable-but-gated surface says so.
Related#
- High-Level Architecture — where these surfaces sit in the request path.
- Customer-Facing Domains — what the domain routes render, and the domain registry that gates launch status.
- Substrate — Iris — the continuation state behind desktop↔mobile hand-off.
- Oshun Studio — Authoring, Editorial, Curation — the Studio subroute surface.
- Messaging Channels — the Telegram bot and mini app.
- Data Architecture and Tenancy — the isolation model behind the operator and tenant surfaces.