---
title: Configuration
description: 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`:

```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:

```ts
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:

```ts
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

- [Application registry](/architecture/application-registry) - how `app.config.ts` feeds the registry.
- [Getting started](/getting-started) - the `.env.example` walkthrough.