# How OpenMAIC Manages LLM Provider Configuration: Server-Side Architecture Explained

> OpenMAIC server-side architecture centralizes LLM provider configuration via YAML files and environment variables. Discover how it secures API keys and exposes resolver APIs.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: architecture
- Published: 2026-09-11

---

**OpenMAIC centralizes LLM provider configuration in a dedicated server-side module that loads YAML files, overrides settings with environment variables, and exposes secure resolver APIs to keep API keys hidden from clients while providing metadata to the UI.**

Managing credentials for multiple Large Language Model providers requires a careful balance between flexibility and security. In OpenMAIC, all LLM provider settings are governed by the **provider-config module** located at [`lib/server/provider-config.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/provider-config.ts), which implements a hierarchical configuration system supporting both file-based and environment-based credential management. This architecture ensures that sensitive API keys never leave the server while still allowing clients to query available models and capabilities.

## Configuration Loading Pipeline

The LLM provider configuration follows a strict loading order that combines declarative files with runtime environment overrides.

### YAML File Initialization

At server startup, the system reads an optional YAML configuration file via `loadYamlFile('server-providers.yml')`. This file can declare provider definitions including API keys, base URLs, allowed model lists, and proxy settings.

Environment variables then override these values using the **LLM_ENV_MAP** constant defined at lines 63-78. This map associates prefixes like `OPENAI` with provider IDs like `openai`, enabling the system to scan for variables following the pattern:

- `<PREFIX>_API_KEY` – Authentication credentials
- `<PREFIX>_BASE_URL` – Custom endpoint URLs
- `<PREFIX>_MODELS` – Comma-separated list of allowed models

The `loadEnvSection` function (lines 37-49) processes these mappings and merges them with the YAML baseline.

### Process-Level Caching

The combined configuration is stored in a process-singleton Map called `_configs` to ensure fast subsequent reads without re-parsing files or re-scanning environment variables. This cache is populated during the `buildConfig` phase (lines 99-110).

## ServerProviderEntry Interface

Each provider configuration conforms to the `ServerProviderEntry` interface defined between lines 25-40:

```typescript
interface ServerProviderEntry {
  apiKey: string;
  baseUrl?: string;
  models?: string[];
  proxy?: string;
  enabled?: boolean;   // operator-wide force-off switch
}

```

The `enabled` boolean allows operators to administratively disable providers without removing their configuration entirely, while the optional `models` array enforces an allow-list of specific model identifiers.

## Secure Public API for LLMs

OpenMAIC exposes a curated set of functions that provide necessary metadata to clients while protecting secrets. According to the implementation in [`lib/server/provider-config.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/provider-config.ts), these resolvers handle the distinction between server-managed and unmanaged providers.

### Listing Configured Providers

The `getServerProviders()` function returns a sanitized view of all managed LLM providers, exposing only the allowed model lists while omitting API keys and base URLs:

```typescript
import { getServerProviders } from '@/lib/server/provider-config';

// Returns { openai: { models: ['gpt-4', 'gpt-3.5-turbo'] }, anthropic: {} }
const providers = getServerProviders();

```

### Credential Resolution

The `resolveApiKey()` and `resolveBaseUrl()` functions implement a fallback strategy. If a provider ID exists in the server configuration, the server-side values are returned; otherwise, the functions accept optional client-supplied values for unmanaged providers:

```typescript
import { resolveApiKey, resolveBaseUrl } from '@/lib/server/provider-config';

// Managed provider: returns server-configured key
const openaiKey = resolveApiKey('openai');

// Unmanaged provider: uses client-provided fallback
const customKey = resolveApiKey('my-custom-llm', 'client-key-123');

```

Additional utilities include `isServerConfiguredProvider()`, which checks administrative configuration status, and `resolveProxy()`, which fetches server-side proxy URLs for specific providers.

## Model Pinning and Defaults

