config

package module
v0.0.0-...-e3bb256 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 24 Imported by: 0

README

config

A Go library for loading application config from JSON, YAML, .env, and environment variables with secure secret overlays, file references, and encrypted credentials.

Go Go report License

English | Russian

Features

  • Loads config into typed Go structs.
  • Supports .json, .yaml, .yml, and .env config files.
  • Merges defaults, main config, security overlay, .env, and process environment.
  • Keeps SecureString values out of the main config file when saving.
  • Stores secrets in a separate YAML security overlay.
  • Supports file:// references for secret values and sidecar config files.
  • Supports enc:// encrypted secret values.
  • Rejects symlinks by default for safer file access.
  • Uses atomic writes with 0600 permissions for saved files.

Install

go get github.com/ra1phdd/config

Quick Start

package main

import (
	"fmt"

	"github.com/ra1phdd/config"
)

type AppConfig struct {
	Port   int                 `json:"port" yaml:"port" env:"APP_PORT"`
	Name   string              `json:"name" yaml:"name" env:"APP_NAME"`
	Token  config.SecureString `json:"token,omitzero" yaml:"token,omitempty" env:"APP_TOKEN"`
	Labels map[string]string   `json:"labels,omitempty" yaml:"labels,omitempty" config:"file=labels.yaml"`
}

func defaults() *AppConfig {
	return &AppConfig{
		Port: 8080,
		Name: "app",
	}
}

func main() {
	cfg, err := config.LoadGeneric(
		defaults,
		config.WithConfigPath("config.yaml"),
		config.WithSecurityPath(".security.yml"),
		config.WithDotEnv(".env"),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(cfg.Port, cfg.Name, cfg.Token.String())

	err = config.SaveGeneric(
		cfg,
		config.WithConfigPath("config.yaml"),
		config.WithSecurityPath(".security.yml"),
	)
	if err != nil {
		panic(err)
	}
}

Merge Order

Values are applied in this order:

  1. Defaults passed to LoadGeneric
  2. Main config file
  3. Security overlay YAML
  4. Optional .env files
  5. Process environment variables

By default, existing process environment variables still win over .env. Use WithDotEnvOverride(true) if you want .env to overwrite already-set environment values before parsing.

Files

Main config file:

  • Supported for loading: .env, .json, .yaml, .yml
  • Supported for saving: .json, .yaml, .yml

Security file:

  • YAML overlay only
  • Missing file is allowed
  • Contains only security-related values such as SecureString

Paths can be passed explicitly or through environment variables:

  • APP_CONFIG
  • APP_SECURITY

Secure Values

Use config.SecureString for secrets.

When saving:

  • The main config file gets a placeholder instead of the secret value.
  • The real secret is written to the security overlay.
  • If CONFIG_KEY_PASSPHRASE is set and an SSH private key is available, the secret is saved as enc://....

When loading, SecureString can resolve:

  • Plain text values
  • file://relative/or/absolute/path
  • enc://base64...

Encryption-related environment variables:

  • CONFIG_KEY_PASSPHRASE
  • CONFIG_SSH_KEY_PATH

If CONFIG_SSH_KEY_PATH is not set, the library looks for a default SSH key in ~/.ssh.

File References For Structured Data

You can move part of the config into a sidecar file with a struct tag:

type AppConfig struct {
	Data map[string]string `json:"data,omitempty" yaml:"data,omitempty" config:"file=data.json"`
}

With this tag:

  • Loading data: file://data.json reads and decodes data.json into Data.
  • Saving keeps the file://... reference in the main config file.
  • The sidecar file is written automatically.

This works for JSON and YAML sidecar files.

Options

Common options:

  • WithConfigPath(path)
  • WithSecurityPath(path)
  • WithDotEnv(paths...)
  • WithEnvironment(enabled)
  • WithDotEnvEnabled(enabled)
  • WithDotEnvOverride(enabled)
  • WithStrictJSON(enabled)
  • WithValidation(enabled)
  • WithMissingConfigAllowed(allowed)
  • WithEmptyConfigAllowed(allowed)
  • WithSymlinksAllowed(allowed)
  • WithPathResolver(resolver)

If your config struct implements:

type ConfigValidator interface {
	Validate() error
}

validation runs automatically on load and save unless disabled.

Path Resolution

The built-in path resolver supports markers in paths:

  • {HOME}: current user's home directory
  • {PWD}: current working directory
  • {CWD}: alias for the current working directory
  • {TMP}: system temporary directory
  • {TEMP}: alias for the system temporary directory

Notes

  • Config and security paths are required. If neither explicit paths nor path environment variables are set, loader construction panics.
  • Symlinks are rejected unless WithSymlinksAllowed(true) is used.
  • Saved files are written atomically.

Documentation

Index

Constants

View Source
const (
	EnvConfig   = "APP_CONFIG"
	EnvSecurity = "APP_SECURITY"

	FileScheme = "file://"
	EncScheme  = "enc://"

	PassphraseEnvVar = "CONFIG_KEY_PASSPHRASE"
	SSHKeyPathEnvVar = "CONFIG_SSH_KEY_PATH"
)

Variables

View Source
var (
	ErrNilConfig     = errors.New("config is nil")
	ErrUnsafePath    = errors.New("unsafe config path")
	ErrInvalidConfig = errors.New("invalid config")
)
View Source
var ErrDecryptionFailed = errors.New("credential: enc:// decryption failed (wrong passphrase or SSH key?)")
View Source
var ErrPassphraseRequired = errors.New("credential: enc:// passphrase required")
View Source
var PassphraseProvider = func() string {
	return os.Getenv(PassphraseEnvVar)
}
View Source
var SSHKeyPathProvider = func() string {
	return os.Getenv(SSHKeyPathEnvVar)
}

Functions

func CopyFile

func CopyFile(src, dst string, perm os.FileMode) error

func DefaultSSHKeyPath

func DefaultSSHKeyPath() (string, error)

func Encrypt

func Encrypt(passphrase, sshKeyPath, plaintext string) (string, error)

func LoadGeneric

func LoadGeneric[T any](defaults func() *T, options ...Option) (*T, error)

func ResolvePath

func ResolvePath(path string) string

func SaveGeneric

func SaveGeneric[T any](cfg *T, options ...Option) error

func WriteFileAtomic

func WriteFileAtomic(path string, data []byte, perm os.FileMode) error

Types

type ConfigValidator

type ConfigValidator interface {
	Validate() error
}

type CredentialResolver

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

func NewCredentialResolver

func NewCredentialResolver(baseDir string, allowSymlinks bool) *CredentialResolver

func (*CredentialResolver) Resolve

func (r *CredentialResolver) Resolve(raw string) (string, error)

type Loader

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

func NewLoader

func NewLoader(options ...Option) (*Loader, error)

func (*Loader) ConfigPath

func (l *Loader) ConfigPath() string

func (*Loader) LoadInto

func (l *Loader) LoadInto(target any) error

func (*Loader) Save

func (l *Loader) Save(cfg any) error

func (*Loader) SecurityPath

func (l *Loader) SecurityPath() string

type Option

type Option func(*Options)

func WithConfigPath

func WithConfigPath(path string) Option

func WithConfigPathEnv

func WithConfigPathEnv(name string) Option

func WithDotEnv

func WithDotEnv(paths ...string) Option

func WithDotEnvEnabled

func WithDotEnvEnabled(enabled bool) Option

func WithDotEnvOverride

func WithDotEnvOverride(enabled bool) Option

func WithEmptyConfigAllowed

func WithEmptyConfigAllowed(allowed bool) Option

func WithEnvironment

func WithEnvironment(enabled bool) Option

func WithMissingConfigAllowed

func WithMissingConfigAllowed(allowed bool) Option

func WithPathResolver

func WithPathResolver(resolver Replacer) Option

func WithSecurityPath

func WithSecurityPath(path string) Option

func WithSecurityPathEnv

func WithSecurityPathEnv(name string) Option

func WithStrictJSON

func WithStrictJSON(enabled bool) Option

func WithSymlinksAllowed

func WithSymlinksAllowed(allowed bool) Option

func WithValidation

func WithValidation(enabled bool) Option

type Options

type Options struct {
	// ConfigPath is the main config file path; supported extensions are .env, .json, .yaml, and .yml.
	ConfigPath string
	// SecurityPath is an optional YAML overlay path for secrets and other private values.
	SecurityPath string
	// DotEnvPaths are optional additional .env files loaded before process env parsing.
	DotEnvPaths []string

	// ApplyEnvironment enables parsing values from process environment variables.
	ApplyEnvironment bool
	// LoadDotEnv enables loading DotEnvPaths; no implicit .env path is used.
	LoadDotEnv bool
	// OverrideDotEnv allows DotEnvPaths to overwrite already-set process environment values.
	OverrideDotEnv bool
	// AllowMissing returns defaults when the main config file does not exist.
	AllowMissing bool
	// AllowEmpty returns defaults when the main config file is empty.
	AllowEmpty bool
	// AllowSymlinks permits reading or writing through symlink paths.
	AllowSymlinks bool
	// StrictJSON rejects unknown JSON fields when parsing .json configs.
	StrictJSON bool
	// Validate runs Validate() hooks after loading and before saving.
	Validate bool

	// Resolver resolves path markers and environment-based path overrides.
	Resolver Replacer
}

type PathResolver

type PathResolver struct {
	Getenv      func(string) string
	Getwd       func() (string, error)
	UserHomeDir func() (string, error)
	TempDir     func() string

	EnvConfig   string
	EnvSecurity string
}

func DefaultPathResolver

func DefaultPathResolver() PathResolver

func DefaultReplacer

func DefaultReplacer() PathResolver

func (PathResolver) CleanAbs

func (r PathResolver) CleanAbs(path string) string

func (PathResolver) ConfigPath

func (r PathResolver) ConfigPath(path string) string

func (PathResolver) ExpandMarkers

func (r PathResolver) ExpandMarkers(path string) string

func (PathResolver) ResolveAgainst

func (r PathResolver) ResolveAgainst(path string, baseDir string) string

func (PathResolver) SecurityPath

func (r PathResolver) SecurityPath(path string) string

func (PathResolver) WithDefaults

func (r PathResolver) WithDefaults() PathResolver

type Replacer

type Replacer = PathResolver

type SecureString

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

func NewSecureString

func NewSecureString(value string) *SecureString

func (*SecureString) IsZero

func (s *SecureString) IsZero() bool

func (*SecureString) MarshalJSON

func (s *SecureString) MarshalJSON() ([]byte, error)

func (*SecureString) MarshalYAML

func (s *SecureString) MarshalYAML() (any, error)

func (*SecureString) Set

func (s *SecureString) Set(value string) *SecureString

func (*SecureString) String

func (s *SecureString) String() string

func (*SecureString) UnmarshalJSON

func (s *SecureString) UnmarshalJSON(value []byte) error

func (*SecureString) UnmarshalText

func (s *SecureString) UnmarshalText(text []byte) error

func (*SecureString) UnmarshalYAML

func (s *SecureString) UnmarshalYAML(value *yaml.Node) error

type SecureStrings

type SecureStrings []*SecureString

func SimpleSecureStrings

func SimpleSecureStrings(val ...string) SecureStrings

func (SecureStrings) MarshalJSON

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

func (*SecureStrings) UnmarshalJSON

func (s *SecureStrings) UnmarshalJSON(value []byte) error

func (*SecureStrings) Values

func (s *SecureStrings) Values() []string

Jump to

Keyboard shortcuts

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