config
Thread-safe configuration from TOML files, environment variables, command-line
arguments and registered defaults. Requires Go 1.27.1. The only runtime dependency
is lixenwraith/toml, pinned to merged commit
b59d068ff6a4. Testify and its dependencies are used only by tests.
package main
import (
"log"
"time"
"github.com/lixenwraith/config"
)
type Settings struct {
Port uint16 `toml:"port"`
Timeout time.Duration `toml:"timeout"`
}
func main() {
initial := Settings{Port: 8080, Timeout: 5 * time.Second}
cfg, err := config.NewBuilder().
WithTarget(&initial).
WithFile("config.toml").
WithEnvPrefix("APP_").
WithArgs([]string{"--port=9090"}).
Build()
if err != nil { log.Fatal(err) }
latest, err := cfg.AsStruct()
if err != nil { log.Fatal(err) }
log.Printf("%+v", latest.(*Settings))
}
# Service settings
timeout = "10s"
port = 8080
Precedence is CLI > environment > file > default. Register paths with Register
or structs with RegisterStruct; defaults define the types used to validate new
values. LoadFile, LoadEnv and LoadCLI replace their complete source. A failed
source load keeps its previous values. Removing an environment variable, CLI flag
or file key removes the old override on the next load.
Get returns a detached raw source value: environment and CLI values remain
strings, TOML integers are int64. GetTyped[T], Scan, ScanSource, ScanTyped[T]
and ScanMap provide checked conversions. Errors leave destinations unchanged;
missing struct fields retain their existing values. AsStruct returns a detached
snapshot on every call. Build fills WithTarget once; subsequent reloads never
mutate that caller-owned object.
Migration
- Only TOML files and
tomltags are supported. JSON/YAML parsers, format constants and alternate tag modes are removed. Existing format/tag option methods accept onlytoml(autoremains an alias for TOML for file options). - Numeric conversion rejects overflow, negative unsigned inputs, fractional integer conversion, non-finite floats and integer-to-float precision loss. Float64-to-float32 rounding within range remains supported. Full uint64 is supported in memory; saving a value above MaxInt64 fails without changing the destination file. Use an application-level string representation if needed.
- Strings can represent decimal numbers, booleans, comma-separated slices/arrays,
durations, RFC3339 timestamps, IP addresses, CIDR networks and URLs. Boolean to
string conversion uses
true/false. There are no user decode hooks or automaticencoding.TextUnmarshalercalls. - Setters and source loads reject values incompatible with registered defaults immediately. Check their errors. Untyped nil defaults leave the value type open; typed nil pointers retain their declared type. Unsupported values, non-finite floats and cyclic/deep graphs are rejected. Configuration value depth is 128.
- All public values, defaults, options and clones own their mutable containers.
Existing references returned from
AsStructare snapshots, not live objects. - Saves standardize formatting and retain loaded comments as a preamble. Comment positions are not preserved. Existing file permissions are retained; new files use 0600. Only registered paths are saved, so unknown file keys are omitted.
- Paths use dot-separated ASCII key segments. Numeric-only segments and overlapping
registrations such as
serverplusserver.portare rejected. Embedded fields are not promoted. Recursive struct schemas are unsupported.
Watching
changes := cfg.WatchWithOptions(config.DefaultWatchOptions())
defer cfg.StopAutoUpdate()
for event := range changes {
// Events are hints; always read the latest complete state.
log.Print(event)
latest, err := cfg.AsStruct()
if err != nil { log.Print(err); continue }
_ = latest.(*Settings)
}
The watcher fingerprints file contents, waits for a quiet debounce interval, then validates and publishes. Invalid edits, timeouts, deletion and blocked permission changes retain the last valid state. Cancelled, expired or superseded reloads cannot publish. Stop closes subscriptions synchronously; a new Watch call can restart watching. Notifications are buffered and best effort.
Documentation
- Quick start, builder, access
- Files and saving, environment, CLI
- Live reconfiguration, validation
- Architecture, audit evidence
Run go test -race ./... and go vet ./.... CI also executes the 386 suite,
cross-compiles for Windows and runs bounded numeric conversion fuzzing.
BSD-3-Clause; see LICENSE.