When operators specify `<PREFIX>_MODELS`, the first entry in the array becomes the **managed default** for that provider. Client-supplied models are validated against this allow-list in the `resolveApiKey` and `resolveBaseUrl` functions. This mechanism prevents users from accessing models that the operator has not explicitly approved, even if the underlying API key technically supports them.

## Disable-by-Operator Capability

The module implements a `disabled` map populated by `collectDisabledProviders()` that allows forced deactivation of specific providers within capability sections. Disabled providers remain visible in listings—marked with `{ disabled: true }`—so the UI can render them appropriately, but the runtime skips them during actual LLM calls. This provides clear user feedback while enforcing operational restrictions.

## Security Architecture

OpenMAIC maintains strict separation between server-side secrets and client-visible metadata. The configuration flow ensures:

1. **Secret containment**: API keys reside only in the `_configs` Map on the server
2. **Environment flexibility**: Operators can inject credentials via environment variables without modifying code
3. **Client safety**: Public APIs expose model lists but never expose `apiKey` or `baseUrl` values

This pattern is validated in [`tests/store/settings-validation.test.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/tests/store/settings-validation.test.ts), which verifies `hasUsableLLMProvider` and `isLLMProviderConfigured` logic, and demonstrated in [`tests/web-search/route.test.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/tests/web-search/route.test.ts), which exercises the resolver functions in route handlers.

## Summary

- **Centralized configuration**: All LLM provider settings live in [`lib/server/provider-config.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/provider-config.ts) with YAML and environment variable support
- **Hierarchical loading**: [`server-providers.yml`](https://github.com/THU-MAIC/OpenMAIC/blob/main/server-providers.yml) provides defaults, while `LLM_ENV_MAP` enables environment variable overrides
- **Secure APIs**: `getServerProviders()`, `resolveApiKey()`, and `resolveBaseUrl()` protect secrets while exposing necessary metadata
- **Administrative controls**: Operators can disable providers globally or pin specific models using the `enabled` flag and `<PREFIX>_MODELS` arrays
- **Process caching**: The `_configs` Map eliminates redundant file system and environment scans after initial load

## Frequently Asked Questions

### How does OpenMAIC keep LLM API keys secure?

OpenMAIC stores API keys exclusively in the server-side `_configs` Map within [`lib/server/provider-config.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/provider-config.ts). The public `getServerProviders()` function deliberately omits the `apiKey` and `baseUrl` fields from its return type, exposing only model arrays and boolean flags. When client applications need to route requests through the server, they call `resolveApiKey()` and `resolveBaseUrl()`, which execute within the server context and never transmit credentials to the browser.

### Can I configure multiple LLM providers simultaneously?

Yes. The configuration system supports multiple providers through the [`server-providers.yml`](https://github.com/THU-MAIC/OpenMAIC/blob/main/server-providers.yml) file or by defining multiple entries in the `LLM_ENV_MAP`. Each provider requires a unique prefix (such as `OPENAI` or `ANTHROPIC`) mapped to its provider ID. The `getServerProviders()` function returns an object containing all configured providers, allowing the application to switch between them based on capability requirements or user preferences.

### What happens if I specify both a YAML file and environment variables?

OpenMAIC applies a layered configuration strategy. The `buildConfig` process first loads values from [`server-providers.yml`](https://github.com/THU-MAIC/OpenMAIC/blob/main/server-providers.yml), then the `loadEnvSection` function overrides those values with any matching environment variables found using the `LLM_ENV_MAP` prefixes. This allows operators to commit non-sensitive base URLs to version control while keeping API keys in secure environment variables.

### How do I restrict which models users can access?

Specify the `<PREFIX>_MODELS` environment variable with a comma-separated list of allowed model identifiers. The first model in this list becomes the default for that provider. The `resolveApiKey` and related resolver functions validate client-supplied models against this allow-list, rejecting requests for models not explicitly included in the server configuration.