---
title: Unified shell
description: How the web app composes authentication, the sidebar, the organization switcher, and application mounts into one navigable product surface.
---

The unified shell is what users actually see: the web app (`apps/web`) that composes every application module into one navigable product. It provides the chrome - sidebar, header, organization switcher - and mounts applications at `/:organization/:application`.

### The route tree

The shell is built on TanStack Start file-based routing (`apps/web/src/routes`):

```txt
routes/
├── _authenticated.tsx              auth guard: redirects unauthenticated users
├── _authenticated/index.tsx        the launcher (application cards)
├── _authenticated/$organization.tsx  org shell: sidebar + header + outlet
└── _authenticated/$organization/$application.tsx  mounts an application
```

The `_authenticated` group is the shell's front door. Its guard checks the session server-side before any shell page renders.

### The route lifecycle

```mermaid
sequenceDiagram
    participant Browser
    participant Guard as _authenticated guard
    participant Org as $organization layout
    participant App as $application route
    participant Registry as app-registry

    Browser->>Guard: request /:organization/:application
    Guard->>Guard: getSessionFn - require session
    Browser->>Org: beforeLoad resolveOrganizationFn
    Org->>Org: verify slug is an active membership
    Org->>Org: render sidebar + header
    Browser->>App: beforeLoad verify app availability
    App->>Registry: resolveApplicationMount(slug)
    Registry-->>App: lazy component or "not available" state
```

### The organization shell

`routes/_authenticated/$organization.tsx` renders the shell for one organization:

- `beforeLoad` calls `resolveOrganizationFn` with the slug, which runs the request scope server-side. If the slug is not one of the user's active memberships, it redirects to `/` (which lands on the first organization).
- The layout composes `SidebarProvider` > `AppSidebar` + `SidebarInset` > `Header` + an `Outlet`.

The slug is verified server-side, so the shell never trusts the URL segment on its own.

### The sidebar and header

- **`AppSidebar`** (`apps/web/src/components/app-sidebar.tsx`) renders the brand, the organization switcher, and navigation. It takes the `organizationSlug` so links point into the current tenant.
- **`Header`** (`apps/web/src/components/header.tsx`) carries the page chrome and actions.

Both are built from `@africaos/ui` primitives - see [Frontend](/frontend/ui-library).

### The launcher

`routes/_authenticated/index.tsx` is the dashboard. It queries `RegistryService.listForOrganization` (through a server function) and renders one card per enabled application, using registry metadata - icon, color, name, description, route. Selecting a card navigates to `/:organization/:application`.

The launcher is fully data-driven. There is no application-specific code in it.

### Mounting an application

The `$application` route resolves the mount from `app-registry.tsx`:

- If the slug has a lazy mount, it renders that component with `organizationSlug` as the only prop.
- If not, it renders the "not available yet" state.

Each application renders through `ApplicationMount`, which is host-aware: inside the web app (embedded mode) it renders content only, because the shell already provides the sidebar. See [The shell package](/applications/anatomy-of-an-app) for how applications structure themselves.

### Composition responsibilities

The web app is the **composition root**. It wires together:

- `getSessionFn` and auth server functions from `@africaos/auth`.
- `resolveOrganizationFn` and org server functions from `@africaos/organizations`.
- The request runtime from `@africaos/request-context` (every server function runs through it).
- The application mounts from `@africaos/*` application packages.
- The shell primitives from `@africaos/shell`.

Application modules depend on the shell and platform packages, never on the web app. This keeps every vertical swappable.

### Next steps

- [Application anatomy](/applications/anatomy-of-an-app) - what an application module looks like inside.
- [Web app (frontend)](/frontend/web-app) - the web app's full structure.