Examples
Complete, production-ready examples demonstrating different use cases of confkit.
Basic Configuration
The simplest possible confkit setup — load typed config from environment variables.
package main import ( "log" "github.com/MimoJanra/confkit" ) type Config struct { Port int `env:"PORT" default:"8080" validate:"min=1,max=65535"` Host string `env:"HOST" default:"localhost"` Database string `env:"DATABASE_URL" validate:"required" secret:"true"` } func main() { cfg, err := confkit.Load[Config](confkit.FromEnv()) if err != nil { log.Fatal(confkit.Explain(err)) } log.Printf("Starting on %s:%d\n", cfg.Host, cfg.Port) }
# Run
export DATABASE_URL="postgres://user:pass@localhost/db"
go run main.go Multiple Sources with Precedence
Sources are checked in order — first source to provide a value wins. Environment variables override YAML, which overrides defaults.
type Config struct { Port int `yaml:"port" env:"PORT" default:"8080"` Database string `yaml:"database" env:"DATABASE_URL"` LogLevel string `yaml:"log_level" env:"LOG_LEVEL" default:"info"` } func main() { // Precedence: env vars override YAML override defaults cfg, err := confkit.Load[Config]( confkit.FromYAML("config.yaml"), // Base config confkit.FromEnv(), // Runtime overrides ) if err != nil { log.Fatal(confkit.Explain(err)) } }
# config.yaml
port: 3000
database: postgres://localhost/mydb
log_level: debug Nested Structures
Organize related settings into sub-structs. confkit recursively scans nested structs.
type DatabaseConfig struct { Host string `yaml:"host" env:"DB_HOST" default:"localhost"` Port int `yaml:"port" env:"DB_PORT" default:"5432"` Username string `yaml:"user" env:"DB_USER"` Password string `yaml:"password" env:"DB_PASSWORD" secret:"true"` } type CacheConfig struct { Enabled bool `yaml:"enabled" env:"CACHE_ENABLED" default:"true"` TTL int `yaml:"ttl" env:"CACHE_TTL" default:"3600"` } type Config struct { DB DatabaseConfig Cache CacheConfig LogLevel string `env:"LOG_LEVEL" default:"info"` } func main() { cfg, err := confkit.Load[Config]( confkit.FromYAML("config.yaml"), confkit.FromEnv(), ) // Access nested: cfg.DB.Host, cfg.Cache.TTL _ = cfg; _ = err }
Custom Validation
Register per-field validators with WithValidator. Reference them with validate:"custom=name".
type Config struct { AdminEmail string `env:"ADMIN_EMAIL" validate:"custom=email"` } func main() { cfg, err := confkit.LoadWithOptions[Config]( confkit.WithSource(confkit.FromEnv()), confkit.WithValidator("email", func(val string) error { if !strings.Contains(val, "@") { return fmt.Errorf("must be a valid email address") } return nil }, ) _ = cfg; _ = err }
Cross-Field Validation (Model Validators)
Validate relationships between fields — Pydantic-style model validators.
type TLSConfig struct { Enabled bool `env:"TLS_ENABLED"` CertPath string `env:"TLS_CERT_PATH"` KeyPath string `env:"TLS_KEY_PATH"` } cfg, err := confkit.LoadWithOptions[TLSConfig]( confkit.WithSource(confkit.FromEnv()), confkit.WithModelValidator(func(cfg *TLSConfig) error { if cfg.Enabled && cfg.CertPath == "" { return fmt.Errorf("tls_cert_path is required when tls_enabled is true") } if cfg.Enabled && cfg.KeyPath == "" { return fmt.Errorf("tls_key_path is required when tls_enabled is true") } return nil }, )
Format Validators
Built-in validators for common formats — no external libraries.
type Config struct { AdminEmail string `env:"ADMIN_EMAIL" validate:"required,email"` ServiceURL string `env:"SERVICE_URL" validate:"http_url"` ClientIP string `env:"CLIENT_IP" validate:"ip"` ServiceID string `env:"SERVICE_ID" validate:"uuid"` Port int `env:"PORT" validate:"port"` APIKey string `env:"API_KEY" validate:"required,len=32,alphanum" secret:"true"` Region string `env:"REGION" validate:"regex=^[a-z]+-[a-z]+-[0-9]+$"` }
Environment Variable Prefixes
Use prefix on a nested struct to automatically scope its environment variables.
type DatabaseConfig struct { Host string `env:"HOST" default:"localhost"` Port int `env:"PORT" default:"5432"` } type Config struct { // Fields in this struct automatically get DB_ prefix DB DatabaseConfig `prefix:"DB_"` } func main() { // Reads from: DB_HOST, DB_PORT (not just HOST, PORT) cfg, err := confkit.Load[Config](confkit.FromEnv()) _ = cfg; _ = err }
DB_HOST=db.example.com DB_PORT=3306
String Interpolation
Reference other fields in default values using ${FIELD_NAME} syntax.
type Config struct { AppName string `env:"APP_NAME" default:"MyApp"` AppVersion string `env:"APP_VERSION" default:"1.0.0"` // Will expand to "MyApp v1.0.0" AppTitle string `env:"APP_TITLE" default:"${APP_NAME} v${APP_VERSION}" ` BaseURL string `env:"BASE_URL" default:"https://api.example.com"` // Will expand to full URL UsersURL string `env:"USERS_URL" default:"${BASE_URL}/users"` }
Hot Reload / File Watching
Use LoadWithWatcher to get live config updates when the file changes — without restarting your service.
func main() { cfg, watcher, err := confkit.LoadWithWatcher[Config]( "config.yaml", confkit.FromEnv(), confkit.FromYAML("config.yaml"), ) if err != nil { log.Fatal(err) } watcher.AddListener(func(oldCfg, newCfg any, err error) { if err != nil { log.Printf("Reload failed: %v", err) return } log.Printf("Config reloaded!\n") // Apply new config to running services }) watcher.Start() startServer(cfg) }
Secret Redaction
Mark fields with secret:"true" — confkit automatically redacts them in error messages, audit logs, and config dumps.
type Config struct { APIKey string `env:"API_KEY" validate:"required" secret:"true"` Password string `env:"PASSWORD" validate:"required" secret:"true"` Username string `env:"USERNAME"` } func main() { cfg, err := confkit.Load[Config](confkit.FromEnv()) if err != nil { // Secrets are automatically redacted fmt.Println(confkit.Explain(err)) // Output: // APIKey // error: field is required // source: env (API_KEY=***) } _ = cfg }
Audit Logging
See exactly which source provided each field value. Great for debugging config precedence issues.
cfg, err := confkit.LoadWithOptions[Config]( confkit.WithSource(confkit.FromYAML("config.yaml")), confkit.WithSource(confkit.FromEnv()), confkit.WithAuditLogger(func(entries []confkit.AuditEntry) { for _, e := range entries { log.Printf("%-30s ← %s: %s", e.Field, e.Source, e.Value) } }, ) // Output: // Server.Port ← env: 9000 // Server.Host ← yaml: 0.0.0.0 // Database.Password ← env: <redacted>
Multi-File Config Merging
Merge base defaults with environment-specific overrides. First file wins for each key.
cfg, err := confkit.Load[Config]( confkit.FromYAMLFiles( "config/base.yaml", // shipped defaults "config/production.yaml", // prod-specific values "config/local.yaml", // developer overrides (git-ignored) ), confkit.FromEnv(), // highest priority )
# base.yaml port: 8080 log_level: info db: host: localhost port: 5432 --- # production.yaml log_level: warn db: host: db.prod.internal
Result: db.host = db.prod.internal, db.port = 5432, log_level = warn.
Cloud Sources
Kubernetes ConfigMap
import "github.com/MimoJanra/confkit/k8s" func main() { cfg, err := confkit.Load[Config]( k8s.FromKubernetesConfigMap("default", "app-config"), ) _ = cfg; _ = err }
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
port: "8080"
log_level: "info" AWS Systems Manager Parameter Store
import "github.com/MimoJanra/confkit/aws" func main() { cfg, err := confkit.Load[Config]( aws.FromAWSSSMParameterStore("/prod/app/config"), ) _ = cfg; _ = err }
HashiCorp Vault
import ( "os" "github.com/MimoJanra/confkit/vault" ) func main() { auth := vault.VaultTokenAuth(os.Getenv("VAULT_TOKEN")) cfg, err := confkit.Load[Config]( vault.FromVault("https://vault.example.com", auth, "/secret/myapp"), ) _ = cfg; _ = err }
AWS Secrets Manager
import "github.com/MimoJanra/confkit/aws" func main() { cfg, err := confkit.Load[Config]( aws.FromAWSSecretsManager("prod/app-secrets"), ) _ = cfg; _ = err }
Consul KV
import "github.com/MimoJanra/confkit/consul" func main() { cfg, err := confkit.Load[Config]( consul.FromConsulWithOptions("consul.example.com:8500", "token", "dc1"), ) _ = cfg; _ = err }
etcd
import "github.com/MimoJanra/confkit/etcd" func main() { cfg, err := confkit.Load[Config]( etcd.FromEtcdWithPrefix( []string{"etcd1.example.com:2379", "etcd2.example.com:2379"}, "/myapp", ), ) _ = cfg; _ = err }
Observability
Prometheus Metrics
import "github.com/MimoJanra/confkit/prometheus" m := prometheus.NewMetrics(prometheus.DefaultRegisterer) cfg, err := confkit.LoadWithOptions[Config]( confkit.WithSource(confkit.FromEnv()), m.Hook(), ) // Tracks: confkit_loads_total, confkit_load_duration_seconds, confkit_errors_total
OpenTelemetry Tracing
import "github.com/MimoJanra/confkit/otel" cfg, err := otel.Load[Config](ctx, tracer, confkit.FromYAML("config.yaml"), confkit.FromEnv(), ) // Creates span "confkit.Load" // Attributes: confkit.sources, confkit.success
Advanced: Custom Source
Implement the Source interface to load config from any backend.
type CustomSource struct { data map[string]string } func (s *CustomSource) Name() string { return "custom" } func (s *CustomSource) Lookup(ctx context.Context, field *confkit.FieldInfo) (any, bool, error) { val, ok := s.data[field.Path] return val, ok, nil } func main() { source := &CustomSource{ data: map[string]string{ "Port": "9000", "Database": "postgres://localhost/db", }, } cfg, err := confkit.Load[Config](source) _ = cfg; _ = err }
Full Production Setup
A complete production-grade configuration: nested structs, hot reload, audit logging, model validation.
package main import ( "log" "time" "github.com/MimoJanra/confkit" ) type DatabaseConfig struct { Host string `yaml:"host" env:"DB_HOST" default:"localhost"` Port int `yaml:"port" env:"DB_PORT" default:"5432" validate:"min=1,max=65535"` Username string `yaml:"username" env:"DB_USER" validate:"required"` Password string `yaml:"password" env:"DB_PASSWORD" validate:"required" secret:"true"` SSL bool `yaml:"ssl" env:"DB_SSL" default:"true"` Timeout time.Duration `yaml:"timeout" env:"DB_TIMEOUT" default:"30s"` } type ServerConfig struct { Host string `yaml:"host" env:"SERVER_HOST" default:"0.0.0.0"` Port int `yaml:"port" env:"SERVER_PORT" default:"8080" validate:"min=1,max=65535"` ReadTimeout time.Duration `yaml:"read_timeout" env:"SERVER_READ_TIMEOUT" default:"10s"` } type Config struct { Database DatabaseConfig Server ServerConfig LogLevel string `yaml:"log_level" env:"LOG_LEVEL" validate:"oneof=debug,info,warn,error" default:"info"` Environment string `yaml:"env" env:"ENVIRONMENT" validate:"oneof=dev,staging,prod" default:"dev"` } func main() { cfg, watcher, err := confkit.LoadWithWatcher[Config]( "config.yaml", confkit.FromYAML("config.yaml"), confkit.FromEnv(), ) if err != nil { log.Fatal(confkit.Explain(err)) } log.Printf("Running in %s mode\n", cfg.Environment) watcher.AddListener(func(_, newCfg any, err error) { if err != nil { log.Printf("Reload failed: %v\n", err) return } log.Printf("Config reloaded\n") updateServices(*newCfg.(*Config)) }) watcher.Start() startServer(cfg) } func updateServices(cfg Config) { /* apply new config to running services */ } func startServer(cfg Config) { log.Printf("Listening on %s:%d\n", cfg.Server.Host, cfg.Server.Port) }