Skip to content
Africa OS Knowledgebase
Esc
↑↓navigate↵open⌘Jpreview
On this page

Configuration

Central configuration - validated environment variables, product constants, feature flags, and the ApplicationConfig contract.

@africaos/config is the single source of truth for configuration. Environment variables are validated once at startup, product constants and feature flags live here, and the application-config contract that every app.config.ts satisfies is defined here too.

Environment variables

packages/config/env.config.ts uses defineEnv from the envin package with Zod schemas. It splits variables into shared, server, and client (VITE_-prefixed):

| Scope | Variable | Rule | | —— | ———————————————————–– | ——————————————— | ––––– | —————————– | | Shared | NODE_ENV | development | production | test, default development. | | Server | DATABASE_URL | Required, must be a valid URL. | | Server | BETTER_AUTH_SECRET | Required, at least 32 characters. | | Server | BETTER_AUTH_URL | Optional URL; blank values treated as absent. | | Server | GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Optional; enables Google login when present. | | Server | WORKOS_API_KEY / WORKOS_CLIENT_ID / WORKOS_REDIRECT_URI | Optional; enables WorkOS SSO when present. | | Client | VITE_APP_NAME | Default "Africa OS". | | Client | VITE_APP_VERSION | Default "0.0.0". | | Client | VITE_PLATFORM_URL | Optional URL. |

Key behaviors:

  • Required vs optional is encoded in the schema. DATABASE_URL and BETTER_AUTH_SECRET are required; everything else is optional so apps that do not need a capability (the landing page has no auth) still boot.
  • Blank values are treated as absent. optionalUrl() preprocesses empty strings to undefined, so copying .env.example (which leaves optional entries blank) does not break startup.
  • Validation is at boot time. A missing or malformed variable fails fast with a clear error rather than failing later in an obscure place.

The env object is the default export; code reads it as env.DATABASE_URL etc.

Product constants

packages/config/src/constants.ts:

export const AFRICA_OS = {
    name: "Africa OS",
    version: "0.0.0",
    namespace: "@africaos",
} as const;

Feature flags

packages/config/src/features.ts declares the platform’s capability flags:

export const features = {
    offline: true,
    desktop: true,
    multiTenant: true,
    realtime: false,
} as const;

The ApplicationConfig contract

packages/config/src/types.ts defines the types every application’s app.config.ts uses:

export interface ApplicationFeature {
    slug: string;
    label: string;
    icon?: string;
    path?: string; // application-relative route when implemented
}

export interface ApplicationConfig {
    id: string;
    name: string;
    icon?: string;
    color?: string;
    description?: string;
    route?: string;
    capabilities: {
        offline: boolean;
        desktop: boolean;
        mobile: boolean;
    };
    features: ReadonlyArray<ApplicationFeature>;
}

export const defineAppConfig = (config: ApplicationConfig): ApplicationConfig => config;

defineAppConfig gives applications full type checking on their self-description, so the registry contract is enforced at build time. A feature with a path is implemented and becomes navigation; one without a path is declared surface (for example a planned module).

Where config is used

  • The database pool reads env.DATABASE_URL.
  • The auth provider reads env.BETTER_AUTH_SECRET, BETTER_AUTH_URL, and the OAuth/WorkOS credentials.
  • Apps read VITE_APP_NAME/VITE_APP_VERSION for the header chrome.
  • Application packages call defineAppConfig in their app.config.ts.

Next steps

Last updated on August 30, 2026