env

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2026 License: MIT Imports: 18 Imported by: 0

README ΒΆ

Env - High-Performance Go Environment Variable Library

Go Reference Go Version Security Policy Thread Safe

πŸ“– δΈ­ζ–‡ζ–‡ζ‘£


πŸ“‹ Overview

Env is a production-ready, zero-dependency, thread-safe Go library for environment variable management. It focuses on security, concurrency, and developer experience.

✨ Key Features
Feature Description
πŸš€ One-Line Setup env.Load(".env") loads and applies to os.Environ
πŸ”’ Type Safety GetString, GetInt, GetBool, GetDuration, GetSlice[T]
πŸ“ Multi-Format Auto-detect .env, .json, .yaml files
⚑ Thread Safety Sharded storage (8 shards) + RWMutex for high concurrency
πŸ›‘οΈ Secure Memory SecureValue auto-zeroes sensitive data with memory pooling
πŸ”„ Variable Expansion Full ${VAR} syntax with default values
πŸ“ Audit Logging Built-in JSON/Log/Channel handlers for compliance
πŸ§ͺ Testing Ready Isolated loaders for test isolation
πŸ“¦ Zero Dependencies Standard library only

πŸ“¦ Installation

go get github.com/cybergodev/env

Requirements: Go 1.24+


πŸš€ Quick Start

Step 1: Create a .env file
# Application
APP_NAME=myapp
APP_PORT=8080
DEBUG=true

# Database
DB_HOST=localhost
DB_PORT=5432
DB_PASSWORD=secret123

# Timeouts
TIMEOUT=30s
Step 2: Use in Go code
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/cybergodev/env"
)

func main() {
    // One-line initialization - loads and applies to os.Environ
    if err := env.Load(".env"); err != nil {
        log.Fatalf("Failed to load: %v", err)
    }

    // Type-safe access with defaults
    port    := env.GetInt("APP_PORT", 8080)
    debug   := env.GetBool("DEBUG", false)
    timeout := env.GetDuration("TIMEOUT", 30*time.Second)

    fmt.Printf("Server: %s:%d\n", env.GetString("APP_NAME", "unknown"), port)
    fmt.Printf("Debug: %v, Timeout: %v\n", debug, timeout)
}

πŸ“š Usage Guide

Basic Operations
// Load multiple files (later files override earlier ones)
env.Load(".env", "config.json", ".env.local")

// Check existence
value, exists := env.Lookup("KEY")
if !exists {
    // Handle missing key
}

// CRUD operations
env.Set("KEY", "value")           // Set value (returns error)
env.Delete("KEY")                 // Delete key (returns error)
keys := env.Keys()                // Get all keys
all := env.All()                  // Get all variables as map
count := env.Len()                // Variable count
Type Access
// String (with default)
name := env.GetString("APP_NAME", "default-app")

// Integer (returns int64)
port := env.GetInt("PORT", 8080)

// Boolean - supports: true/1/yes/on/enabled, false/0/no/off/disabled
debug := env.GetBool("DEBUG", false)

// Duration
timeout := env.GetDuration("TIMEOUT", 30*time.Second)

// Generic slice: string, int, int64, uint, uint64, bool, float64, time.Duration
hosts := env.GetSlice[string]("HOSTS", []string{"localhost"})
ports := env.GetSlice[int]("PORTS", []int{8080})
Struct Mapping
type Config struct {
    Port    int           `env:"PORT" envDefault:"8080"`
    Debug   bool          `env:"DEBUG" envDefault:"false"`
    Timeout time.Duration `env:"TIMEOUT"`
    Origins []string      `env:"CORS_ORIGINS"`
}

var cfg Config
if err := env.Load(".env"); err != nil {
    log.Fatal(err)
}
if err := env.ParseInto(&cfg); err != nil {
    log.Fatal(err)
}
Loader API (Fine-grained Control)
cfg := env.ProductionConfig()
cfg.Filenames = []string{"/etc/app/.env"}

loader, err := env.New(cfg)
if err != nil {
    log.Fatal(err)
}
defer loader.Close()

// Load additional files
loader.LoadFiles("override.env")

// Apply to os.Environ
loader.Apply()

// Access values
port := loader.GetInt("PORT", 8080)

πŸ“ Multi-Format Support

.env Files
# Comments start with #
DATABASE_URL=postgres://localhost:5432/db
PORT=8080
DEBUG=true

# Quotes are optional
MESSAGE="Hello World"
SINGLE='Single quotes work too'

# Variable expansion
URL=${HOST}:${PORT:-443}
JSON (Auto-flattened)
{
    "database": { "host": "localhost", "port": 5432 },
    "ports": [8080, 8081]
}

Access:

env.GetString("database.host")    // "localhost" (dot notation)
env.GetInt("database.port")       // 5432
env.GetSlice[int]("ports")        // [8080, 8081]
// Also works: DATABASE_HOST, DATABASE_PORT
YAML (Auto-flattened)
database:
  host: localhost
  port: 5432
ports: [8080, 8081]

Access: Same as JSON - use dot notation or uppercase underscore format.


πŸ”„ Serialization / Deserialization

// Map to format string
data := map[string]string{"PORT": "8080", "DEBUG": "true"}

envString, _  := env.Marshal(data)                      // .env (default)
jsonString, _ := env.Marshal(data, env.FormatJSON)      // JSON
yamlString, _ := env.Marshal(data, env.FormatYAML)      // YAML

// Parse string to Map
m, _ := env.UnmarshalMap("PORT=8080\nDEBUG=true")           // .env format
m, _ := env.UnmarshalMap(`{"port": 8080}`, env.FormatJSON)  // JSON
m, _ := env.UnmarshalMap(yamlString, env.FormatAuto)        // Auto-detect

// Struct <-> Map conversion
m, _ := env.MarshalStruct(&config)          // Struct to map
env.UnmarshalInto(m, &config)               // Map to struct

// String directly to struct
env.UnmarshalStruct("PORT=8080\nDEBUG=true", &config, env.FormatEnv)

πŸ”„ Variable Expansion

.env files fully support ${VAR} syntax:

HOST=localhost
PORT=8080

# Variable reference
URL=${HOST}:${PORT}                    # β†’ "localhost:8080"

# Default value if unset or empty
TIMEOUT=${TIMEOUT:-30s}

# Default value only if unset (preserves empty string)
NAME=${NAME-default}

# Combined expansion
FULL_URL=https://${HOST}:${PORT:-443}

πŸ”’ Secure Value Handling

Use SecureValue for sensitive data like passwords, API keys, and tokens:

// Get SecureValue
sv := env.GetSecure("API_KEY")
if sv != nil {
    defer sv.Release()

    // Safe logging
    fmt.Println(sv.Masked())       // [SECURE:32 bytes]

    // Access actual value
    value := sv.String()

    // Get bytes (caller must clean up)
    data := sv.Bytes()
    defer env.ClearBytes(data)     // Manual zeroing
}

// Create SecureValue directly
secret := env.NewSecureValue("super_secret")
defer secret.Release()

// Create with strict error checking
secret, err := env.NewSecureValueStrict("super_secret")
if err != nil {
    log.Fatal("Memory lock failed:", err)
}
defer secret.Release()
SecureValue Methods
Method Description
String() Get string value
Bytes() Get byte slice copy (caller must clean up)
Length() Get value length
Masked() Get masked representation for logging
Close() Zero memory, don't return to pool
Release() Zero memory and return to pool
IsClosed() Check if closed
IsMemoryLocked() Check if memory is protected from swap

πŸ“ Audit Logging

cfg := env.ProductionConfig()
cfg.AuditEnabled = true
cfg.AuditHandler = env.NewJSONAuditHandler(os.Stdout)

loader, _ := env.New(cfg)
// Output: {"action":"set","key":"API_KEY","success":true,"timestamp":"..."}

Built-in Handlers:

env.NewJSONAuditHandler(w)      // JSON format β†’ io.Writer
env.NewLogAuditHandler(logger)  // Standard log.Logger
env.NewChannelAuditHandler(ch)  // Channel (external processing)
env.NewNopAuditHandler()        // No-op (discard)

πŸ§ͺ Testing Support

func TestConfig(t *testing.T) {
    // Create isolated loader (doesn't affect global state)
    cfg := env.TestingConfig()
    cfg.Filenames = []string{".env.test"}

    loader, err := env.New(cfg)
    if err != nil {
        t.Fatal(err)
    }
    defer loader.Close()

    port := loader.GetInt("PORT", 8080)
    // Test your code...
}

// Reset default loader between tests
func TestMain(m *testing.M) {
    env.ResetDefaultLoader()
    os.Exit(m.Run())
}

πŸ› οΈ Utility Functions

// Sensitive key detection
env.IsSensitiveKey("API_SECRET")  // true
env.IsSensitiveKey("HOST")        // false

// Value masking
env.MaskValue("API_KEY", "secret123")  // "***"

