Struct tags in, a validated config out.
Fuda reads your YAML or JSON file, layers in defaults and environment overrides, resolves secrets and connection strings, and validates the result.
All of it is declared on the struct itself, with no glue code in main().
Read the documentation site for the complete beginner-first guide, or the package documentation for the API reference.
- One struct, one source of truth. Keys, defaults, env vars, secrets, and validation rules live next to the field they describe. Nothing is spread across a config struct, a flags package, and a validator call.
- Secrets without a secrets SDK.
Pull values from files, HTTP endpoints, or Vault straight into a field with
ref/refFrom. Compose connection strings from those values withdsn, with no manual string building. - Human input, typed output.
Write
10MiBor7din config and get anint64ortime.Durationback. Writedebugand get your own enum type back via theScannerinterface. - Fails loudly, not late.
validatetags check the fully loaded struct before your program starts. You get field-level errors instead of a nil pointer three hours into a shift. - Grows with you.
Start with
fuda.LoadFile, then reach for the builder for env prefixes, dotenv overlays, templated config, or live file/remote watching. None of that requires changing the struct you already wrote.
go get github.com/arloliu/fudaOne Load call resolves a secret, composes a DSN, validates the struct, and sets a dynamic default.
// LogLevel is a custom type; Scan lets fuda convert the default/YAML// string into it via the Scanner interface.typeLogLevelintconst (
LevelInfoLogLevel=iotaLevelDebug
)
func (l*LogLevel) Scan(srcany) error {
ifsrc=="debug" {
*l=LevelDebug
}
returnnil
}
typeConfigstruct {
AppNamestring`yaml:"app_name" validate:"required"`Envstring`yaml:"env" default:"dev" validate:"oneof=dev staging prod"`LogLevelLogLevel`yaml:"log_level" default:"info"`Hoststring`yaml:"host" default:"0.0.0.0" env:"APP_HOST"`Portint`yaml:"port" default:"8080" env:"APP_PORT" validate:"min=1,max=65535"`DBUserstring`yaml:"db_user" default:"app"`DBPasswordstring`ref:"file://secrets/db_password.txt"`DBHoststring`yaml:"db_host" default:"localhost"`DBNamestring`yaml:"db_name" default:"orders"`DSNstring`dsn:"postgres://${.DBUser}:${.DBPassword}@${.DBHost}/${.DBName}"`StartedAt time.Time
}
// SetDefaults runs after tags are applied, for defaults tags can't express.func (c*Config) SetDefaults() {
c.StartedAt=time.Now()
}
funcmain() {
varcfgConfigiferr:=fuda.LoadFile("config.yaml", &cfg); err!=nil {
varverr*fuda.ValidationErroriferrors.As(err, &verr) {
log.Fatalf("invalid config: %v", verr.Errors)
}
log.Fatal(err)
}
fmt.Printf("%s DSN: %s\n", cfg.AppName, cfg.DSN)
}Pair it with a minimal config.yaml:
app_name: orders-apidb_user: appWith secrets/db_password.txt holding the database password, Fuda resolves the ref, composes DSN, applies every default, validates the result, and stamps StartedAt before your program ever sees cfg.
APP_PORT=9090 overrides Port at runtime without touching the file.
The struct above already covers files, defaults, env overrides, secrets, DSN composition, validation, dynamic defaults, and custom types. Fuda also handles the rest of a real service's configuration.
typeConfigstruct {
Timeout time.Duration`yaml:"timeout"`Retention fuda.Duration`yaml:"retention"`CacheSize fuda.ByteSize`yaml:"cache_size"`
}timeout: 30sretention: 7dcache_size: 10MiBFuda parses 7d into a duration and 10MiB into a byte count, no manual parsing required.
fuda.Duration and fuda.ByteSize also marshal back to a readable string instead of raw nanoseconds or bytes.
- Environment prefixes.
WithEnvPrefix("APP_")matchesenvtags againstAPP_HOSTinstead ofHOST. - Dotenv overlays.
WithDotEnvFiles([]string{".env", ".env.local"})layers environment files before Fuda reads the struct. - Templated config.
WithTemplate(data)renders the YAML or JSON file as a Go template before parsing it. - Live reload.
fuda/watcherreloads and revalidates the struct when a watched file or remote reference changes, and delivers the new value on a channel. - Vault secrets.
fuda/vaultresolvesref:"vault:///secret/data/app#password"against a running Vault server, with Kubernetes and AppRole auth built in.
Each of these is a runnable program under examples/.
| Tag | Example | What it does |
|---|---|---|
yaml / json | yaml:"host" | Maps a file key to the field. |
default | default:"8080" | Supplies a value when no other source did. |
env | env:"APP_PORT" | Reads an environment variable override. |
ref | ref:"file:///run/secrets/token" | Resolves a fixed external URI (file, HTTP, Vault). |
refFrom | refFrom:"TokenURI" | Resolves the URI stored in another field. |
dsn | dsn:"postgres://${.Host}:5432/app" | Builds a string from fields, env values, or refs. |
validate | validate:"required,min=1" | Applies a validator rule after loading. |
See the full tag reference for dsnStrict and every validator rule.
See CHANGELOG.md for release history.
Fuda is licensed under the Apache License 2.0.
