config/docs/index.md

142 lines
3.9 KiB
Markdown
Raw Permalink Normal View History

---
title: config
description: Layered configuration management for the Core framework with file, environment, and in-memory resolution.
---
# config
`forge.lthn.ai/core/config` provides layered configuration management for applications built on the Core framework. It resolves values through a priority chain -- defaults, file, environment variables, and explicit `Set()` calls -- so that the same codebase works identically across local development, CI, and production without code changes.
## Module Path
```
forge.lthn.ai/core/config
```
Requires **Go 1.26+**.
## Quick Start
### Standalone usage
```go
package main
import (
"fmt"
config "forge.lthn.ai/core/config"
)
func main() {
cfg, err := config.New() // loads ~/.core/config.yaml if it exists
if err != nil {
panic(err)
}
// Write a value and persist it
_ = cfg.Set("dev.editor", "vim")
_ = cfg.Commit()
// Read it back
var editor string
_ = cfg.Get("dev.editor", &editor)
fmt.Println(editor) // "vim"
}
```
### As a Core framework service
```go
import (
config "forge.lthn.ai/core/config"
"forge.lthn.ai/core/go/pkg/core"
)
app, _ := core.New(
core.WithService(config.NewConfigService),
)
// The config service loads automatically during OnStartup.
// Retrieve it later via core.ServiceFor[*config.Service](app).
```
## Package Layout
| File | Purpose |
|-----------------|----------------------------------------------------------------|
| `config.go` | Core `Config` struct -- layered Get/Set, file load, commit |
| `env.go` | Environment variable iteration and prefix-based loading |
| `service.go` | Framework service wrapper with lifecycle (`Startable`) support |
| `config_test.go`| Tests following the `_Good` / `_Bad` / `_Ugly` convention |
## Dependencies
| Module | Role |
|-----------------------------------|-----------------------------------------|
| `forge.lthn.ai/core/go` | Core framework (`core.Config` interface, `ServiceRuntime`) |
| `forge.lthn.ai/core/go-io` | Storage abstraction (`Medium` for reading/writing files) |
| `forge.lthn.ai/core/go-log` | Contextual error helper (`E()`) |
| `github.com/spf13/viper` | Underlying configuration engine |
| `gopkg.in/yaml.v3` | YAML serialisation for `Commit()` |
## Configuration Priority
Values are resolved in ascending priority order:
1. **Defaults** -- hardcoded fallbacks (via `Set()` before any file load)
2. **File** -- YAML loaded from `~/.core/config.yaml` (or a custom path)
3. **Environment variables** -- prefixed with `CORE_CONFIG_` by default
4. **Explicit Set()** -- in-memory overrides applied at runtime
Environment variables always override file values. An explicit `Set()` call overrides everything.
## Key Access
All keys use **dot notation** for nested values:
```go
cfg.Set("a.b.c", "deep")
var val string
cfg.Get("a.b.c", &val) // "deep"
```
This maps to YAML structure:
```yaml
a:
b:
c: deep
```
## Environment Variable Mapping
Environment variables are mapped to dot-notation keys by:
1. Stripping the prefix (default `CORE_CONFIG_`)
2. Lowercasing
3. Replacing `_` with `.`
For example, `CORE_CONFIG_DEV_EDITOR=nano` resolves to key `dev.editor` with value `"nano"`.
You can change the prefix with `WithEnvPrefix`:
```go
cfg, _ := config.New(config.WithEnvPrefix("MYAPP"))
// MYAPP_SETTING=secret -> key "setting"
```
## Persisting Changes
`Set()` only writes to memory. Call `Commit()` to flush changes to disk:
```go
cfg.Set("dev.editor", "vim")
cfg.Commit() // writes to ~/.core/config.yaml
```
`Commit()` only persists values that were loaded from the file or explicitly set via `Set()`. Environment variable values are never leaked into the config file.
## Licence
EUPL-1.2