// Key masking for logging
env.MaskKey("DB_PASSWORD")  // "DB_***"

// String sanitization
safe := env.SanitizeForLog(userInput)

// Mask sensitive content in string
masked := env.MaskSensitiveInString(logMessage)

// Format detection
env.DetectFormat("config.yaml")  // FormatYAML
Sensitive Key Patterns

Automatically detected (case-insensitive):

*password*, *secret*, *key*, *token*, *credential*,
*api_key*, *private*, *auth*, *session*, *access*

βš™οΈ Configuration

Preset Configurations
env.DefaultConfig()     // Safe defaults
env.DevelopmentConfig() // Relaxed limits + allow override
env.TestingConfig()     // Tight config + isolated testing
env.ProductionConfig()  // Strict security + audit
Configuration Comparison
Setting Default Development Testing Production
FailOnMissingFile false false false true
OverwriteExisting false true true false
ValidateValues true true true true
AuditEnabled false false false true
MaxFileSize 2 MB 10 MB 64 KB 64 KB
MaxVariables 500 500 50 50
Full Configuration Options
cfg := env.DefaultConfig()

// === File Handling ===
cfg.Filenames         = []string{".env"}
cfg.FailOnMissingFile = false
cfg.OverwriteExisting = true
cfg.AutoApply         = true

// === Validation ===
cfg.RequiredKeys   = []string{"DB_URL"}
cfg.AllowedKeys    = []string{"PORT", "DEBUG"}  // Empty = allow all
cfg.ForbiddenKeys  = []string{"PATH"}           // Block dangerous keys

// === Security Limits ===
cfg.MaxFileSize    = 2 << 20   // 2 MB
cfg.MaxVariables   = 500
cfg.ValidateValues = true

// === Parsing Options ===
cfg.MaxLineLength     = 1024
cfg.MaxKeyLength      = 64
cfg.MaxValueLength    = 4096
cfg.MaxExpansionDepth = 5

// === JSON/YAML Options ===
cfg.JSONNullAsEmpty = true
cfg.YAMLNullAsEmpty = true

// === Advanced Options ===
cfg.Prefix     = "APP_"      // Only load keys with prefix
cfg.FileSystem = nil         // nil = OS filesystem

// === Audit Logging ===
cfg.AuditEnabled = true
cfg.AuditHandler = env.NewJSONAuditHandler(os.Stdout)
Default Limits
Setting Default Hard Limit
MaxFileSize 2 MB 100 MB
MaxLineLength 1,024 chars 64 KB
MaxKeyLength 64 chars 1,024 chars
MaxValueLength 4,096 chars 1 MB
MaxVariables 500 10,000
MaxExpansionDepth 5 20

πŸ“– API Reference

Package Functions
Function Description
Load(files...) Load files and apply to os.Environ
LoadWithConfig(cfg) Load with custom config
GetString(key, def...) Get string value
GetInt(key, def...) Get int64 value
GetBool(key, def...) Get boolean value
GetDuration(key, def...) Get duration value
GetSlice[T](key, def...) Get generic slice
GetSliceFrom[T](loader, key, def...) Get slice from specific loader
Lookup(key) Get value + existence check
Set(key, value) Set value (returns error)
Delete(key) Delete key (returns error)
Keys() Get all keys
All() Get all variables as map
Len() Get variable count
GetSecure(key) Get SecureValue for sensitive data
Validate() Validate required keys
ParseInto(&struct) Populate struct from env vars
Marshal(data, format?) Convert map/struct to format string
UnmarshalMap(string, format?) Parse format string to map
UnmarshalStruct(string, &struct, format?) Parse string to struct
UnmarshalInto(map, &struct) Populate struct from map
MarshalStruct(struct) Convert struct to map
New(cfg) Create new loader with config
NewSecureValue(string) Create SecureValue from string
NewSecureValueStrict(string) Create SecureValue with error on lock failure
ResetDefaultLoader() Reset singleton (for testing)
ClearBytes([]byte) Securely zero byte slice
SetMemoryLockEnabled(bool) Enable/disable memory locking
IsMemoryLockEnabled() Check if memory locking is enabled
SetMemoryLockStrict(bool) Enable strict mode for lock failures
IsMemoryLockStrict() Check if strict mode is enabled
IsMemoryLockSupported() Check if platform supports memory locking
RegisterParser(format, factory) Register custom parser
ForceRegisterParser(format, factory) Override built-in parser
MaskSensitiveInString(string) Mask sensitive content in string
Loader Methods
Method Description
LoadFiles(files...) Load files into loader
Apply() Apply to os.Environ
Validate() Validate required keys
Close() Close and cleanup resources
IsApplied() Check if applied to os.Environ
IsClosed() Check if closed
LoadTime() Get last load time
Config() Get loader configuration

πŸ” Memory Locking (Advanced)

For high-security applications, enable memory locking to prevent sensitive data from being swapped to disk:

// Enable memory locking at startup
env.SetMemoryLockEnabled(true)

// Optional: Enable strict mode to fail if locking fails
env.SetMemoryLockStrict(true)

// Check platform support
if env.IsMemoryLockSupported() {
    // Platform supports mlock/VirtualLock
}

// Create SecureValue with locking
sv := env.NewSecureValue("api_secret")
defer sv.Release()

// Check if memory is actually locked
if sv.IsMemoryLocked() {
    fmt.Println("Memory is protected from swap")
}

Requirements:

  • Unix: Requires CAP_IPC_LOCK capability or root privileges
  • Windows: Requires SE_LOCK_MEMORY_NAME privilege

πŸ”Œ Custom Parsers (Advanced)

Register custom parsers for additional file formats:

// Register a custom parser (cannot override built-in formats)
err := env.RegisterParser(customFormat, func(cfg env.Config, factory *env.ComponentFactory) (env.EnvParser, error) {
    return &MyCustomParser{validator: factory.Validator()}, nil
})

// Force override built-in parsers (use with caution!)
err := env.ForceRegisterParser(env.FormatEnv, customFactory)

πŸ›‘οΈ Security Features

Feature Description
Key/Value Validation Block invalid formats and dangerous patterns
Forbidden Keys Prevent overwriting PATH, LD_PRELOAD, DYLD_*, etc.
Size Limits File size, line length, variable count limits
Expansion Depth Prevent exponential expansion attacks
Sensitive Data Masking Auto-detect and mask passwords, tokens, keys
Secure Memory SecureValue zeroes memory on GC/cleanup
Path Traversal Protection Block .., absolute paths, UNC paths

⚑ Performance

Metric Value
Sharded Concurrency 8 shards for parallel access
Memory Pooling Reusable SecureValue, Builder, Scanner pools
Zero Allocations Fast path for simple key lookups
Benchmarks Run go test -bench=. -benchmem

πŸ“ Examples

See the examples directory for complete example code:

Example Description
01_quickstart.go Basic usage
02_loader_config.go Configuration options
03_type_access.go Type conversion
04_struct_mapping.go Struct population
05_secure_values.go Secure handling
06_audit_logging.go Audit logging
07_marshal_unmarshal.go Serialization
08_utilities.go Utility functions

πŸ“„ License

MIT License - See LICENSE file for details.


If this project helps you, please give it a Star! ⭐

Documentation ΒΆ

Overview ΒΆ

Package env provides environment variable loading and management.

Adapter Pattern Implementation ΒΆ

This file implements adapters for internal types that need to be exposed via public interfaces with extended functionality.

Interface hierarchy (simplified):

  • env.KeyValidator = internal.KeyValidator
  • env.ValueValidator = internal.ValueValidator
  • env.VariableExpander = internal.VariableExpander
  • env.AuditLogger = internal.AuditLogger

The remaining adapters handle cases where the public interface has more methods than the minimal internal interface:

  • auditorAdapter: internal.Auditor β†’ FullAuditLogger
  • validatorInterfaceWrapper: minimal validator β†’ Validator (with ValidateRequired)
  • auditorInterfaceWrapper: minimal auditor β†’ FullAuditLogger

Package env provides a high-security environment variable library for Go 1.24+. It is designed for applications where security, concurrent access, and production-grade features are critical.

The library supports multiple file formats (.env, JSON, YAML), secure memory handling for sensitive values, comprehensive audit logging, and extensive validation.

Two Usage Modes ΒΆ

The library provides two complementary usage patterns:

## Global Mode (Simple)

Use package-level functions after calling Load(). Best for simple applications:

err := env.Load(".env")
if err != nil {
    log.Fatal(err)
}
port := env.GetInt("PORT", 8080)
host := env.GetString("DATABASE_HOST", "localhost")

## Instance Mode (Advanced)

Create isolated Loader instances with New(). Best for tests and fine-grained control:

cfg := env.DefaultConfig()
cfg.Filenames = []string{".env"}
loader, err := env.New(cfg)
if err != nil {
    log.Fatal(err)
}
defer loader.Close()
value := loader.GetString("DATABASE_URL")

When to Use Which Mode ΒΆ

Use Global Mode when:

  • Simple application with single configuration
  • Want automatic apply to os.Environ (Load does this by default)
  • Quick scripts, prototypes, or small services

Use Instance Mode when:

  • Writing tests that need isolation
  • Multiple configurations in same process
  • Need control over when variables are applied to os.Environ
  • Want explicit lifecycle management with Close()

Secure Value Handling ΒΆ

For sensitive values like API keys and passwords, use SecureValue:

sv := loader.GetSecure("API_KEY")
if sv != nil {
    defer sv.Release()
    data := sv.Bytes()
    // use data securely
    env.ClearBytes(data)
}

Struct Mapping ΒΆ

Map environment variables to structs using tags:

type Config struct {
    Host string `env:"DB_HOST" envDefault:"localhost"`
    Port int    `env:"DB_PORT" envDefault:"5432"`
}

var cfg Config
if err := env.ParseInto(&cfg); err != nil {
    log.Fatal(err)
}

Environment Presets ΒΆ

The library provides preset configurations for different environments:

  • DefaultConfig: Secure defaults for general use
  • DevelopmentConfig: Relaxed limits, overwrite enabled
  • TestingConfig: Isolated, compact limits
  • ProductionConfig: Strict limits, audit enabled

File Format Support ΒΆ

Supported file formats:

  • .env: Standard dotenv format with KEY=value pairs
  • .json: JSON files with nested structure (flattened with underscores)
  • .yaml/.yml: YAML files with nested structure (flattened with underscores)

Thread Safety ΒΆ

All Loader methods are safe for concurrent use. The library uses sharded locking for optimal performance in high-concurrency scenarios.

Error Types ΒΆ

The library defines several error types for precise error handling:

  • ErrFileNotFound: File does not exist
  • ErrFileTooLarge: File exceeds size limit
  • ErrInvalidKey: Key format validation failed
  • ErrSecurityViolation: Security policy violation
  • ErrClosed: Loader has been closed
  • ParseError: Parsing failure with file/line info
  • ValidationError: Configuration or value validation failure
  • SecurityError: Security-related error
  • FileError: File operation error
  • ExpansionError: Variable expansion error
  • JSONError: JSON parsing error
  • YAMLError: YAML parsing error
  • MarshalError: Marshaling/unmarshaling error

Package env provides environment variable loading and management.

Component Factory ΒΆ

This file implements ComponentFactory which creates and manages shared components used by Loader and Parser. It provides a clean lifecycle for validator, auditor, and expander instances.

The factory uses adapters (defined in adapters.go) to bridge between public interfaces and internal interfaces, allowing both built-in and custom components to work seamlessly.

Index ΒΆ

Constants ΒΆ

View Source
const (
	// DefaultMaxFileSize is the maximum allowed file size (2 MB).
	DefaultMaxFileSize = internal.DefaultMaxFileSize

	// DefaultMaxLineLength is the maximum allowed line length.
	DefaultMaxLineLength = internal.DefaultMaxLineLength

	// DefaultMaxKeyLength is the maximum allowed key length.
	DefaultMaxKeyLength = internal.DefaultMaxKeyLength

	// DefaultMaxValueLength is the maximum allowed value length.
	DefaultMaxValueLength = internal.DefaultMaxValueLength

	// DefaultMaxVariables is the maximum number of variables per file.
	DefaultMaxVariables = internal.DefaultMaxVariables

	// DefaultMaxExpansionDepth is the maximum variable expansion depth.
	DefaultMaxExpansionDepth = internal.DefaultMaxExpansionDepth
)

Default security limits for high-security configurations. These values are intentionally conservative to prevent various attacks.

Variables ΒΆ

View Source
var (
	// ErrFileNotFound indicates the specified file does not exist.
	ErrFileNotFound = errors.New("file not found")

	// ErrFileTooLarge indicates the file exceeds the maximum allowed size.
	// Re-exported from internal/errors for backward compatibility.
	ErrFileTooLarge = ierrors.ErrFileTooLarge

	// ErrLineTooLong indicates a line exceeds the maximum allowed length.
	// Re-exported from internal/errors for backward compatibility.
	ErrLineTooLong = ierrors.ErrLineTooLong

	// ErrInvalidKey indicates the key does not match the required pattern.
	ErrInvalidKey = errors.New("invalid key format")

	// ErrForbiddenKey indicates the key is not allowed for security reasons.
	ErrForbiddenKey = errors.New("key is forbidden for security reasons")

	// ErrSecurityViolation indicates a general security policy violation.
	// Re-exported from internal/errors for backward compatibility.
	ErrSecurityViolation = ierrors.ErrSecurityViolation

	// ErrExpansionDepth indicates variable expansion exceeded the maximum depth.
	ErrExpansionDepth = errors.New("variable expansion depth exceeded")

	// ErrMaxVariables indicates the maximum number of variables has been reached.
	ErrMaxVariables = errors.New("maximum number of variables exceeded")

	// ErrNullByte indicates a null byte was detected in the input.
	ErrNullByte = errors.New("null byte detected in input")

	// ErrControlChar indicates a control character was detected in the input.
	ErrControlChar = errors.New("control character detected in input")

	// ErrInvalidValue indicates the value contains invalid content.
	// Re-exported from internal/errors for backward compatibility.
	ErrInvalidValue = ierrors.ErrInvalidValue

	// ErrMissingRequired indicates a required key is missing.
	ErrMissingRequired = errors.New("required key is missing")

	// ErrDuplicateKey indicates a duplicate key was encountered.
	ErrDuplicateKey = errors.New("duplicate key encountered")

	// ErrClosed indicates the loader has been closed.
	ErrClosed = errors.New("loader has been closed")

	// ErrInvalidConfig indicates the configuration is invalid.
	ErrInvalidConfig = errors.New("invalid configuration")
)

Sentinel errors provide simple error comparison using errors.Is().

View Source
var DefaultKeyPattern *regexp.Regexp = nil

DefaultKeyPattern is the default pattern for valid keys. Set to nil to use fast byte-level validation (isValidDefaultKey) instead of regex. This provides ~10x performance improvement for key validation. The regex pattern ^[A-Za-z][A-Za-z0-9_]*$ is still available for reference:

var defaultKeyPatternRegex = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
View Source
var ErrAlreadyInitialized = errors.New("default loader already initialized")

ErrAlreadyInitialized is returned when attempting to set a default loader when one has already been initialized.

View Source
var ErrValidateRequiredUnsupported = errors.New(
	"custom validator does not implement ValidateRequired; " +
		"implement Validator interface for required key validation",
)

ErrValidateRequiredUnsupported is returned when a custom validator does not implement the ValidateRequired method. Users who need required key validation should implement the full Validator interface.

Functions ΒΆ

func All ΒΆ

func All() map[string]string

All returns all environment variables from the default loader as a map. Returns nil if the loader cannot be initialized.

Example:

vars := env.All()
for key, value := range vars {
    fmt.Printf("%s = %s\n", key, value)
}

func ClearBytes ΒΆ

func ClearBytes(b []byte)

ClearBytes securely zeros a byte slice. Use this function to clear sensitive data returned by SecureValue.Bytes().

Example:

data := sv.Bytes()
defer env.ClearBytes(data)

func Delete ΒΆ

func Delete(key string) error

Delete removes a key from the default loader. Returns an error if the loader cannot be initialized.

Example:

if err := env.Delete("TEMP_VAR"); err != nil {
    log.Fatal(err)
}

func ForceRegisterParser ΒΆ added in v1.1.0

func ForceRegisterParser(format FileFormat, factory ParserFactory) error

ForceRegisterParser registers a parser factory, allowing override of built-in parsers (FormatEnv, FormatJSON, FormatYAML).

WARNING: Use with caution. Overriding built-in parsers may introduce security vulnerabilities if the replacement parser doesn't implement the same security checks (key validation, value validation, size limits, etc.). Ensure your custom parser properly validates all input.

This is intended for advanced use cases where you need complete control over parsing behavior, such as:

  • Adding custom security checks to the built-in parser
  • Implementing format extensions (e.g., HEREDOC support, multi-line values)
  • Testing with mock parsers

Example:

// Override the default .env parser with a custom implementation
err := env.ForceRegisterParser(env.FormatEnv, func(cfg env.Config, factory *env.ComponentFactory) (env.EnvParser, error) {
    return &MyCustomEnvParser{
        validator: factory.Validator(),
        auditor:   factory.Auditor(),
    }, nil
})

func GetBool ΒΆ

func GetBool(key string, defaultValue ...bool) bool

GetBool retrieves a boolean value from the default loader with optional default. Returns false if the loader cannot be initialized or the key is not found and no default is provided.

Example:

debug := env.GetBool("DEBUG")           // Returns false if not found
debug := env.GetBool("DEBUG", true)     // Returns true if not found

func GetDuration ΒΆ

func GetDuration(key string, defaultValue ...time.Duration) time.Duration

GetDuration retrieves a duration value from the default loader with optional default. Returns 0 if the loader cannot be initialized or the key is not found and no default is provided.

Example:

timeout := env.GetDuration("TIMEOUT")                  // Returns 0 if not found
timeout := env.GetDuration("TIMEOUT", 30*time.Second) // Returns 30s if not found

func GetInt ΒΆ

func GetInt(key string, defaultValue ...int64) int64

GetInt retrieves an integer value from the default loader with optional default. Returns 0 if the loader cannot be initialized or the key is not found and no default is provided.

Example:

port := env.GetInt("PORT")           // Returns 0 if not found
port := env.GetInt("PORT", 8080)     // Returns 8080 if not found

func GetSlice ΒΆ

func GetSlice[T sliceElement](key string, defaultValue ...[]T) []T

GetSlice retrieves a slice of values from the default loader. Returns nil if the loader cannot be initialized or the key is not found and no default is provided.

Indexed keys are searched in format: KEY_0, KEY_1, KEY_2, etc. Also supports comma-separated values as fallback for .env files.

Example:

ports := env.GetSlice[int]("PORTS")
hosts := env.GetSlice[string]("HOSTS", []string{"localhost"})

func GetSliceFrom ΒΆ

func GetSliceFrom[T sliceElement](loader *Loader, key string, defaultValue ...[]T) []T

GetSliceFrom retrieves a slice of values from a loader by iterating through indexed keys. If the key is not found and no default is provided, returns nil. Supports dot-notation path resolution for nested keys.

Indexed keys are searched in format: KEY_0, KEY_1, KEY_2, etc. Also supports comma-separated values as fallback for .env files.

Why a Function Instead of a Method? ΒΆ

This is a generic function rather than a method because Go does not support type parameters on methods. The pattern is:

// Method approach (not possible in Go):
// loader.GetSlice[int]("PORTS")  // ❌ Compile error

// Function approach (current implementation):
env.GetSliceFrom[int](loader, "PORTS")  // βœ“ Works

For package-level usage without a loader instance, use GetSlice[T]().

Example:

ports := env.GetSliceFrom[int](loader, "PORTS")           // Returns []int{8080, 8081} from PORTS_0, PORTS_1
hosts := env.GetSliceFrom[string](loader, "HOSTS", []string{"localhost"}) // With default

func GetString ΒΆ

func GetString(key string, defaultValue ...string) string

GetString retrieves a value from the default loader with optional default. Returns an empty string if the loader cannot be initialized or the key is not found and no default is provided. Use Lookup to distinguish between "not found" and "empty value".

Example:

value := env.GetString("KEY")           // Returns "" if not found
value := env.GetString("KEY", "default") // Returns "default" if not found

func IsMarshalError ΒΆ

func IsMarshalError(err error) bool

IsMarshalError checks if an error is a marshaling error.

func IsMemoryLockEnabled ΒΆ

func IsMemoryLockEnabled() bool

IsMemoryLockEnabled returns whether memory locking is currently enabled.

func IsMemoryLockStrict ΒΆ

func IsMemoryLockStrict() bool

IsMemoryLockStrict returns whether strict mode is enabled.

func IsMemoryLockSupported ΒΆ

func IsMemoryLockSupported() bool

IsMemoryLockSupported returns whether the current platform supports memory locking. This returns false on platforms like wasm or nacl where memory locking is not available.

Note: This only indicates platform support, not whether the process has the required privileges to actually lock memory.

func IsSensitiveKey ΒΆ

func IsSensitiveKey(key string) bool

IsSensitiveKey determines if a key likely contains sensitive data.

func Keys ΒΆ

func Keys() []string

Keys returns all keys from the default loader. Returns nil if the loader cannot be initialized.

Example:

for _, key := range env.Keys() {
    fmt.Printf("%s = %s\n", key, env.GetString(key))
}

func Len ΒΆ

func Len() int

Len returns the number of loaded variables from the default loader. Returns 0 if the loader cannot be initialized.

Example:

count := env.Len()
fmt.Printf("Loaded %d environment variables\n", count)

func Load ΒΆ

func Load(filenames ...string) error

Load initializes the default loader with the given files.

IMPORTANT: This function:

  1. Sets the package-level default loader (used by GetString, GetInt, etc.)
  2. Auto-applies all loaded variables to os.Environ

For isolated instances without auto-apply, use New().

Files are loaded sequentially; later files can override values from earlier files.

Supported file formats:

  • .env files (dotenv format)
  • .json files (JSON format with nested structure)
  • .yaml/.yml files (YAML format with nested structure)

For JSON/YAML files, nested values are flattened and can be accessed using dot-notation:

// config.json: {"database": {"host": "localhost", "port": 5432}}
env.Load("config.json")
host := env.GetString("database.host") // "localhost"
port := env.GetInt("database.port")    // 5432

Returns ErrAlreadyInitialized if the default loader has already been initialized.

Example:

// Initialize with default .env file
if err := env.Load(); err != nil {
    log.Fatal(err)
}

// Initialize with multiple files
if err := env.Load(".env", ".env.local"); err != nil {
    log.Fatal(err)
}
// Now use package-level functions
port := env.GetInt("PORT", 8080)

func LoadWithConfig ΒΆ added in v1.1.0

func LoadWithConfig(cfg Config) error

LoadWithConfig initializes the default loader with a custom configuration.

IMPORTANT: This function:

  1. Sets the package-level default loader (used by GetString, GetInt, etc.)
  2. Forces AutoApply=true regardless of cfg.AutoApply value

For isolated instances without setting the default, use New().

Returns ErrAlreadyInitialized if the default loader has already been initialized.

Example:

cfg := env.DefaultConfig()
cfg.Filenames = []string{".env.production"}
cfg.OverwriteExisting = true
if err := env.LoadWithConfig(cfg); err != nil {
    log.Fatal(err)
}

func Lookup ΒΆ

func Lookup(key string) (string, bool)

Lookup retrieves a value and existence from the default loader.

Example:

value, exists := env.Lookup("DATABASE_URL")
if !exists {
    log.Fatal("DATABASE_URL is required")
}

func Marshal ΒΆ

func Marshal(data interface{}, format ...FileFormat) (string, error)

Marshal converts data to the specified format with sorted keys (default: .env format). The input can be a map[string]string or a struct (will be converted to map first). The format parameter is optional; if not provided, defaults to .env format. Supported formats: FormatEnv, FormatJSON, FormatYAML.

Keys are always sorted for consistent output.

Example:

// Map to .env format (sorted)
envString, _ := env.Marshal(mapData)

// Struct to .env format (sorted)
envString, _ := env.Marshal(config)

// Map to JSON format (sorted)
jsonString, _ := env.Marshal(mapData, env.FormatJSON)

// Struct to YAML format (sorted)
yamlString, _ := env.Marshal(config, env.FormatYAML)

func MarshalStruct ΒΆ

func MarshalStruct(v interface{}) (map[string]string, error)

MarshalStruct converts a struct to environment variables. Struct fields can be tagged with `env:"KEY"` to specify the env variable name. Nested structs are flattened with underscore-separated keys.

func MaskKey ΒΆ

func MaskKey(key string) string

MaskKey masks a key name for logging purposes.

func MaskSensitiveInString ΒΆ

func MaskSensitiveInString(s string) string

MaskSensitiveInString masks potentially sensitive content in a string.

func MaskValue ΒΆ

func MaskValue(key, value string) string

MaskValue masks a value based on its sensitivity. This is a utility function for general value masking.

func ParseInto ΒΆ

func ParseInto(v interface{}) error

ParseInto populates a struct from the default loader's environment variables. Struct fields can be tagged with `env:"KEY"` to specify the env variable name. Optional `envDefault:"value"` sets a default if the key is not found.

This function automatically reads all loaded environment variables and maps them to struct fields based on the `env` tags. It eliminates the need to manually build a data map before calling UnmarshalStruct.

Example:

type Config struct {
    Host string `env:"DB_HOST"`
    Port int    `env:"DB_PORT,envDefault:5432"`
    Debug bool  `env:"DEBUG,envDefault:false"`
}

if err := env.Load(".env"); err != nil {
    log.Fatal(err)
}

var cfg Config
if err := env.ParseInto(&cfg); err != nil {
    log.Fatal(err)
}

func RegisterParser ΒΆ

func RegisterParser(format FileFormat, factory ParserFactory) error

RegisterParser registers a custom parser factory for a specific file format. Returns an error if a factory is already registered for the format. Built-in formats (DotEnv, JSON, YAML) cannot be overridden for security.

Example:

err := env.RegisterParser(env.FormatEnv, func(cfg env.Config, factory *env.ComponentFactory) (env.EnvParser, error) {
    return &MyCustomParser{validator: factory.Validator()}, nil
})

func ResetDefaultLoader ΒΆ

func ResetDefaultLoader() error

ResetDefaultLoader resets the default loader singleton and clears any cached error. This function is intended for use in tests to ensure isolation between test cases. It is safe for concurrent use.

Returns any error that occurred while closing the old loader. A nil return value indicates either no loader was set or it was closed successfully.

Design: The function atomically swaps the loader to nil while holding the lock, then closes the old loader outside the lock. This design choice:

  • Ensures only one reset can happen at a time (mutex protected)
  • Allows new loaders to be created immediately after the swap
  • Clears cached initialization errors to allow retry
  • Avoids blocking concurrent operations during potentially slow Close()
  • The old and new loaders are completely independent

Concurrent getDefaultLoader() calls during reset will:

  • Either see the old loader (before swap) - safe to use
  • Or see nil and create a new loader (after swap) - safe to use

They will never receive a closed loader.

Example:

func TestSomething(t *testing.T) {
    if err := env.ResetDefaultLoader(); err != nil {
        t.Logf("warning: failed to reset loader: %v", err)
    }
    defer env.ResetDefaultLoader()
    // ... test code
}

func SanitizeForLog ΒΆ

func SanitizeForLog(s string) string

SanitizeForLog removes potentially sensitive information from a string. It scans for patterns that might indicate sensitive data and masks them.

func Set ΒΆ

func Set(key, value string) error

Set sets a value in the default loader.

Example:

if err := env.Set("DEBUG", "true"); err != nil {
    log.Fatal(err)
}

func SetMemoryLockEnabled ΒΆ

func SetMemoryLockEnabled(enabled bool)

SetMemoryLockEnabled enables or disables memory locking globally. This affects all new SecureValue objects created after the call. Existing SecureValue objects are not affected.

This function is safe to call from multiple goroutines simultaneously.

Example:

func main() {
    // Enable at application startup
    env.SetMemoryLockEnabled(true)

    // ... rest of application
}

func SetMemoryLockStrict ΒΆ

func SetMemoryLockStrict(strict bool)

SetMemoryLockStrict sets whether memory locking failures should return errors. By default, locking failures are silently ignored for compatibility. Enable strict mode for high-security applications that require confirmation that memory is actually locked.

Example:

env.SetMemoryLockEnabled(true)
env.SetMemoryLockStrict(true) // Now locking failures will return errors
sv := env.NewSecureValue("sensitive-data")
// If locking failed, sv will still be valid but the error is logged

func UnmarshalInto ΒΆ

func UnmarshalInto(data map[string]string, v interface{}) error

UnmarshalInto populates a struct from a map[string]string. Struct fields can be tagged with `env:"KEY"` to specify the env variable name. Optional `envDefault:"value"` sets a default if the key is not found.

func UnmarshalMap ΒΆ

func UnmarshalMap(data string, format ...FileFormat) (map[string]string, error)

UnmarshalMap parses a formatted string into a map[string]string. The format parameter is optional and defaults to FormatEnv. Use FormatAuto for automatic format detection.

Nested structures (JSON/YAML) are flattened with underscore delimiter.

Example:

// .env format (default)
m, _ := env.UnmarshalMap("KEY=value")

// JSON format
m, _ := env.UnmarshalMap(`{"server": {"host": "localhost"}}`, env.FormatJSON)

// Auto-detect format
m, _ := env.UnmarshalMap(jsonString, env.FormatAuto)

func UnmarshalStruct ΒΆ

func UnmarshalStruct(data string, v interface{}, format ...FileFormat) error

UnmarshalStruct parses a formatted string and populates the struct. The format parameter is optional and defaults to FormatEnv. Use FormatAuto for automatic format detection.

Struct fields should use `env:"KEY"` tags for mapping.

Example:

type Config struct {
    Host string `env:"SERVER_HOST"`
    Port int    `env:"SERVER_PORT"`
}

// .env format (default)
var cfg Config
_ = env.UnmarshalStruct("SERVER_HOST=localhost\nSERVER_PORT=8080", &cfg)

// JSON format
_ = env.UnmarshalStruct(`{"server": {"host": "localhost"}}`, &cfg, env.FormatJSON)

func Validate ΒΆ

func Validate() error

Validate validates the default loader against required and allowed keys. Returns an error if the loader cannot be initialized or validation fails.

Example:

if err := env.Validate(); err != nil {
    log.Fatal("Environment validation failed:", err)
}

Types ΒΆ

type AuditAction ΒΆ

type AuditAction = internal.Action

AuditAction represents the type of action being audited. Use these constants with AuditLogger.Log() to record security-relevant events.

Audit constants for common actions. These are used with AuditLogger methods to categorize audit events:

  • ActionLoad: File loading operations
  • ActionParse: Parsing operations for env, JSON, YAML files
  • ActionGet: Variable retrieval operations
  • ActionSet: Variable assignment operations
  • ActionDelete: Variable deletion operations
  • ActionValidate: Validation operations
  • ActionExpand: Variable expansion operations
  • ActionSecurity: Security-related events (path validation, forbidden keys)
  • ActionError: Error conditions
  • ActionFileAccess: File system access operations

type AuditEvent ΒΆ

type AuditEvent = internal.Event

AuditEvent represents a single audit log entry.

type AuditHandler ΒΆ

type AuditHandler = internal.Handler

AuditHandler defines the interface for audit log handlers.

type AuditLogger ΒΆ

type AuditLogger = internal.AuditLogger

AuditLogger is an alias for internal.AuditLogger. This interface requires only LogError, making it easy to implement custom loggers. For full audit capabilities, see FullAuditLogger.

type ChannelAuditHandler ΒΆ

type ChannelAuditHandler = internal.ChannelHandler

ChannelAuditHandler sends audit events to a channel.

func NewChannelAuditHandler ΒΆ

func NewChannelAuditHandler(ch chan<- AuditEvent) *ChannelAuditHandler

NewChannelAuditHandler creates a new ChannelAuditHandler.

type ComponentConfig ΒΆ added in v1.1.0

type ComponentConfig struct {
	CustomValidator Validator        // Custom key/value validator
	CustomExpander  VariableExpander // Custom variable expander
	CustomAuditor   AuditLogger      // Custom audit logger
	FileSystem      FileSystem       // Custom file system (for testing)
	AuditHandler    AuditHandler     // Custom audit handler
	AuditEnabled    bool             // Enable audit logging
	Prefix          string           // Only process vars with this prefix
}

ComponentConfig holds custom component implementations and advanced options.

type ComponentFactory ΒΆ

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

ComponentFactory creates and manages shared components used by Loader and Parser. It provides a clean lifecycle for validator, auditor, and expander instances. ComponentFactory is safe for concurrent use.

func (*ComponentFactory) Auditor ΒΆ

func (f *ComponentFactory) Auditor() FullAuditLogger

Auditor returns the audit logger component as FullAuditLogger.

func (*ComponentFactory) Close ΒΆ

func (f *ComponentFactory) Close() error

Close releases resources held by the factory. After calling Close, the factory should not be used. Safe to call multiple times; subsequent calls return nil. This method is safe for concurrent use.

func (*ComponentFactory) Expander ΒΆ added in v1.1.0

func (f *ComponentFactory) Expander() VariableExpander

Expander returns the expander as VariableExpander interface.

func (*ComponentFactory) IsClosed ΒΆ

func (f *ComponentFactory) IsClosed() bool

IsClosed returns true if the factory has been closed. This method is safe for concurrent use.

func (*ComponentFactory) LineParserAuditor ΒΆ added in v1.1.0

func (f *ComponentFactory) LineParserAuditor() internal.LineAuditLogger

LineParserAuditor returns the auditor as internal.LineAuditLogger interface.

func (*ComponentFactory) LineParserExpander ΒΆ added in v1.1.0

func (f *ComponentFactory) LineParserExpander() internal.LineExpander

LineParserExpander returns the expander as internal.LineExpander interface.

func (*ComponentFactory) LineParserValidator ΒΆ added in v1.1.0

func (f *ComponentFactory) LineParserValidator() internal.LineKeyValidator

LineParserValidator returns the validator as internal.LineKeyValidator interface.

func (*ComponentFactory) Validator ΒΆ

func (f *ComponentFactory) Validator() Validator

Validator returns the validator component as a Validator interface.

type Config ΒΆ

type Config struct {
	FileConfig       // File loading behavior
	ValidationConfig // Key and value validation
	LimitsConfig     // Size and count limits
	JSONConfig       // JSON parsing options
	YAMLConfig       // YAML parsing options
	ParsingConfig    // General parsing behavior
	ComponentConfig  // Custom components and advanced options
}

Config holds all configuration options for the Loader.

Configuration is organized into nested structures for better organization while maintaining backward compatibility through Go's struct embedding. You can access fields either way:

// Old way (still works via field promotion):
cfg.Filenames = []string{".env"}
cfg.MaxFileSize = 1024

// New way (recommended for clarity):
cfg.FileConfig.Filenames = []string{".env"}
cfg.LimitsConfig.MaxFileSize = 1024

For sensible defaults, use DefaultConfig():

cfg := env.DefaultConfig()
cfg.FileConfig.Filenames = []string{".env"}

func DefaultConfig ΒΆ

func DefaultConfig() Config

DefaultConfig returns a Config with secure default values. These defaults are suitable for high-security environments.

func DevelopmentConfig ΒΆ

func DevelopmentConfig() Config

DevelopmentConfig returns a Config optimized for development environments. This configuration prioritizes developer experience and flexibility:

  • FailOnMissingFile: false (graceful handling of missing .env files)
  • OverwriteExisting: true (easy iteration during development)
  • AllowYamlSyntax: true (supports YAML-style values)
  • Relaxed size limits (10MB files, 500 variables)
  • Value validation ENABLED for security (prevents injection attacks)

Example:

cfg := env.DevelopmentConfig()
cfg.FileConfig.Filenames = []string{".env.development"}
loader, err := env.New(cfg)

func ProductionConfig ΒΆ

func ProductionConfig() Config

ProductionConfig returns a Config optimized for production environments. This configuration provides maximum security for production deployments:

  • FailOnMissingFile: true (fail fast on configuration errors)
  • AuditEnabled: true (compliance and security monitoring)
  • Strict size limits (64KB files, 50 variables)
  • Value validation enabled

Example:

cfg := env.ProductionConfig()
cfg.FileConfig.Filenames = []string{"/etc/app/.env"}
cfg.ComponentConfig.AuditHandler = env.NewJSONAuditHandler(os.Stdout)
loader, err := env.New(cfg)

func TestingConfig ΒΆ

func TestingConfig() Config

TestingConfig returns a Config optimized for testing environments. This configuration is designed for isolated, repeatable tests:

  • FailOnMissingFile: false (tests may not have .env files)
  • OverwriteExisting: true (test isolation)
  • Compact size limits (test files are typically small)
  • No audit logging (reduces test noise)

Example:

func TestSomething(t *testing.T) {
    cfg := env.TestingConfig()
    cfg.FileConfig.Filenames = []string{".env.test"}
    loader, err := env.New(cfg)
    if err != nil {
        t.Fatal(err)
    }
    defer loader.Close()
}

func (*Config) IsZero ΒΆ added in v1.1.0

func (c *Config) IsZero() bool

IsZero returns true if the Config appears to be uninitialized (all fields zero). This is useful to determine if DefaultConfig() should be applied.

Note: A partially-initialized Config may not be detected as zero. Always start from DefaultConfig() for custom configurations:

cfg := env.DefaultConfig()
cfg.Filenames = []string{".env.production"}

func (*Config) Validate ΒΆ

func (c *Config) Validate() error

Validate validates the configuration and returns an error if invalid. This method performs all configuration validation by delegating to focused sub-methods.

type EnvApplicator ΒΆ

type EnvApplicator interface {
	// Apply applies all loaded variables to the process environment.
	Apply() error
}

EnvApplicator handles applying loaded variables to the process environment. Use this interface when you only need to apply variables to os.Environ.

type EnvCloser ΒΆ

type EnvCloser interface {
	// Close closes the loader and releases resources.
	Close() error
}

EnvCloser handles lifecycle management. Use this interface when you only need to close and release resources.

type EnvFileLoader ΒΆ

type EnvFileLoader interface {
	// LoadFiles loads environment variables from multiple files.
	// If no filenames are provided, defaults to ".env".
	LoadFiles(filenames ...string) error
}

EnvFileLoader handles loading environment variables from files and strings. Use this interface when you only need file loading capabilities.

type EnvGetter ΒΆ

type EnvGetter interface {
	// Get retrieves a value by key with optional default.
	GetString(key string, defaultValue ...string) string

	// Lookup retrieves a value by key and reports whether it exists.
	Lookup(key string) (string, bool)

	// Keys returns all keys.
	Keys() []string

	// All returns all environment variables as a map.
	All() map[string]string
}

EnvGetter handles reading environment variable values. Use this interface when you only need read access to variables.

type EnvLoader ΒΆ

EnvLoader defines the full interface for loading and managing environment variables. It combines all fine-grained interfaces for convenience.

For new code, consider using the fine-grained interfaces (EnvFileLoader, EnvGetter, EnvSetter, EnvApplicator, EnvCloser) which follow the Interface Segregation Principle and allow for more precise dependency declarations.

type EnvParser ΒΆ

type EnvParser interface {
	// Parse reads and parses environment variables from an io.Reader.
	// The filename parameter is used for error messages and audit logging.
	Parse(r io.Reader, filename string) (map[string]string, error)
}

EnvParser defines the interface for parsing environment files.

type EnvSetter ΒΆ

type EnvSetter interface {
	// Set sets a value for a key with validation.
	Set(key, value string) error

	// Delete removes a key.
	Delete(key string) error
}

EnvSetter handles writing environment variable values. Use this interface when you only need write access to variables. Note: Set and Delete methods return error for validation failures, unlike EnvStorage.Set which is a simple storage operation.

type EnvStorage ΒΆ

type EnvStorage interface {
	// Get retrieves a value by key. Returns the value and whether it exists.
	Get(key string) (string, bool)

	// Set stores a value for a key.
	Set(key, value string)

	// Delete removes a key.
	Delete(key string)

	// Keys returns all keys in the storage.
	Keys() []string

	// Len returns the number of entries.
	Len() int

	// ToMap returns a copy of all values as a regular map.
	ToMap() map[string]string

	// Clear removes all entries.
	Clear()
}

EnvStorage defines the interface for storing and retrieving environment variables.

type ExpansionError ΒΆ

type ExpansionError = ierrors.ExpansionError

ExpansionError provides detailed information about variable expansion failures. This is an alias for internal.ExpansionError to maintain backward compatibility.

type File ΒΆ

type File interface {
	io.Reader
	io.Writer
	io.Closer
	Stat() (os.FileInfo, error)
	Sync() error
}

File defines the interface for file operations. It combines common io interfaces with file-specific operations.

type FileConfig ΒΆ added in v1.1.0

type FileConfig struct {
	Filenames         []string // Files to load (default: [".env"])
	FailOnMissingFile bool     // Return error if file doesn't exist
	OverwriteExisting bool     // Overwrite existing environment variables
	AutoApply         bool     // Auto-apply to os.Environ
}

FileConfig controls file loading behavior.

type FileError ΒΆ

type FileError = ierrors.FileError

FileError provides detailed information about file-related errors. This is an alias for internal.FileError to maintain backward compatibility.

type FileFormat ΒΆ

type FileFormat int

FileFormat represents the file format for environment configuration.

const (
	// FormatAuto automatically detects the format based on file extension.
	FormatAuto FileFormat = iota
	// FormatEnv represents the .env file format.
	FormatEnv
	// FormatJSON represents a JSON file format.
	FormatJSON
	// FormatYAML represents a YAML file format.
	FormatYAML
)

func DetectFormat ΒΆ

func DetectFormat(filename string) FileFormat

DetectFormat detects the file format based on the file extension. Returns FormatEnv for ".env" files, FormatJSON for ".json" files, FormatYAML for ".yaml" and ".yml" files, and FormatAuto for unknown extensions.

func (FileFormat) String ΒΆ

func (f FileFormat) String() string

String returns the string representation of the file format. Returns "auto", "dotenv", "json", "yaml", or "unknown" based on the format value.

type FileSystem ΒΆ

type FileSystem interface {
	// Open opens a file for reading.
	Open(name string) (File, error)

	// OpenFile opens a file with the specified flags and permissions.
	OpenFile(name string, flag int, perm os.FileMode) (File, error)

	// Stat returns file information.
	Stat(name string) (os.FileInfo, error)

	// MkdirAll creates a directory and any necessary parents.
	MkdirAll(path string, perm os.FileMode) error

	// Remove removes a file.
	Remove(name string) error

	// Rename renames a file.
	Rename(oldpath, newpath string) error

	// Getenv retrieves the value of the environment variable named by the key.
	Getenv(key string) string

	// Setenv sets the value of the environment variable named by the key.
	Setenv(key, value string) error

	// Unsetenv unsets the environment variable named by the key.
	Unsetenv(key string) error

	// LookupEnv retrieves the value of the environment variable named by the key.
	LookupEnv(key string) (string, bool)
}

FileSystem defines the interface for file system operations. This interface allows for dependency injection, making code more testable by enabling mock implementations.

var DefaultFileSystem FileSystem = OSFileSystem{}

DefaultFileSystem is the default file system implementation using the real OS.

type FullAuditLogger ΒΆ added in v1.1.0

type FullAuditLogger interface {
	AuditLogger
	// Log records an audit event.
	Log(action AuditAction, key, reason string, success bool) error
	// LogWithFile records an audit event with file information.
	LogWithFile(action AuditAction, key, file, reason string, success bool) error
	// LogWithDuration records an audit event with timing information.
	LogWithDuration(action AuditAction, key, reason string, success bool, duration time.Duration) error
	// Close closes the audit logger and releases resources.
	Close() error
}

FullAuditLogger provides extended audit logging capabilities. It extends AuditLogger with additional methods for detailed logging. The built-in internal.Auditor implements this interface.

type JSONAuditHandler ΒΆ

type JSONAuditHandler = internal.JSONHandler

JSONAuditHandler writes audit events as JSON to an io.Writer.

func NewJSONAuditHandler ΒΆ

func NewJSONAuditHandler(w io.Writer) *JSONAuditHandler

NewJSONAuditHandler creates a new JSONAuditHandler.

type JSONConfig ΒΆ added in v1.1.0

type JSONConfig struct {
	JSONNullAsEmpty    bool // Convert null to empty string
	JSONNumberAsString bool // Convert numbers to strings
	JSONBoolAsString   bool // Convert booleans to strings
	JSONMaxDepth       int  // Maximum nesting depth
}

JSONConfig controls JSON parsing behavior.

type JSONError ΒΆ

type JSONError = ierrors.JSONError

JSONError represents a JSON parsing error. This is an alias for internal.JSONError to maintain backward compatibility.

type KeyValidator ΒΆ

type KeyValidator = internal.KeyValidator

KeyValidator is an alias for internal.KeyValidator. Implementations can enforce naming conventions, security policies, and length limits.

type LimitsConfig ΒΆ added in v1.1.0

type LimitsConfig struct {
	MaxFileSize       int64 // Maximum file size in bytes
	MaxVariables      int   // Maximum variables per file
	MaxLineLength     int   // Maximum line length
	MaxKeyLength      int   // Maximum key length
	MaxValueLength    int   // Maximum value length
	MaxExpansionDepth int   // Maximum variable expansion depth
}

LimitsConfig controls size and count limits for parsing.

type Loader ΒΆ

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

Loader is the main type for loading and managing environment variables. It provides thread-safe access to environment variables with full security validation, audit logging, and error handling.

func New ΒΆ

func New(cfg ...Config) (*Loader, error)

New creates a new Loader with the given configuration.

BEHAVIOR:

  • Does NOT set the package-level default loader
  • Does NOT auto-apply to os.Environ (unless cfg.AutoApply = true)
  • Can be called multiple times to create independent instances
  • Requires explicit lifecycle management: defer loader.Close()

If no configuration is provided or a zero-value Config is passed, DefaultConfig() is used automatically.

FOR SIMPLE USE CASES: Use Load() instead, which sets up the package-level default and applies to os.Environ automatically.

WHEN TO USE New():

  • Multiple loaders in one application (different configs/files)
  • Testing with isolated environment state
  • When you need explicit control over when variables are applied

Example:

// Use default configuration
loader, err := env.New()

// Use custom configuration
cfg := env.DefaultConfig()
cfg.Filenames = []string{".env.production"}
cfg.AutoApply = true
loader, err := env.New(cfg)

func (*Loader) All ΒΆ

func (l *Loader) All() map[string]string

All returns all environment variables as a map.

func (*Loader) Apply ΒΆ

func (l *Loader) Apply() error

Apply applies all loaded variables to the process environment.

func (*Loader) Close ΒΆ

func (l *Loader) Close() error

Close closes the loader and securely clears all stored values. If the loader owns its ComponentFactory, it will also close the factory.

func (*Loader) Config ΒΆ

func (l *Loader) Config() Config

Config returns the loader's configuration. Note: The returned Config should be treated as read-only. Modifying the Security.KeyPattern, AllowedKeys, ForbiddenKeys, or RequiredKeys fields may affect the loader's behavior. For a safe mutable copy, manually copy the necessary fields.

func (*Loader) Delete ΒΆ

func (l *Loader) Delete(key string) error

Delete removes a key.

func (*Loader) GetBool ΒΆ

func (l *Loader) GetBool(key string, defaultValue ...bool) bool

GetBool retrieves a boolean value with optional default. If the key is not found and no default is provided, returns false.

Example:

debug := loader.GetBool("DEBUG")           // Returns false if not found
debug := loader.GetBool("DEBUG", true)     // Returns true if not found

func (*Loader) GetDuration ΒΆ

func (l *Loader) GetDuration(key string, defaultValue ...time.Duration) time.Duration

GetDuration retrieves a duration value with optional default. If the key is not found and no default is provided, returns 0.

Example:

timeout := loader.GetDuration("TIMEOUT")                  // Returns 0 if not found
timeout := loader.GetDuration("TIMEOUT", 30*time.Second) // Returns 30s if not found

func (*Loader) GetInt ΒΆ

func (l *Loader) GetInt(key string, defaultValue ...int64) int64

GetInt retrieves an integer value with optional default. If the key is not found and no default is provided, returns 0.

Example:

port := loader.GetInt("PORT")           // Returns 0 if not found
port := loader.GetInt("PORT", 8080)     // Returns 8080 if not found

func (*Loader) GetSecure ΒΆ

func (l *Loader) GetSecure(key string) *SecureValue

GetSecure retrieves a SecureValue by key.

func (*Loader) GetString ΒΆ

func (l *Loader) GetString(key string, defaultValue ...string) string

GetString retrieves a value by key with optional default. If the key is not found and no default is provided, returns empty string. Supports dot-notation path resolution for nested keys (e.g., "database.host" -> "DATABASE_HOST").

Example:

value := loader.GetString("KEY")           // Returns "" if not found
value := loader.GetString("KEY", "default") // Returns "default" if not found

func (*Loader) IsApplied ΒΆ

func (l *Loader) IsApplied() bool

IsApplied returns true if the variables have been applied to os.Environ.

func (*Loader) IsClosed ΒΆ

func (l *Loader) IsClosed() bool

IsClosed returns true if the loader has been closed.

func (*Loader) Keys ΒΆ

func (l *Loader) Keys() []string

Keys returns all keys.

func (*Loader) Len ΒΆ

func (l *Loader) Len() int

Len returns the number of loaded variables.

func (*Loader) LoadFiles ΒΆ

func (l *Loader) LoadFiles(filenames ...string) error

LoadFiles loads environment variables from multiple files in order. If no filenames are provided, defaults to ".env". Files are loaded sequentially; later files can override values from earlier files.

Example:

// Load default .env file
err := loader.LoadFiles()

// Load specific files
err := loader.LoadFiles(".env", ".env.local")

func (*Loader) LoadTime ΒΆ

func (l *Loader) LoadTime() time.Time

LoadTime returns the time when variables were last loaded.

func (*Loader) Lookup ΒΆ

func (l *Loader) Lookup(key string) (string, bool)

Lookup retrieves a value by key and reports whether it exists. Supports dot-notation path resolution for nested keys (e.g., "database.host" -> "DATABASE_HOST"). For indexed access (e.g., "service.cors.origins.0"), falls back to comma-separated values if indexed key is not found. Returns the value with leading and trailing whitespace trimmed.

func (*Loader) ParseInto ΒΆ

func (l *Loader) ParseInto(v interface{}) error

ParseInto populates a struct from loaded environment variables. Struct fields can be tagged with `env:"KEY"` to specify the env variable name. Optional `envDefault:"value"` sets a default if the key is not found.

func (*Loader) Set ΒΆ

func (l *Loader) Set(key, value string) error

Set sets a value for a key.

func (*Loader) Validate ΒΆ

func (l *Loader) Validate() error

Validate validates the loaded environment against required and allowed keys.

type LogAuditHandler ΒΆ

type LogAuditHandler = internal.LogHandler

LogAuditHandler writes audit events using the standard log package.

func NewLogAuditHandler ΒΆ

func NewLogAuditHandler(logger *log.Logger) *LogAuditHandler

NewLogAuditHandler creates a new LogAuditHandler.

type MarshalError ΒΆ

type MarshalError = ierrors.MarshalError

MarshalError represents a marshaling/unmarshaling error. This is an alias for internal.MarshalError to maintain backward compatibility.

type Marshaler ΒΆ

type Marshaler interface {
	MarshalEnv() ([]byte, error)
}

Marshaler is the interface for types that can marshal themselves to env format.

type NopAuditHandler ΒΆ

type NopAuditHandler = internal.NopHandler

NopAuditHandler discards all audit events.

func NewNopAuditHandler ΒΆ

func NewNopAuditHandler() *NopAuditHandler

NewNopAuditHandler creates a new NopAuditHandler.

type OSFileSystem ΒΆ

type OSFileSystem struct{}

OSFileSystem implements FileSystem using the real operating system. OSFileSystem is safe for concurrent use. The zero value is valid and ready to use.

func (OSFileSystem) Getenv ΒΆ

func (OSFileSystem) Getenv(key string) string

Getenv retrieves the value of the environment variable using os.Getenv.

func (OSFileSystem) LookupEnv ΒΆ

func (OSFileSystem) LookupEnv(key string) (string, bool)

LookupEnv retrieves the value of the environment variable using os.LookupEnv.

func (OSFileSystem) MkdirAll ΒΆ

func (OSFileSystem) MkdirAll(path string, perm os.FileMode) error

MkdirAll creates a directory and any necessary parents using os.MkdirAll.

func (OSFileSystem) Open ΒΆ

func (OSFileSystem) Open(name string) (File, error)

Open opens a file for reading using os.Open.

func (OSFileSystem) OpenFile ΒΆ

func (OSFileSystem) OpenFile(name string, flag int, perm os.FileMode) (File, error)

OpenFile opens a file with the specified flags and permissions.

func (OSFileSystem) Remove ΒΆ

func (OSFileSystem) Remove(name string) error

Remove removes a file using os.Remove.

func (OSFileSystem) Rename ΒΆ

func (OSFileSystem) Rename(oldpath, newpath string) error

Rename renames a file using os.Rename.

func (OSFileSystem) Setenv ΒΆ

func (OSFileSystem) Setenv(key, value string) error

Setenv sets the value of the environment variable using os.Setenv.

func (OSFileSystem) Stat ΒΆ

func (OSFileSystem) Stat(name string) (os.FileInfo, error)

Stat returns file information using os.Stat.

func (OSFileSystem) Unsetenv ΒΆ

func (OSFileSystem) Unsetenv(key string) error

Unsetenv unsets the environment variable using os.Unsetenv.

type ParseError ΒΆ

type ParseError = ierrors.ParseError

ParseError provides detailed information about parsing failures. This is an alias for internal.ParseError to maintain backward compatibility.

type ParserFactory ΒΆ

type ParserFactory func(cfg Config, factory *ComponentFactory) (EnvParser, error)

ParserFactory creates an EnvParser from a Config and ComponentFactory. Custom parser implementations should implement this function signature to register their parsers with the library.

type ParsingConfig ΒΆ added in v1.1.0

type ParsingConfig struct {
	AllowExportPrefix bool // Allow "export KEY=value" syntax
	AllowYamlSyntax   bool // Allow YAML-style values in .env
	ExpandVariables   bool // Expand ${VAR} references
}

ParsingConfig controls general parsing behavior.

type RequiredValidator ΒΆ added in v1.1.0

type RequiredValidator interface {
	// ValidateRequired checks that all required keys are present.
	ValidateRequired(keys map[string]bool) error
}

RequiredValidator defines the interface for validating required keys.

type SecureValue ΒΆ

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

SecureValue wraps a sensitive value with automatic memory zeroing. When the value is garbage collected, its memory is securely cleared. If memory locking is enabled, the data is also protected from being swapped to disk.

func GetSecure ΒΆ

func GetSecure(key string) *SecureValue

GetSecure retrieves a SecureValue from the default loader. Returns nil if the loader cannot be initialized or the key is not found. Use GetSecure for sensitive values that need secure memory handling.

Example:

sv := env.GetSecure("API_KEY")
if sv != nil {
    fmt.Println(sv.Masked()) // Safe for logging
    data := sv.Bytes()
    defer env.ClearBytes(data)
    // Use data securely
}

func NewSecureValue ΒΆ

func NewSecureValue(value string) *SecureValue

NewSecureValue creates a new SecureValue from a string. The value is stored in a separate memory allocation that will be zeroed when the SecureValue is garbage collected or explicitly closed. This function uses a pool to reduce allocations.

Memory Locking: If memory locking is enabled globally (via SetMemoryLockEnabled(true)), this function will attempt to lock the memory to prevent swapping. Locking failures are silently ignored unless strict mode is enabled.

func NewSecureValueStrict ΒΆ

func NewSecureValueStrict(value string) (*SecureValue, error)

NewSecureValueStrict creates a new SecureValue and returns an error if memory locking is enabled but fails. Use this function when you need to ensure that the memory is actually protected from being swapped to disk.

Example:

env.SetMemoryLockEnabled(true)
sv, err := env.NewSecureValueStrict("sensitive-data")
if err != nil {
    // Memory locking failed - handle appropriately
    log.Printf("Warning: memory not locked: %v", err)
}
defer sv.Release()

func (*SecureValue) Bytes ΒΆ

func (sv *SecureValue) Bytes() []byte

Bytes returns a copy of the value as a byte slice. The caller is responsible for securely clearing the returned slice using ClearBytes.

func (*SecureValue) Close ΒΆ

func (sv *SecureValue) Close() error

Close securely clears the value and marks it as closed. After calling Close, all access methods return zero values. Note: This method does NOT return the SecureValue to the pool. For explicit pool return, use Release() instead.

func (*SecureValue) IsClosed ΒΆ

func (sv *SecureValue) IsClosed() bool

IsClosed returns true if the value has been closed.

func (*SecureValue) IsMemoryLocked ΒΆ

func (sv *SecureValue) IsMemoryLocked() bool

IsMemoryLocked returns true if the value's memory is currently locked (protected from being swapped to disk). Returns false if memory locking is not enabled or if locking failed.

func (*SecureValue) Length ΒΆ

func (sv *SecureValue) Length() int

Length returns the length of the value without exposing it.

func (*SecureValue) Masked ΒΆ

func (sv *SecureValue) Masked() string

Masked returns a masked representation for logging.

func (*SecureValue) MemoryLockError ΒΆ

func (sv *SecureValue) MemoryLockError() error

MemoryLockError returns any error that occurred during memory locking. Returns nil if locking was successful or not attempted. This is useful in strict mode to detect if memory locking failed.

func (*SecureValue) Release ΒΆ

func (sv *SecureValue) Release()

Release securely clears the value and returns it to the pool. This is more efficient than Close() for high-frequency operations as it allows the SecureValue to be reused.

The finalizer is cleared before returning to the pool to ensure: 1. The object can be safely reused without finalizer interference 2. NewSecureValue() will set a fresh finalizer when the object is reused

func (*SecureValue) String ΒΆ

func (sv *SecureValue) String() string

String returns the value as a string. This method is provided for convenience but should be used carefully as it creates a copy of the sensitive data.

type SecurityError ΒΆ

type SecurityError = ierrors.SecurityError

SecurityError provides detailed information about security violations. This is an alias for internal.SecurityError to maintain backward compatibility.

type Unmarshaler ΒΆ

type Unmarshaler interface {
	UnmarshalEnv(map[string]string) error
}

Unmarshaler is the interface for types that can unmarshal themselves from env values.

type ValidationConfig ΒΆ added in v1.1.0

type ValidationConfig struct {
	RequiredKeys   []string       // Require these keys to be present
	AllowedKeys    []string       // Only allow these keys (empty = all allowed)
	ForbiddenKeys  []string       // Always forbid these keys
	KeyPattern     *regexp.Regexp // Pattern for valid keys (nil = default)
	ValidateValues bool           // Validate values for security issues
	ValidateUTF8   bool           // Validate that values are valid UTF-8
}

ValidationConfig controls key and value validation.

type ValidationError ΒΆ

type ValidationError = ierrors.ValidationError

ValidationError provides detailed information about validation failures. This is an alias for internal.ValidationError to maintain backward compatibility.

type Validator ΒΆ

type Validator interface {
	KeyValidator
	ValueValidator
	RequiredValidator
}

Validator combines all validation capabilities. Implementations should implement all three methods for full functionality. Partial implementations (only KeyValidator) will return ErrValidateRequiredUnsupported from ValidateRequired.

type ValueValidator ΒΆ

type ValueValidator = internal.ValueValidator

ValueValidator is an alias for internal.ValueValidator. Implementations can check for security issues like null bytes or control characters.

type VariableExpander ΒΆ

type VariableExpander = internal.VariableExpander

VariableExpander is an alias for internal.VariableExpander. Implementations can support different expansion syntaxes ($VAR, ${VAR}, etc.).

type YAMLConfig ΒΆ added in v1.1.0

type YAMLConfig struct {
	YAMLNullAsEmpty    bool // Convert null/~ to empty string
	YAMLNumberAsString bool // Convert numbers to strings
	YAMLBoolAsString   bool // Convert booleans to strings
	YAMLMaxDepth       int  // Maximum nesting depth
}

YAMLConfig controls YAML parsing behavior.

type YAMLError ΒΆ

type YAMLError = ierrors.YAMLError

YAMLError represents a YAML parsing error. This is an alias for internal.YAMLError to maintain backward compatibility.

Directories ΒΆ

Path Synopsis
Package internal provides audit logging functionality.
Package internal provides audit logging functionality.

Jump to

Keyboard shortcuts

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