Skip to content

sconf

sconf is a layered configuration library for Go modeled after Microsoft.Extensions.Configuration from ASP.NET Core. Every source — files, environment variables, command line, in-memory maps, Vault — is reduced to one flat model (path → string, with : as the separator), merged in order, and bound uniformly into your structs.

Why sconf

  • One flat model for every source. A YAML tree, a JSON file, MYAPP_SERVERS__0__HOST, and --servers:0:host all become the same key servers:0:host. Because merging happens per key, any single value can be overridden by a higher layer — including a single field of a single array element.
  • Layering with per-key precedence. Sources added later win, key by key, not file by file.
  • Arrays of objects from environment variables using the __: convention — a capability the flat model gives you for free (and one that viper's per-source merge model does not provide; viper cannot override one element of an array from an env var, since BindEnv operates on whole keys of unflattened trees).
  • Typed binding via generics. One entry point, sconf.Load[T], returns *T with defaults, enum validation, time.Duration/time.Time parsing, pointers, maps, slices, and embedded structs handled.
  • Auto-generated --help from your struct tags — human-readable or machine-readable (--help --format env|json|yaml|toml), as an HTTP endpoint (UsageHandler), and programmatically via Describe[T].
  • Secrets from HashiCorp Vault built in: declare a field as secret.UserPass, secret.Cert, secret.KV, or secret.Value, keep only the path in your config file, and the values are fetched at load time and refreshed in the background automatically. VAULT_WAIT rides out a Vault that isn't reachable yet at startup (istio sidecars, sealed nodes).
  • .env files as a first-class layer (AddDotEnvFile) with environment-variable semantics, and the env:"NAME" tag to bind a field to one exact variable.
  • Config dumping — print the final merged configuration in flat, env, JSON, YAML, or TOML form, with secret redaction.
  • Operational niceties: optional files, waiting for files to appear on disk (Vault sidecar / init containers), and typed sentinel errors.

Installation

sh
go get github.com/dvislobokov/sconf

Requires Go 1.21 or newer (the floor is set by dependencies). The Vault integration is part of the core package — if your config has no secret fields, Vault is simply never contacted.

A minimal example

go
package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/dvislobokov/sconf"
)

type Config struct {
	Listen   string `yaml:"listen" default:"0.0.0.0:8080" description:"HTTP listen address"`
	LogLevel string `yaml:"log_level" enum:"debug,info,warn,error" default:"info"`
	Workers  int    `yaml:"workers" default:"4"`

	Limits struct {
		ProcessTimeout time.Duration `yaml:"process_timeout" default:"30s"`
	} `yaml:"limits"`
}

func main() {
	cfg, err := sconf.Load[Config](
		sconf.New().
			AddYAMLFile("appsettings.yaml", sconf.Optional()).
			AddEnvironmentVariables("PIXELMILL_"),
		os.Args[1:],
	)
	// on --help, Load prints usage and exits the process itself
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("listen=%s log_level=%s workers=%d\n", cfg.Listen, cfg.LogLevel, cfg.Workers)
}

Run it with an environment override and a command-line override:

sh
PIXELMILL_WORKERS=16 ./pixelmill --limits__process_timeout=20s
txt
listen=127.0.0.1:9000 log_level=debug workers=16

The value of workers came from the environment, limits:process_timeout from the command line, and everything else from appsettings.yaml — with struct-tag defaults filling any gaps.

Package layout

PackageContents
sconfBuilder, Config, Load[T], Usage[T], Dump[T], sentinel errors, option re-exports
sconf/providerJSONFile, YAMLFile, TOMLFile, DotEnvFile, Env, Args, Map, file options
sconf/bindreflection binder, Unmarshaler / Validator hooks, Describe / Usage
sconf/secretsecret field types (UserPass, Cert, KV, Value) — depends only on the YAML/TOML parsers
sconf/internal/vaultVault client internals — used by the core, not imported directly

Where to go next

Source: github.com/dvislobokov/sconf

Released under the MIT License.