Pular para o conteúdo
Esta página ainda não foi traduzida. O conteúdo abaixo está em inglês. Ler em inglês

Getting started

Configuration

Typed configuration loaded from environment variables and .env files, with secrets that never reach logs.

Última atualização:

Vukem has no stringly lookups such as config("app.name"). Configuration is read once at boot into typed structs; invalid values abort boot with all problems listed, not only the first one.

.env files#

The config package ships a small, standard-library-only .env parser. Values are resolved with this precedence, highest first:

  1. The real process environment
  2. .env.<APP_ENV>.local
  3. .env.local (skipped when APP_ENV=test, so tests are reproducible)
  4. .env.<APP_ENV>
  5. .env
.env
APP_NAME=Blog
APP_ENV=development
# Written by vukem key:generate
APP_KEY=base64:your-32-byte-key-in-base64
APP_URL=http://localhost:3000

# ${VAR} interpolation
ASSET_URL=${APP_URL}/build

# Read the value from a file (Docker and Kubernetes secrets)
DB_PASSWORD=@file:/run/secrets/db

# Quotes and escapes
GREETING="Hello\nWorld"
export LOG_LEVEL=debug

Supported syntax: KEY=value, export KEY=value, single and double quotes, \n escapes inside double quotes (values may span lines), # comments, ${OTHER} interpolation and the @file: prefix. The @ keeps SQLite DSNs such as file:storage/app.db untouched.

Per-environment files override .env:

APP_NAME=Blog
APP_ENV=development
APP_KEY=
APP_URL=http://localhost:3000

Core settings#

vukem.New loads the core vukem.Config:

VariableDefaultNotes
APP_NAMEVukem
APP_ENVdevelopmentdevelopment, production or test
APP_DEBUGfalseMust be false when APP_ENV=production (boot error otherwise)
APP_URLhttp://localhost:3000Public origin of the app
APP_KEY—Required in production (and for any key-derived feature), at least 32 bytes, base64: prefix optional
HOSTlocalhostListen host; 0.0.0.0 for all IPv4 interfaces, :: for IPv6 (dual-stack on most systems)
PORT3000Listen port, 0–65535
SHUTDOWN_TIMEOUT15sGraceful shutdown budget
TRUSTED_PROXIESempty (trust nobody)Comma-separated CIDRs or IPs whose X-Forwarded-For sets c.IP(); an invalid entry is a boot error (Deploy)
CLIENT_IP_HEADERemptyRead the client IP from this single-IP header (e.g. X-Real-IP) instead of X-Forwarded-For, only from TRUSTED_PROXIES peers
TRUST_FORWARDED_PROTOfalseTreat X-Forwarded-Proto: https from a trusted proxy as HTTPS (HSTS); requires TRUSTED_PROXIES
HSTS_FORCEfalseSend HSTS on every response in production when the TLS proxy's range cannot be trusted (trade-offs); validated in every environment (requires an https:// APP_URL), applied only in production (a boot warning otherwise)
LOG_LEVELinfodebug, info, warn, error
LOG_FORMATtext (json in production)text or json
LOG_DEDUP_WINDOW1m in production, off elsewhereRepeated identical errors become one repeated summary per window; 0 disables it

Typed module config#

Fill your own structs with config.Load[T] using env and default tags. Fields of nested structs without an env tag are filled recursively.

config/mail.go
type Mail struct {
	Host     string        `env:"MAIL_HOST"     default:"localhost"`
	Port     int           `env:"MAIL_PORT"     default:"1025"`
	From     string        `env:"MAIL_FROM"`
	Password config.Secret `env:"MAIL_PASSWORD"`
	Timeout  time.Duration `env:"MAIL_TIMEOUT"  default:"10s"`
	Cc       []string      `env:"MAIL_CC"` // comma-separated
}

func LoadMail(app *vukem.App) (Mail, error) {
	return config.Load[Mail](app.Source) // every conversion error, joined
}

Supported field types: string, bool, ints, uints, floats, time.Duration, []string (comma-separated), *url.URL, config.Secret and any encoding.TextUnmarshaler.

Testing with a fake environment#

Tests inject configuration instead of calling os.Setenv:

Go
src := config.MapSource(map[string]string{
	"APP_KEY": config.GenerateKey(),
	"APP_ENV": "test",
})

app, err := vukem.New(vukem.WithSource(src))

vukem.WithEnv(fsys, getenv) loads .env files from any fs.FS (for example fstest.MapFS) with a custom getenv.

Secret#

config.Secret holds sensitive values. Every formatting path prints [redacted]: fmt (%v, %s, %#v), encoding/json, encoding.TextMarshaler and log/slog.

Go
cfg.Password.Reveal() // the real value, only where it is used
fmt.Println(cfg.Password)        // [redacted]
app.Log.Info("mail", "cfg", cfg) // password logged as [redacted]

APP_KEY#

  • Required. vukem key:generate writes one to .env (creating it from .env.example when missing); --show prints a key instead and --force replaces an existing one, which makes current sessions and encrypted data unreadable. Where only the compiled app exists (Docker, CI), ./app key:generate prints a new key without booting the app or writing any file; see CLI. In code, config.GenerateKey() returns 32 random bytes from crypto/rand, base64:-encoded.
  • Required in production and whenever APP_ENV is unset or empty. Only with an explicit APP_ENV=development or test does an app boot without it (development logs one warning), and every key-derived feature fails closed on use: cookie sessions (SESSION_DRIVER=cookie, the default, so DefaultMiddleware is still a boot error), app.Crypt (nil; Decrypt and SetCookie return crypt.ErrNoKey, Encrypt panics), app.Key.Derive (panics), app.DeriveKey (returns config.ErrKeyMissing), encrypted ORM columns (orm.ErrNoEncrypter) and the maintenance bypass cookie (disabled). An API-only app with token auth and no encryption runs without a key. A malformed key is always a boot error.
  • The decoded key is available as app.Key. Never use it directly: derive a purpose-specific key with HKDF-SHA256 so cookies, sessions and encryption never share key material.
Go
sessionKey := app.Key.Derive("session", 32) // HKDF info "vukem:session"

signer, err := app.DeriveKey("signed.urls", 32) // config.ErrKeyMissing instead of a panic

APP_PREVIOUS_KEYS lists former keys (comma-separated, same base64: format). They only open or verify what they sealed or signed (encrypted cookies and columns, queued mail, cookie sessions, the maintenance bypass, and signed URLs and social state through app.DerivePreviousKeys); everything new uses APP_KEY. vukem key:rotate moves the current key there and writes a new one; see Encryption.

Secure defaults#

CheckBehaviour
Missing APP_KEY in production or with APP_ENV unset, malformed APP_KEY anywhereBoot error
Missing APP_KEY with explicit APP_ENV=development/testBoots; key-derived features fail closed on use
APP_DEBUG=true with APP_ENV=productionBoot error
PORT out of rangeBoot error
Invalid TRUSTED_PROXIES entry; TRUST_FORWARDED_PROTO or CLIENT_IP_HEADER without TRUSTED_PROXIES; an invalid or list CLIENT_IP_HEADER; HSTS_FORCE without an https:// APP_URLBoot error
Debug error detailsOnly when Services.Debug is set; never in production