How ai-memory Ensures a Single Configuration Read Path at Startup

ai-memory guarantees a single configuration read path by loading all runtime settings exactly once through Config::load, storing the result in an immutable Arc<Config>, and sharing that reference throughout the entire codebase.

The ai-memory project avoids a common pitfall in Rust applications: scattered environment variable reads that cause configuration drift. According to the source code in akitaonrails/ai-memory, the design intentionally eliminates any second read path by centralizing all configuration logic in one module and one function call.

The Core Design: One Load, Immutable Share

The configuration system is documented directly in crates/ai-memory-cli/src/config.rs. The module header explicitly states: "All settings are read exactly once at startup… and there is no second read path"【/cache/repos/github.com/akitaonrails/ai-memory/main/crates/ai-memory-cli/src/config.rs#L3-L6】.

This is enforced through three architectural decisions:

  • Single Config::load entry point – merges environment variables, TOML file, and defaults in one Figment-based call
  • No std::env::var elsewhere – eliminates hidden reads that caused bugs in earlier designs
  • Immutable Arc<Config> propagation – every subsystem receives a reference, never reloading

The Config struct holds every configurable field: data paths, server bind address, LLM provider options, timeouts, and more. Once constructed by Config::load, it never changes【/cache/repos/github.com/akitaonrails/ai-memory/main/crates/ai-memory-cli/src/config.rs#L24-L40】【/cache/repos/github.com/akitaonrails/ai-memory/main/crates/ai-memory-cli/src/config.rs#L125-L136】.

Loading Configuration Once in the CLI Entry Point

The single-read guarantee begins at program start. In crates/ai-memory-cli/src/lib.rs, the CLI entry point calls Config::load exactly once and wraps the result in Arc:

// crates/ai-memory-cli/src/lib.rs
let config = Arc::new(Config::load(config_path.as_deref(), data_dir)?);

This Arc<Config> is then passed to all subsystems. The ? operator ensures that any configuration error fails fast before the application proceeds【/cache/repos/github.com/akitaonrails/ai-memory/main/crates/ai-memory-cli/src/lib.rs#L64-L66】.

Propagating Configuration Without Reloading

Downstream modules receive the shared configuration reference. In crates/ai-memory-web/src/state.rs, the server state is constructed from the same Arc<Config>:

// crates/ai-memory-web/src/state.rs
pub fn new_state(config: Arc<Config>) -> State {
    State {
        bind: config.bind.clone(),
        server_url: config.server_url.clone(),
        // …other fields pulled from the same Config instance…
    }
}

No module calls Config::load or std::env::var. All values originate from the single load at startup.

Reading Configuration in LLM Providers

Even deeply nested components follow the same pattern. The LLM factory in crates/ai-memory-llm/src/factory.rs consumes configuration values without any file or environment access:

// crates/ai-memory-llm/src/factory.rs
let timeout = config.llm_timeout_secs;          // sourced from Config::load only
let provider = ProviderConfig::new(..., timeout);

This ensures that llm_timeout_secs comes from the same merged configuration as every other setting—no overrides, no hidden defaults, no divergence.

How Figment Enables Single-Path Loading

The Config::load function uses the Figment crate to merge three configuration sources in one call:

  1. AI_MEMORY_-prefixed environment variables
  2. Optional TOML configuration file (path passed as argument)
  3. Hardcoded defaults

This merge happens atomically. The resulting Config struct contains the final, resolved values. Because Figment handles all source prioritization internally, no other code needs to check environment variables or parse files.

Preventing the Bug That Motivated This Design

The module header in config.rs references a past bug: scattered env::var calls caused hidden configuration reads that were hard to trace and debug. The current design eliminates this class of error by construction—there is simply no API available to read configuration after startup.

Summary

  • Config::load in crates/ai-memory-cli/src/config.rs is the sole function that reads environment variables or configuration files
  • Immutable Arc<Config> carries resolved settings to every subsystem without reloading
  • No std::env::var calls exist outside the configuration module, preventing hidden reads
  • Figment-based merging handles all source prioritization in one atomic operation

Frequently Asked Questions

What happens if I need to reload configuration without restarting?

ai-memory does not support runtime configuration reloads. The single-read design trades dynamic updates for predictability and simplicity. Any configuration change requires a process restart to take effect.

Why use Arc<Config> instead of a global static?

Arc<Config> enables testability and explicit dependency injection. Tests can construct custom Config values and pass them to components without modifying global state. A static would require lazy initialization and could encourage hidden env::var reads.

Where are the default values defined?

Defaults are embedded in the Config struct's Figment integration within crates/ai-memory-cli/src/config.rs. They merge with environment variables and TOML values during the single load call, with explicit precedence: env vars override TOML, which overrides defaults.

How does this design affect binary size or performance?

The single-read pattern has negligible runtime cost—one allocation for the Arc and cheap reference copies thereafter. It eliminates repeated environment lookups and file system checks, likely improving performance over scattered reads. The main benefit is reliability: configuration is resolved once and never questioned again.

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 →