A typed, observable, policy-driven configuration system for Go services.
Stop debugging configuration errors in production. Rigging gives you compile-time safety, runtime observability, and policy enforcement for your service configuration.
type Config struct {
Database struct {
Host string `conf:"required"`
Port int `conf:"default:5432,min:1024,max:65535"`
Password string `conf:"required,secret"`
} `conf:"prefix:database"`
}
cfg, err := rigging.NewLoader[Config]().
WithSource(sourcefile.New("config.yaml", sourcefile.Options{})).
WithSource(sourceenv.New(sourceenv.Options{Prefix: "APP_"})).
Load(ctx)
// Type-safe: cfg.Database.Port is an int, guaranteed by compiler
// Observable: Track where Database.Host came from (file vs env)
// Policy-driven: Port validated to be within 1024-65535 rangeConfiguration management in production services faces several challenges:
- Type Safety: String-based key access loses compile-time guarantees
- Observability: Difficult to trace where configuration values originated
- Validation: Business rules scattered throughout the codebase
- Testing: Global state makes configuration hard to test
- Precedence: Unclear which source wins when values conflict
Rigging addresses these through three core principles:
Define your configuration schema as Go structs. The compiler catches errors, your IDE provides autocomplete, and refactoring tools work correctly.
type Config struct {
Database struct {
Host string `conf:"required"`
Port int `conf:"default:5432"`
} `conf:"prefix:database"`
}
cfg, err := loader.Load(ctx)
// cfg.Database.Port is an int, guaranteed by the compiler
// No runtime type assertions neededTrack the source of every configuration value. Know exactly where each value came from for debugging and compliance.
prov, _ := rigging.GetProvenance(cfg)
for _, field := range prov.Fields {
log.Printf("%s from %s", field.FieldPath, field.SourceName)
}
// Output:
// Database.Host from file:config.yaml
// Database.Password from env:APP_DATABASE__PASSWORD
// Database.Port from defaultCapture configuration snapshots for debugging across environments:
snapshot, _ := rigging.CreateSnapshot(cfg)
rigging.WriteSnapshot(snapshot, "config-{{timestamp}}.json")
// Creates: config-20240115-103000.json with secrets redactedEnforce configuration policy at startup. Rigging supports both typed transforms (for normalization/canonicalization) and validation rules, so configuration is prepared and checked before your application runs.
type Config struct {
Environment string `conf:"required,oneof:prod,staging,dev"`
Database struct {
Host string `conf:"required"`
Port int `conf:"min:1024,max:65535"`
} `conf:"prefix:database"`
}
// Custom cross-field validation
loader.WithValidator(rigging.ValidatorFunc[Config](func(ctx context.Context, cfg *Config) error {
if cfg.Environment == "prod" && cfg.Database.Host == "localhost" {
return errors.New("production cannot use localhost")
}
return nil
}))
cfg, err := loader.Load(ctx)
// If we reach here, all validation passedUse WithTransformer(...) to normalize or derive typed values before tag validation, and WithValidator(...) for cross-field or business-rule checks after tag validation.
For example, a transformer can normalize " PROD " to "prod" before a oneof:prod,staging,dev tag check runs:
loader := rigging.NewLoader[Config]().
WithSource(sourceenv.New(sourceenv.Options{Prefix: "APP_"})).
WithTransformerFunc(func(ctx context.Context, cfg *Config) error {
cfg.Environment = strings.ToLower(strings.TrimSpace(cfg.Environment))
return nil
})| Feature | Rigging | Viper | envconfig |
|---|---|---|---|
| Type safety | Compile-time | Runtime | Compile-time |
| Multi-source | Explicit order | Implicit | Env only |
| Provenance | Full tracking | No | No |
| Validation | Tags + custom | Manual | Tags only |
| Secret redaction | Automatic | Manual | Manual |
| Global state | None | Singleton | None |
| Watch/reload | Custom sources | Built-in | No |
* Rigging provides the Watch() API for custom configuration sources. Built-in file and environment sources don't support watching yet.
| Capability | Status | Notes |
|---|---|---|
Loader.Watch() API |
Available | Reload pipeline and snapshots are supported |
Custom source watch (Source.Watch) |
Available | Implement change events in your source |
Built-in sourcefile watch |
Not supported yet | Returns ErrWatchNotSupported |
Built-in sourceenv watch |
Not supported yet | Returns ErrWatchNotSupported |
See Configuration Sources: Watch and Reload and the FAQ for current limitations and usage guidance.
go get github.com/Azhovan/rigging@latest- New service setup: Quick Start -> Configuration Sources ->
examples/basic - Validation-first startup policy: Quick Start: Fail-Fast Validation -> API Reference: Validation Semantics
- Debugging config incidents: Quick Start: Provenance -> Quick Start: Snapshots -> Configuration Patterns
- Migrating from Viper / envconfig: Comparison -> Quick Start: Key Mapping Rules -> Configuration Sources
- Custom source + reload loop: Configuration Sources: Custom Sources -> Configuration Sources: Watch and Reload -> API Reference: Watch and Reload
- Quick Start Guide - Get started with installation, basic usage, validation, and observability
- Configuration Sources - Learn about environment variables, file sources, custom sources, and watch/reload
- API Reference - Complete API documentation for all types, methods, and struct tags
- Configuration Patterns - Best practices and design patterns for organizing your configuration
- Examples - Complete working examples
Q: Why not just use Viper?
A: Viper uses map[string]interface{} which loses type safety. Rigging gives you compile-time guarantees and provenance tracking.
Q: Can I use this with existing config files? A: Yes! Rigging supports YAML, JSON, and TOML files. Just define a struct that matches your file structure.
Q: How do I handle secrets?
A: Mark fields with secret tag and load from environment variables. Secrets are automatically redacted in dumps.
Q: Does Rigging support hot-reload?
A: The loader.Watch() API is implemented and ready to use. However, built-in sources (sourcefile, sourceenv) don't emit change events yet. You can implement custom sources with watch support, or wait for file watching (planned via fsnotify).
Q: Is this production-ready? A: Rigging is designed for production use with comprehensive error handling, validation, and observability. The API is currently v0.x - expect minor breaking changes as we incorporate feedback from early adopters.
MIT License - see LICENSE file for details.
Contributions welcome! See CONTRIBUTING.md for guidelines.