Skip to content
Africa OS Knowledgebase
Esc
navigateopen⌘Jpreview
On this page

Adding an application

The full recipe for a new application module - scaffold, register, mount, enable, and build the first feature.

This guide stands up a whole new application module (packages/applications/<app>), registers it with the platform, mounts it in the web app, and enables it for an organization. It assumes the platform is running (see Running the platform) and that you know the anatomy.

The five steps

Step 1 - scaffold the package

Create packages/applications/<app>/ modeled on retail:

packages/applications/<app>/
├── app.config.ts
├── package.json
├── tsconfig.json
├── project.json
├── vitest.config.ts
└── src/
    ├── index.ts
    ├── application.tsx
    ├── queryClient.ts
    ├── components/
    ├── features/
    └── routes/

Write app.config.ts with defineAppConfig (see Configuration) - the metadata the registry will store:

import { defineAppConfig } from "@africaos/config";

export default defineAppConfig({
  id: "<app>",
  name: "<App> OS",
  icon: "package",
  color: "#2563eb",
  description: "Your vertical's purpose",
  route: "/",
  capabilities: { offline: false, desktop: true, mobile: false },
  features: [],
});

Write a mountable root component (application.tsx) that computes the path relative to its mount prefix and renders a shell with the matching page - exactly like RetailApplication. Export it, the config, and the query client from index.ts.

Step 2 - install and register

pnpm install
pnpm db:register-apps

register-apps.ts reads the new app.config.ts, validates it, and upserts the application into platform.applications. Confirm with:

pnpm nx run @africaos/<app>:typecheck

Step 3 - mount in the web app

Add one lazy entry to apps/web/src/features/applications/app-registry.tsx:

"<app>": lazy(() =>
  import("@africaos/<app>").then((module) => ({
    default: module.XApplication,
  }))
),

The shell’s $application route will now render your application at /:organization/<app> whenever it is enabled and the user holds platform.applications.access.

Step 4 - enable it for an organization

The registry row alone does not grant access. Enable the application for the organizations you care about:

  • In the seed: add an organization_applications row in infra/database/seeds/development.sql and run pnpm db:seed.
  • At runtime: RegistryService.setEnabled(organizationId, "<app>", true) or OrganizationService.enableApplication.

The launcher lists only enabled applications, so an enabled org sees the card.

Step 5 - build the first feature

Follow Adding a feature to build the first domain slice, and list it in app.config.ts features with a path so it appears in the application’s navigation.

What you inherit

Because the application runs through the platform, you get authentication, tenancy, authorization (including the automatic platform.applications.access gate), the shell, and the data-driven launcher with no further work.

Verification checklist

  • app.config.ts registers without warnings (pnpm db:register-apps).
  • pnpm nx run @africaos/<app>:typecheck passes.
  • The lazy mount resolves in app-registry.tsx.
  • The application renders at /:organization/<app> once enabled.
  • Server functions scope to the active organization and guard with requirePermission.
  • pnpm test passes for the new package.

Last updated on August 18, 2026