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_applicationsrow ininfra/database/seeds/development.sqland runpnpm db:seed. - At runtime:
RegistryService.setEnabled(organizationId, "<app>", true)orOrganizationService.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.tsregisters without warnings (pnpm db:register-apps). -
pnpm nx run @africaos/<app>:typecheckpasses. - 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 testpasses for the new package.