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
gem install legion-settingsOr add to your Gemfile:
gem 'legion-settings'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.
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.
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(', ')}")
endwatch! 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.
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=redisHot reload picks up changes to this file as part of the same reload! flow.
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 valuesTypes 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 raisingThe 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.
- Ruby >= 3.4
legion-json
Apache-2.0