config

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package config defines wowapi's typed configuration contracts: the framework-owned Framework struct, the Secret type with structural redaction, and the ModuleView through which modules receive their namespaced configuration.

Ownership model, precedence, and loader behavior are specified in docs/blueprint/12-configuration-and-deployment.md. Phase 0 ships the core types and validation; the layered loader (files → overlay → env vars → secret resolution) lands in Phase 1.

Product applications compose rather than fork: they define their own Config type embedding Framework in an internal/appcfg package (scaffolded by `wowapi init`).

Index

Constants

View Source
const SchemaVersion = 1

SchemaVersion is the current config file format version. Loaders reject files declaring a newer version (config written for a newer wowapi) and files older than the supported floor.

Variables

This section is empty.

Functions

func Schema

func Schema[T any]() ([]byte, error)

Schema renders a JSON Schema for a config struct from the same `conf`, `default`, `required`, `unsafe`, and `doc` tags the binder reads — the two can't drift because there is only one tag set. Feeds `wowapi config schema` and the product configs/config.schema.json check.

Types

type DB

type DB struct {
	DSN         Secret `conf:"dsn" json:"dsn" doc:"runtime database DSN (app_rt role) as a secretref:// reference"`
	MigrateDSN  Secret `` /* 142-byte string literal not displayed */
	PlatformDSN Secret `` /* 444-byte string literal not displayed */
	Pool               // embedded: pool knobs stay flat under db.* and flow to every process view wholesale
}

DB configures the Postgres pools. DSNs are optional at load time and validated at process-view narrowing instead: api/worker require DSN, migrate requires MigrateDSN (D-0021) — the framework repo's config tooling and DB-less tests must stay loadable.

type Env

type Env string

Env is the deployment environment. It gates dev-only behavior: anything marked unsafe refuses to run when the environment is Prod.

const (
	EnvLocal Env = "local"
	EnvDev   Env = "dev"
	EnvStage Env = "stage"
	EnvProd  Env = "prod"
)

func (Env) IsProd

func (e Env) IsProd() bool

IsProd reports whether production safety rules apply.

func (Env) Valid

func (e Env) Valid() bool

Valid reports whether e is one of the known environments.

type Fingerprint

type Fingerprint [sha256.Size]byte

Fingerprint identifies an effective configuration: the SHA-256 of its canonical *redacted* JSON rendering. Secret values never enter the hash (Secret marshals as its redaction marker), so the fingerprint is safe to log, expose as a metric label, and include in /readyz output — and two processes sharing config sections can be compared for drift (12 §7).

Note the redaction consequence: rotating a secret's VALUE (same ref) does not change the fingerprint; changing the reference does.

func FingerprintOf

func FingerprintOf(v any) (Fingerprint, error)

FingerprintOf hashes the canonical redacted JSON rendering of v. v is normally a bound config struct; json.Marshal is deterministic for structs (field order) and maps (sorted keys), making the hash canonical.

func Load

func Load[T any](opts Options) (T, Fingerprint, error)

Load computes the effective configuration exactly once, at boot: compiled defaults ← base file ← env overlay ← env vars ← flags, then secret resolution, then validation. It fails with ALL problems joined, never just the first (blueprint 12 §3–4).

func (Fingerprint) Short

func (f Fingerprint) Short() string

Short returns the first 12 hex chars — enough for log correlation.

func (Fingerprint) String

func (f Fingerprint) String() string

String returns the full lowercase hex digest.

type Framework

type Framework struct {
	// Environment carries NO default tag: it is fail-closed (D-0010/SEC-1) —
	// the loader errors when it is absent from every layer. The compiled
	// `local` value exists only through Defaults() for tests/local tooling.
	Environment   Env       `` /* 135-byte string literal not displayed */
	SchemaVersion int       `conf:"schema_version" default:"1" json:"schema_version" doc:"config file format version"`
	HTTP          HTTP      `conf:"http" json:"http"`
	Log           Log       `conf:"log" json:"log"`
	DB            DB        `conf:"db" json:"db"`
	Telemetry     Telemetry `conf:"telemetry" json:"telemetry"`
}

Framework is the framework-owned configuration. It is loaded and validated once at boot and is immutable afterwards; hot paths read precomputed values, never stores. Fields grow phase by phase with the components that consume them (DB in Phase 2, Auth in Phase 4, …).

func Defaults

func Defaults() Framework

Defaults returns the compiled framework defaults — the always-present, always-safe bottom layer of the precedence chain.

func (Framework) CheckSharedDrift

func (f Framework) CheckSharedDrift(expected string) error

CheckSharedDrift reports an error when this process's shared-config fingerprint differs from expected (the hex fingerprint the deployment pins, e.g. via an env var stamped at release). An empty expected disables the check. Wire it as a startup gate or a /readyz check so a mis-deployed process fails loudly rather than silently diverging.

func (Framework) SharedFingerprint

func (f Framework) SharedFingerprint() (Fingerprint, error)

SharedFingerprint is the fingerprint of the shared section only — the value api/worker/migrate compare to detect drift. Like Fingerprint it is redacted (secret VALUES never enter it), so it is safe to log and expose.

func (Framework) SharedSection

func (f Framework) SharedSection() SharedSection

SharedSection extracts the cross-process-shared configuration.

func (Framework) Validate

func (f Framework) Validate() error

Validate checks the whole struct and returns ALL problems joined, not just the first — boot failures must list everything wrong at once.

type HTTP

type HTTP struct {
	Addr              string        `conf:"addr" default:":8080" json:"addr" doc:"HTTP listen address"`
	ReadHeaderTimeout time.Duration `conf:"read_header_timeout" default:"5s" json:"read_header_timeout" doc:"maximum time to read request headers"`
	RequestTimeout    time.Duration `conf:"request_timeout" default:"30s" json:"request_timeout" doc:"per-request handler timeout"`
	MaxBodyBytes      int64         `conf:"max_body_bytes" default:"1048576" json:"max_body_bytes" doc:"maximum request body size in bytes"`
	// CORSAllowedOrigins is the exact-match CORS allowlist (deny-by-default when
	// empty). Set per environment, e.g. modules-free base leaves it empty and the
	// prod overlay lists the product's web origins.
	CORSAllowedOrigins []string  `` /* 127-byte string literal not displayed */
	RateLimit          RateLimit `conf:"rate_limit" json:"rate_limit"`
}

HTTP holds server guardrails. Zero values are replaced by Defaults.

type Layer

type Layer string

Layer identifies which precedence layer supplied a config value (blueprint 12 §3; surfaced by `wowapi config doctor`).

const (
	LayerDefault  Layer = "default"   // compiled default tag
	LayerBaseFile Layer = "base-file" // configs/base.yaml
	LayerEnvFile  Layer = "env-file"  // configs/<env>.yaml overlay
	LayerEnvVar   Layer = "env"       // PREFIX__SECTION__FIELD environment variable
	LayerFlag     Layer = "flag"      // local-only CLI flag
	LayerSecret   Layer = "secret"    // value resolved through the secret provider
)

type Loaded

type Loaded[T any] struct {
	Config      T
	Fingerprint Fingerprint
	Provenance  Provenance
	// Warnings carries non-fatal findings (e.g. unsafe knobs enabled in stage).
	Warnings []string
}

Loaded is the full result of LoadDetailed.

func LoadDetailed

func LoadDetailed[T any](opts Options) (Loaded[T], error)

LoadDetailed is Load plus per-key provenance and warnings, for `wowapi config doctor` and startup diagnostics.

type Log

type Log struct {
	Level  string `conf:"level" default:"info" json:"level" doc:"log level: debug|info|warn|error"`
	Format string `conf:"format" default:"json" json:"format" doc:"log output format: json|text (prod requires json)"`
}

Log configures structured logging.

type MapView

type MapView map[string]any

MapView is a ModuleView backed by an in-memory map. The loader produces these from the `modules.<name>` subtree; tests construct them directly.

func (MapView) Decode

func (m MapView) Decode(out any) error

Decode implements ModuleView with strict unknown-key rejection.

type ModuleView

type ModuleView interface {
	// Decode strict-decodes the module's namespace into the module-owned
	// typed struct. Unknown keys in the namespace are an error (typo
	// defense); the module's own validation runs after decoding. Errors here
	// fail application boot.
	Decode(out any) error
}

ModuleView is the ONLY configuration surface a module receives (via module.Context.Config()). It exposes exactly the module's own `modules.<name>.*` namespace: there is deliberately no Get(key), no parent traversal, and no way to read framework or sibling-module configuration.

type Namespaces

type Namespaces map[string]MapView

Namespaces is the raw `modules.*` subtree of a product configuration: one isolated MapView per module name. The binder captures the subtree opaquely (module keys are validated by each module's own strict Decode, not by the framework binder), and the app hands each module exactly its own view — there is no API to traverse from a view back to framework, product, or sibling configuration.

type Options

type Options struct {
	// BaseFile is the committed product config file (configs/base.yaml).
	BaseFile string
	// EnvFile is the environment overlay (configs/<env>.yaml).
	EnvFile string
	// EnvPrefix enables the environment-variable layer:
	// "ACME__" maps ACME__DB__MAX_CONNS=32 onto db.max_conns. Empty = no env layer.
	EnvPrefix string
	// Environ supplies the environment ("KEY=VALUE" pairs); nil = os.Environ().
	Environ []string
	// Secrets resolves secretref:// values at boot. Required if any Secret
	// field is set; resolution failures fail the load.
	Secrets secrets.Provider
	// Flags holds local-tooling overrides by dotted key ("http.addr" → value).
	// The loader refuses to start when flags are set and environment=prod.
	Flags map[string]string
}

Options configures a Load call. Zero-value fields skip their layer.

type Pool

type Pool struct {
	MaxConns     int           `conf:"max_conns" default:"16" json:"max_conns" doc:"maximum pool connections"`
	QueryTimeout time.Duration `conf:"query_timeout" default:"5s" json:"query_timeout" doc:"per-query context deadline"`
}

Pool holds the connection-pool knobs shared by every process view. New pool fields belong HERE, never directly on DB: the app views embed Pool, so additions propagate to api/worker/migrate narrowing automatically instead of silently dropping out of a hand-copied field list (ARCH-17).

type Provenance

type Provenance map[string]Layer

Provenance maps dotted config keys to the layer that supplied their value.

type RateLimit

type RateLimit struct {
	Disabled          bool    `conf:"disabled" json:"disabled" doc:"set true to remove the default per-client rate limiter from the chain"`
	RequestsPerSecond float64 `conf:"requests_per_second" default:"20" json:"requests_per_second" doc:"sustained requests/sec per client key (per replica)"`
	Burst             int     `conf:"burst" default:"40" json:"burst" doc:"burst capacity per client key"`
}

RateLimit configures the in-process per-client rate limiter that the generated api installs in its default middleware chain (roadmap S2/CA-2). It is OPT-OUT: enabled unless Disabled is set, so a scaffolded product is protected against resource-exhaustion by default. Limits are guardrails, not billing.

type Secret

type Secret struct {
	// contains filtered or unexported fields
}

Secret holds a resolved secret value with structural redaction: every standard rendering path (fmt verbs, JSON/text marshaling, slog) emits a redaction marker, never the value. The raw value is reachable only via Reveal, whose call sites are restricted by boundary lint to adapters and the app composition root.

The zero Secret is empty and renders as "[redacted]".

func NewSecret

func NewSecret(ref, value string) Secret

NewSecret builds a resolved secret. ref may be empty (e.g. testkit fakes).

func (Secret) Format

func (s Secret) Format(f fmt.State, verb rune)

Format implements fmt.Formatter so every fmt verb (%v, %+v, %s, %q, %x, …) renders the redaction marker.

func (Secret) GoString

func (s Secret) GoString() string

GoString implements fmt.GoStringer so %#v cannot leak the value.

func (Secret) IsZero

func (s Secret) IsZero() bool

IsZero reports whether the secret is unset (no ref and no value).

func (Secret) LogValue

func (s Secret) LogValue() slog.Value

LogValue implements slog.LogValuer with redaction.

func (Secret) MarshalJSON

func (s Secret) MarshalJSON() ([]byte, error)

MarshalJSON redacts. Secrets are never serialized as values.

func (Secret) MarshalText

func (s Secret) MarshalText() ([]byte, error)

MarshalText redacts (covers yaml/text encoders that honor TextMarshaler).

func (Secret) Ref

func (s Secret) Ref() string

Ref returns the secret reference this value was resolved from ("" if none). Safe to log.

func (Secret) Reveal

func (s Secret) Reveal() string

Reveal returns the raw secret value. Do not log it. Boundary lint flags Reveal calls outside adapters/, app/, and _test.go files.

func (Secret) String

func (s Secret) String() string

String implements fmt.Stringer with redaction.

func (*Secret) UnmarshalText

func (s *Secret) UnmarshalText(b []byte) error

UnmarshalText accepts only a secret *reference*; the value is resolved later, at boot, by the app composition root via a secrets.Provider. A raw (non-reference) value is rejected so plaintext secrets cannot enter through config files or environment variables.

type SharedSection

type SharedSection struct {
	Environment   Env `json:"environment"`
	SchemaVersion int `json:"schema_version"`
	DB            DB  `json:"db"`
}

SharedSection is the config subset that must match across every process of one deployment.

type Telemetry

type Telemetry struct {
	TraceSampleRatio float64 `` /* 176-byte string literal not displayed */
}

Telemetry configures distributed tracing (roadmap O1). Tracing is OFF by default (zero-cost NoOp tracer) and becomes active only when the sample ratio is > 0 — the composition root then wires the OTel adapter with this ratio, exporting to the OTLP endpoint named by the standard OTEL_EXPORTER_OTLP_ENDPOINT environment variable (e.g. http://jaeger:4318 in the compose stack). This is the real config key that replaces the previously-documented-but-nonexistent cfg.TraceSampleRatio (roadmap CA-2/CA-7).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL