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
- func Schema[T any]() ([]byte, error)
- type DB
- type Env
- type Fingerprint
- type Framework
- type HTTP
- type Layer
- type Loaded
- type Log
- type MapView
- type ModuleView
- type Namespaces
- type Options
- type Pool
- type Provenance
- type RateLimit
- type Secret
- func (s Secret) Format(f fmt.State, verb rune)
- func (s Secret) GoString() string
- func (s Secret) IsZero() bool
- func (s Secret) LogValue() slog.Value
- func (s Secret) MarshalJSON() ([]byte, error)
- func (s Secret) MarshalText() ([]byte, error)
- func (s Secret) Ref() string
- func (s Secret) Reveal() string
- func (s Secret) String() string
- func (s *Secret) UnmarshalText(b []byte) error
- type SharedSection
- type Telemetry
Constants ¶
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 ¶
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.
type Fingerprint ¶
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 ¶
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.
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.
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 ¶
MapView is a ModuleView backed by an in-memory map. The loader produces these from the `modules.<name>` subtree; tests construct them directly.
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 ¶
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 ¶
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 (Secret) Format ¶
Format implements fmt.Formatter so every fmt verb (%v, %+v, %s, %q, %x, …) renders the redaction marker.
func (Secret) MarshalJSON ¶
MarshalJSON redacts. Secrets are never serialized as values.
func (Secret) MarshalText ¶
MarshalText redacts (covers yaml/text encoders that honor TextMarshaler).
func (Secret) Ref ¶
Ref returns the secret reference this value was resolved from ("" if none). Safe to log.
func (Secret) Reveal ¶
Reveal returns the raw secret value. Do not log it. Boundary lint flags Reveal calls outside adapters/, app/, and _test.go files.
func (*Secret) UnmarshalText ¶
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 {
}
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).