How HolaOS Manages Configuration Settings: A Layered Runtime System Explained

HolaOS centralizes runtime configuration in a JSON document with environment variable overrides, providing a three-layer precedence system (file → env → defaults) for flexible, container-ready deployments.

The HolaOS configuration system, implemented in runtimeConfigPath() and related utilities in runtime/api-server/src/runtime-config.ts, gives operators two ways to control application behavior: a persistent JSON file for reproducible state and environment variables for dynamic, containerized environments. This article examines the complete configuration flow from disk to strongly-typed runtime object.

How Configuration Loading Works

HolaOS loads configuration through three distinct stages that progressively merge sources and validate the result.

Stage 1: Loading the Raw JSON Document

The loadRuntimeConfigDocument() function determines where to look for configuration by calling runtimeConfigPath(). This path resolver checks the HOLABOSS_RUNTIME_CONFIG_PATH_ENV environment variable first, then falls back to the default sandbox state directory. If the file exists, it parses the JSON; otherwise, it returns an empty document structure.

// Internal implementation detail from runtime/api-server/src/runtime-config.ts#L70-L78
// Path resolution prioritizes HOLABOSS_RUNTIME_CONFIG_PATH_ENV
const configPath = runtimeConfigPath(); // → sandbox/state/runtime-config.json or custom path
const rawDocument = loadRuntimeConfigDocument(configPath);

Stage 2: Merging with Environment Variables

The loadRuntimeConfigPayload() function extracts nested sections (runtime, providers, integrations, capabilities) and normalizes each field. For every property, it applies precedence-based resolution: JSON value first, then environment variable fallback, finally hard-coded default.

Key environment variables include:

  • HOLABOSS_SANDBOX_AUTH_TOKEN_ENV
  • HOLABOSS_USER_ID_ENV
  • HOLABOSS_MODEL_PROXY_KEY_ENV

The firstEnvValue() helper implements this fallback chain. Boolean and list values receive specialized normalization through normalizeBool() and normalizeStringList() to handle string representations consistently.

// From runtime/api-server/src/runtime-config.ts#L122-L158
// Environment variables override JSON values only when JSON is absent
const mergedPayload = loadRuntimeConfigPayload({
  preferEnv: false, // JSON takes precedence
});

Stage 3: Building the Typed Config Object

The resolveProductRuntimeConfig() function constructs a ProductRuntimeConfig instance from the merged payload. This stage performs runtime validation: when requireAuth: true, the function throws if no authentication token is present; similarly for requireUser.

// From runtime/api-server/src/runtime-config.ts#L103-L112
const config = resolveProductRuntimeConfig({
  requireAuth: true,   // Validates HOLABOSS_SANDBOX_AUTH_TOKEN presence
  requireUser: true,   // Validates HOLABOSS_USER_ID presence
});

Accessing Configuration in Application Code

Components retrieve configuration through two patterns depending on their architecture.

Direct Function Calls

Simple utilities call resolveProductRuntimeConfig() directly. The runtimeConfigHeaders() function uses this approach to build HTTP headers for downstream services, ensuring updates propagate immediately to outgoing requests.

import { runtimeConfigHeaders } from "@/runtime/api-server/src/runtime-config";

const headers = runtimeConfigHeaders({ requireAuth: true });
// Produces: { "X-API-Key": "...", "X-Holaboss-User-Id": "..." }

fetch("https://model-proxy.example/api", { headers });

Injectable Service Pattern

API handlers use FileRuntimeConfigService, which encapsulates loading, status checking, and updating behind a clean interface. This class abstracts the underlying file operations and re-loading logic.

import { FileRuntimeConfigService } from "@/runtime/api-server/src/runtime-config";

const configService = new FileRuntimeConfigService();

// Read-only access to current configuration
const current = await configService.getConfig();

// Check if configuration exists on disk
const exists = await configService.status(); // → { exists: boolean }

Updating Configuration at Runtime

The updateRuntimeConfigDocument() function receives partial payloads from client requests, merges them into the existing JSON structure while preserving unknown fields, and persists the result via writeRuntimeConfigDocument().

After saving, the service calls resolveProductRuntimeConfig() to refresh the in-memory view, ensuring subsequent reads reflect the updated state.

// From runtime/api-server/src/runtime-config.ts#L88-L97
await configService.updateConfig({
  desktop_browser_enabled: true,
  desktop_browser_url: "https://my-browser.local",
  unknown_field: "preserved", // Not in TypeScript interface but kept in JSON
});

Configuration Precedence Explained

HolaOS implements deterministic, predictable layering:

Precedence Source Typical Use Case
1 (Highest) runtime-config.json Reproducible deployments, version-controlled defaults
2 Environment variables (HOLABOSS_*) Container secrets, dynamic cloud assignments
3 (Lowest) Hard-coded defaults DEFAULT_MODEL = "gpt-5.4"

This design satisfies two operational modes: static file management for development and GitOps workflows, and environment injection for Kubernetes, Docker, and serverless platforms.

Complete Configuration Example

// Full workflow: load, inspect, modify, and apply configuration
import {
  resolveProductRuntimeConfig,
  FileRuntimeConfigService,
  runtimeConfigHeaders,
} from "@/runtime/api-server/src/runtime-config";

// 1. Resolve with validation
const cfg = resolveProductRuntimeConfig({
  requireAuth: true,
  requireUser: true,
});

console.log(cfg.model);           // → "gpt-5.4" (from JSON or default)
console.log(cfg.authToken);       // → from HOLABOSS_SANDBOX_AUTH_TOKEN_ENV if JSON absent

// 2. Service-based updates
const service = new FileRuntimeConfigService();
await service.updateConfig({ model: "gpt-5.5" });

// 3. Headers automatically reflect changes
const headers = runtimeConfigHeaders({ requireAuth: true });

Summary

  • Configuration lives in runtime-config.json under the sandbox state directory, with path override via HOLABOSS_RUNTIME_CONFIG_PATH_ENV
  • Three-stage loading: raw document → env merging → typed validation via resolveProductRuntimeConfig()
  • Environment variables provide fallback values when JSON fields are absent, not override
  • FileRuntimeConfigService encapsulates read, status-check, and update operations for dependency injection
  • Updates preserve unknown JSON fields and immediately refresh the resolved configuration
  • runtimeConfigHeaders() ensures downstream service calls always use current settings

Frequently Asked Questions

How do I override a HolaOS setting without modifying the JSON file?

Set the corresponding environment variable. HolaOS checks HOLABOSS_* variables after reading the JSON document, using firstEnvValue() to apply fallbacks. For example, export HOLABOSS_MODEL_PROXY_KEY_ENV=sk-... provides the API key without touching runtime-config.json.

What happens if runtime-config.json does not exist?

loadRuntimeConfigDocument() returns an empty document structure, and all configuration resolves from environment variables and hard-coded defaults. The system does not fail; it operates in "env-only" mode until a configuration file is created.

How does HolaOS handle configuration updates without restarting?

The FileRuntimeConfigService.updateConfig() method writes changes to disk and immediately calls resolveProductRuntimeConfig() to rebuild the typed configuration object. Components reading through the service or calling runtimeConfigHeaders() receive updated values on their next access—no restart required.

Where is the configuration file located by default?

runtimeConfigPath() returns sandbox/state/runtime-config.json relative to the working directory unless HOLABOSS_RUNTIME_CONFIG_PATH_ENV is set. This default path supports HolaOS's sandbox isolation model while remaining overrideable for custom deployments.

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 →