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

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

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

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:

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, which verifies hasUsableLLMProvider and isLLMProviderConfigured logic, and demonstrated in 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 with YAML and environment variable support
  • Hierarchical loading: 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. 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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →