Piko config loader

The wdk/config package loads configuration into a Go struct from files, environment variables, flags, and secret resolvers. It runs an ordered set of passes and merges each source on top of the previous one.

Overview

config.Load fills a struct pointer from struct tags. Sources apply in precedence order, with the highest precedence last. The order is struct tag defaults, programmatic defaults, config files, .env files, environment variables, command line flags, and secret resolvers. A validation pass runs at the end.

The pass model drives the whole loader from tags on your own struct. You write one config.Load call and the package handles merging, type conversion, flag registration, and secret resolution. There is no separate provider object to construct. The package is pure Go and runs in interpreted dev-i mode with no build tag or CGO.

import (
    "context"

    "piko.sh/piko/wdk/config"
)

type AppConfig struct {
    DatabaseURL string `json:"databaseUrl" env:"APP_DB_URL" flag:"dbUrl" validate:"required"`
    APIKey      string `json:"apiKey" env:"APP_API_KEY"`
    Port        int    `json:"port" env:"APP_PORT" flag:"port" default:"8080"`
}

func load(ctx context.Context) (*AppConfig, error) {
    cfg := &AppConfig{}
    _, err := config.Load(ctx, cfg, config.LoaderOptions{
        FilePaths:          []string{"config.json"},
        FlagPrefix:         "app",
        UseGlobalResolvers: true,
    })
    return cfg, err
}

config.Load includes the built in env:, base64:, and file: resolvers. It returns a *LoadContext whose FieldSources map records the source that set each field, which helps when you debug where a value came from.

Struct tags

Each field tag controls one part of the load.

  • json or yaml. Names the field in config files. FilePaths accepts .json, .yaml, and .yml, and later files override earlier ones.
  • default. Sets a fallback value applied before files, environment variables, and flags.
  • env. Names the environment variable for the field. EnvPrefix in LoaderOptions prepends a prefix to every name.
  • flag. Names the command line flag. The loader prefixes it with FlagPrefix, so flag:"dbUrl" with prefix app registers --app.dbUrl.
  • validate. Holds validation rules. The validation pass runs only when you set Validator in LoaderOptions. A nil Validator skips the pass.

Loader options

LoaderOptions controls where the loader reads from and how it behaves.

  • FilePaths. Lists config files to load and merge in order.
  • FlagPrefix. Prefixes flag names registered with the global flag coordinator.
  • EnvPrefix. Prepends a prefix to every environment variable name.
  • UseGlobalResolvers. Adds every resolver from the global registry to the load.
  • StrictFile. Returns an error when a config file holds a field that the target struct does not define.
  • ProgrammaticDefaults and ProgrammaticOverrides. Merge a struct of values at the lowest and highest precedence. Overrides let functional options win over every other source.

For repeated loads, config.NewLoader(opts, options...) builds a reusable *Loader. A single Loader is not safe to share across goroutines.

Secret resolvers

A resolver replaces a placeholder string with a fetched value. The built in resolvers cover env:, base64:, and file:. Cloud and infrastructure resolvers live in the config_resolver_* sub packages and attach through the global registry.

import (
    "context"

    "piko.sh/piko/wdk/config"
    "piko.sh/piko/wdk/config/config_resolver_aws"
)

func registerResolvers(ctx context.Context) error {
    resolver, err := config_resolver_aws.NewResolver(ctx)
    if err != nil {
        return err
    }
    return config.RegisterResolver(resolver)
}

config.RegisterResolver adds a resolver to the global registry. Any load with UseGlobalResolvers: true then inherits it. The global flag coordinator and resolver registry are safe for concurrent use.

Website configuration

piko.WebsiteConfig is the framework configuration type for site level settings, including theme, translations, fonts, favicons, name, description, and canonical base URL. It is importable from the top level piko package. Provide it programmatically with piko.WithWebsiteConfig(cfg), which replaces values loaded from piko.yaml.

The server side configuration that the framework loads at boot is internal and is not importable from application code. Load your own application settings into your own struct with config.Load.

See also

Companion resolvers:

Framework docs: