# Understanding the L1-L4 Layered Configuration System in AIOS Pro

> Explore the L1-L4 layered configuration system in AIOS Pro. Understand how each layer overwrites previous settings for deterministic runtime configuration.

- Repository: [SynkraAI/aios-core](https://github.com/synkraai/aios-core)
- Tags: deep-dive
- Published: 2026-02-16

---

**AIOS Pro resolves runtime configuration through a deterministic five-level hierarchy where L1 (Framework) → L2 (Project) → Pro → L3 (App) → L4 (Local), with each successive layer overwriting the previous according to deep-merge rules defined in ADR-PRO-002.**

The **L1-L4 layered configuration system in AIOS Pro** provides a structured approach to managing environment-specific settings while maintaining team-wide consistency. Implemented in the `SynkraAI/aios-core` repository, this architecture ensures that global defaults remain stable, shared policies are enforceable, and individual developers can safely customize their environments without polluting version control.

## The Five-Level Configuration Hierarchy

AIOS Pro evaluates configuration through five distinct layers, though only the first four (L1-L4) are present in every project. Each layer serves a specific purpose and is merged in a deterministic order, with higher layers overwriting lower ones based on the merge rules in *ADR-PRO-002*.

| Layer | File (relative to project root) | Purpose | Typical contents |
|------|--------------------------------|---------|-------------------|
| **L1 – Framework** | [`.aios-core/framework-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/framework-config.yaml) | Read-only defaults shipped with the AIOS package | Global metadata, default performance limits, built-in resource locations |
| **L2 – Project** | [`.aios-core/project-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/project-config.yaml) | Team-wide settings that live in source control | Project name, shared resource quotas, CI/CD integration points |
| **L3 – App** (optional) | [`aios-app.config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/aios-app.config.yaml) (inside an app folder) | Per-app customisation – only loaded when `options.appDir` is supplied | UI themes, feature flags that differ between micro-apps |
| **L4 – Local** | [`.aios-core/local-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/local-config.yaml) (git-ignored) | **Developer-specific, machine-specific overrides** (secrets, IDE preferences, local tooling) | `${ENV_VAR}` interpolation allowed, secret tokens, personal IDE selections |

## How Configuration Inheritance Works in AIOS Pro

The inheritance mechanism in AIOS Pro is implemented in [`.aios-core/core/config/config-resolver.js`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/config-resolver.js) and follows a strict loading and merging protocol.

### Loading and Resolution Order

The entry point `resolveConfig(projectRoot, options)` first checks whether the repository is in legacy mode (detecting [`core-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/core-config.yaml)). If not, it delegates to `loadLayeredConfig()` to orchestrate the hierarchy.

The merge sequence follows this exact order:

```javascript
// Pseudo-code from loadLayeredConfig() in config-resolver.js
config = deepMerge(L1, L2)          // L2 overrides L1
config = deepMerge(config, Pro)     // Pro layer overrides previous
config = deepMerge(config, L3)      // optional app overrides
config = deepMerge(config, L4)      // L4 overrides everything above it
config = deepMerge(config, L5)      // user-wide overrides (outside L1-L4 scope)

```

### Deep Merge Strategy and Rules

The actual merging logic resides in [`.aios-core/core/config/merge-utils.js`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/merge-utils.js) and applies different strategies based on data type:

*   **Scalars (string, number, boolean)** – *Last-wins* semantics apply. The value from the higher layer completely replaces the lower layer value.
*   **Objects** – Deep-merged recursively. Nested properties from higher layers override specific keys in lower layer objects without discarding sibling properties.
*   **Arrays** – Replaced entirely by the higher-level array, unless the configuration key ends with the special suffix `+append`, which triggers concatenation instead of replacement.
*   **`null` values** – When a higher layer explicitly sets a key to `null`, that key is deleted from the resulting configuration.

### Validation and Environment Handling

After each layer loads, the resolver enforces strict validation rules:

*   **Environment Pattern Linting** – The `lintEnvPatterns` function (in [`env-interpolator.js`](https://github.com/SynkraAI/aios-core/blob/main/env-interpolator.js)) ensures that only **L4 (Local)** configuration files may contain `${VAR}` placeholders. Attempting to use environment variable interpolation in L1, L2, or L3 triggers a validation warning.
*   **JSON-Schema Validation** – Each layer has its own schema file ([`framework-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/framework-config.schema.json), [`project-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/project-config.schema.json), [`local-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/local-config.schema.json), [`user-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/user-config.schema.json)). The `validateConfig` function checks layer-specific constraints and collects warnings in `result.warnings`, accessible via the CLI command `aios config validate --level L4`.

When `options.debug === true`, `loadLayeredConfig()` builds a `sources` map that records which specific layer (L1-L4) supplied each configuration key, enabling precise provenance tracking via `aios config show --debug`.

## Practical Implementation Example

To resolve a project's effective configuration with full provenance tracking:

```javascript
const { resolveConfig } = require('.aios-core/core/config/config-resolver');

// Resolve a project's final config (debug mode shows provenance)
const { config, sources, warnings } = resolveConfig('/path/to/my-project', {
  debug: true,
  appDir: '/path/to/my-project/apps/dashboard',
});

console.log('Effective config →', config);
console.log('Where each key came from →', sources);
if (warnings.length) console.warn('Config warnings:', warnings);

```

**Step-by-step override example:**

1.  **L1 (Framework)** supplies default `metadata.framework_name = "AIOS‑FullStack"` and `performance_defaults.max_concurrent_operations = 4`.
2.  **L2 (Project)** overrides the performance setting: `performance_defaults.max_concurrent_operations = 8`.
3.  **L4 (Local)** provides developer-specific overrides:

    ```yaml
    performance_defaults:
      max_concurrent_operations: 16   # L4 overrides L2

    ide:
      selected:
        - vscode
        - claude-code
    ```

4.  **Final merged result:** `max_concurrent_operations = 16` (from L4), `metadata.framework_name = "AIOS‑FullStack"` (from L1), and the IDE selection from L4, with all other L1/L2 values preserved.

## Key Source Files for the L1-L4 System

| File | Role | Link |
|------|------|------|
| [`config-resolver.js`](https://github.com/SynkraAI/aios-core/blob/main/config-resolver.js) | Detects legacy vs layered mode, orchestrates loading, validation, and source tracking | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/config-resolver.js) |
| [`merge-utils.js`](https://github.com/SynkraAI/aios-core/blob/main/merge-utils.js) | Implements the deep-merge algorithm used for inheritance | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/merge-utils.js) |
| [`config-cache.js`](https://github.com/SynkraAI/aios-core/blob/main/config-cache.js) | TTL-based cache for already-resolved configs (optimises repeated calls) | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/config-cache.js) |
| [`env-interpolator.js`](https://github.com/SynkraAI/aios-core/blob/main/env-interpolator.js) | Handles `${VAR}` interpolation and linting of env patterns | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/env-interpolator.js) |
| [`schemas/framework-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/schemas/framework-config.schema.json) | JSON-Schema for L1 | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/schemas/framework-config.schema.json) |
| [`schemas/project-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/schemas/project-config.schema.json) | JSON-Schema for L2 | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/schemas/project-config.schema.json) |
| [`schemas/local-config.schema.json`](https://github.com/SynkraAI/aios-core/blob/main/schemas/local-config.schema.json) | JSON-Schema for L4 (local) | [🔗](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/core/config/schemas/local-config.schema.json) |
| [`tests/config/fixtures/local-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/tests/config/fixtures/local-config.yaml) | Example L4 file used in the test suite | [🔗](https://github.com/SynkraAI/aios-core/blob/main/tests/config/fixtures/local-config.yaml) |

## Summary

- The **L1-L4 layered configuration system in AIOS Pro** provides a deterministic five-level hierarchy (L1 Framework → L2 Project → Pro → L3 App → L4 Local) where each layer overwrites the previous according to deep-merge rules.
- **L1** contains read-only framework defaults, **L2** stores team-wide project settings, **L3** handles optional per-app customizations, and **L4** holds git-ignored, developer-specific overrides including secrets.
- The `resolveConfig()` function in [`config-resolver.js`](https://github.com/SynkraAI/aios-core/blob/main/config-resolver.js) orchestrates loading, while [`merge-utils.js`](https://github.com/SynkraAI/aios-core/blob/main/merge-utils.js) implements the inheritance logic: scalars use last-wins, objects deep-merge, arrays replace (unless using `+append`), and `null` deletes keys.
- **Environment variable interpolation** is restricted to L4 only, enforced by [`env-interpolator.js`](https://github.com/SynkraAI/aios-core/blob/main/env-interpolator.js), and each layer undergoes JSON-Schema validation to ensure configuration integrity.

## Frequently Asked Questions

### What happens if L4 local-config.yaml conflicts with L2 project-config.yaml?

When the same key exists in both files, **L4 always wins**. According to the merge logic in [`merge-utils.js`](https://github.com/SynkraAI/aios-core/blob/main/merge-utils.js), the deterministic loading order (L1 → L2 → Pro → L3 → L4) means that L4's values overwrite any previous layer's settings for scalar values, while nested objects are deep-merged with L4's properties taking precedence. This ensures that developer-specific overrides in [`.aios-core/local-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/local-config.yaml) (which is git-ignored) can safely supersede team-wide policies without affecting other team members.

### Can I use environment variables in AIOS Pro configuration files?

**Only in L4 (Local) configuration.** The [`env-interpolator.js`](https://github.com/SynkraAI/aios-core/blob/main/env-interpolator.js) module enforces a strict linting rule via `lintEnvPatterns` that restricts `${VAR}` interpolation to [`.aios-core/local-config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/.aios-core/local-config.yaml). If you attempt to use environment variable placeholders in L1 (Framework), L2 (Project), or L3 (App) configs, the validator will emit warnings. This security measure ensures that secrets and machine-specific values stay out of source control while allowing developers to reference local environment variables for personal tooling and API tokens.

### How do I debug which configuration layer is providing a specific value?

Enable **debug mode** in the `resolveConfig()` options. When you call `resolveConfig(projectRoot, { debug: true })`, the `loadLayeredConfig()` function in [`config-resolver.js`](https://github.com/SynkraAI/aios-core/blob/main/config-resolver.js) builds a `sources` map that records the provenance of every configuration key. This map indicates whether each value originated from L1, L2, L3, or L4. You can also use the CLI command `aios config show --debug` to inspect the effective configuration alongside its source layers, making it easy to trace why a particular setting has its current value.

### Is the L3 App layer required for all AIOS Pro projects?

**No, L3 is optional.** The App layer ([`aios-app.config.yaml`](https://github.com/SynkraAI/aios-core/blob/main/aios-app.config.yaml)) is only loaded when the `options.appDir` parameter is explicitly supplied to `resolveConfig()`. This design supports micro-frontend or micro-service architectures where individual applications within a monorepo need distinct UI themes or feature flags. If you are building a single-application project or do not specify an app directory, the configuration resolver skips L3 entirely and proceeds directly from the Pro layer to L4 (Local), simplifying the inheritance chain.