legion-settings
Configuration management module for the LegionIO framework. Loads settings from JSON files, directories, and environment variables. Provides a unified Legion::Settings[:key] accessor used by all other Legion gems.
Version: 1.3.27
Installation
gem install legion-settings
Or add to your Gemfile:
gem 'legion-settings'
Usage
require 'legion/settings'
Legion::Settings.load # loads defaults, env, DNS bootstrap, and nearest .legionio.env
Legion::Settings.load(config_dir: './settings') # also loads all .json files in the directory
Legion::Settings[:client][:hostname]
Legion::Settings.dig(:transport, :connection, :host)
[] and dig will auto-load settings on first access, and implicit access follows the same overlay/project-env/base precedence as explicit load.
Config Loading
Legion::Settings.load only consumes the paths you pass via config_file, config_dir, or config_dirs.
If a caller wants the canonical Legion search directories, use Legion::Settings::Loader.default_directories:
~/.legionio/settings/etc/legionio/settingson Unix-like systems%APPDATA%\\legionio\\settingson Windows whenAPPDATAis present
LegionIO uses those directories during daemon boot. Library consumers can choose to pass any directory set they want.
Each Legion module registers its own defaults via merge_settings during startup, and the nearest .legionio.env file is merged on top of base settings. Request overlays applied through with_overlay take highest precedence.
Hot Reload
Legion::Settings.reload! re-reads the config files that were previously loaded, reapplies module defaults and the nearest .legionio.env, re-resolves secret references, and returns a hash describing the changed keys.
changes = Legion::Settings.reload!
changes
# {
# "llm.default_model" => { old: "old-model", new: "new-model" }
# }
Callbacks run only when changes are detected:
Legion::Settings.on_reload do |changes|
Legion::Settings.logger.info("Settings changed: #{changes.keys.join(', ')}")
end
watch! installs a SIGHUP handler when the platform supports it. Repeated signals are coalesced through one background reload worker, so rapid SIGHUP bursts do not create unbounded reload threads.
Legion::Settings.watch! do |changes|
Legion::Settings.logger.info("Reloaded #{changes.size} setting(s)")
end
# Later, from a shell:
# kill -HUP <daemon_pid>
On platforms without HUP, watch! logs and returns without raising. Direct reload! remains available for API endpoints, tests, or environments that use a different process-control mechanism.
Project Environment Overrides
When present, the nearest .legionio.env file is loaded after base settings and module defaults. Dot notation maps to nested settings:
llm.default_model=claude-sonnet
cache.driver=redis
Hot reload picks up changes to this file as part of the same reload! flow.
Secret Resolution
Settings values can reference external secret sources using URI syntax. Three schemes are supported:
| Scheme | Format | Resolution |
|---|---|---|
vault:// |
vault://path/to/secret#key |
Reads static KV secrets from HashiCorp Vault via Legion::Crypt |
env:// |
env://ENV_VAR_NAME |
Reads from environment variable |
lease:// |
lease://name#key |
Reads from dynamic Vault leases via Legion::Crypt::LeaseManager |
Array values act as fallback chains — the first non-nil result wins:
{
"transport": {
"connection": {
"password": ["vault://secret/data/rabbitmq#password", "env://RABBITMQ_PASSWORD", "guest"]
}
}
}
Call Legion::Settings.resolve_secrets! to resolve all URIs in-place. In the LegionIO boot sequence this is called automatically after Legion::Crypt.start. The env:// scheme works even when Vault is not connected.
Legion::Settings.resolve_secrets!
# All vault://, env://, and lease:// references are now replaced with their resolved values
Schema Validation
Types are inferred automatically from default values. Optional constraints can be added:
Legion::Settings.merge_settings('mymodule', { host: 'localhost', port: 8080 })
Legion::Settings.define_schema('mymodule', { port: { required: true } })
Legion::Settings.validate! # raises ValidationError if any settings are invalid
# In development, warn instead of raising:
# Set LEGION_DEV=true or Legion::Settings.set_prop(:dev, true)
# validate! will warn through the configured logger instead of raising
Logging Defaults
The logging key includes a transport sub-section that controls whether log events are forwarded over the message bus:
{
"logging": {
"level": "info",
"format": "text",
"log_file": "./legionio/logs/legion.log",
"log_stdout": true,
"trace": true,
"async": true,
"include_pid": false,
"transport": {
"enabled": true,
"forward_logs": true,
"forward_exceptions": true
}
}
}
When transport.enabled is true, log events and unhandled exceptions are published to the AMQP bus so a central log consumer can aggregate them.
Requirements
- Ruby >= 3.4
legion-json
License
Apache-2.0