This document provides an in-depth technical overview of all features and capabilities of the SecretFetch library.
- Core Concepts
- Struct Tags
- Value Sources
- Type Conversion
- Validation
- Value Processing
- Caching
- Error Handling
- AWS Integration
- Concurrency
- Performance Considerations
The internal secret
type is the core of SecretFetch's functionality:
type secret struct {
pattern *regexp.Regexp // Compiled regex pattern for validation
isBase64 bool // Whether value should be base64 decoded
isJSON bool // Whether value should be parsed as JSON
isYAML bool // Whether value should be parsed as YAML
value string // The actual secret value
ttl time.Duration // Cache duration
fetchedAt time.Time // When the value was last fetched
validation func(string) error // Custom validation function
transform func(string) (string, error) // Custom transformation function
field reflect.StructField // The struct field this secret belongs to
envKey string // Environment variable key
fallback string // Fallback value
awsKey string // AWS Secrets Manager key
mu sync.RWMutex // Mutex for thread-safe operations
cache *cachedValue // Cached value and expiration
}
aws
: AWS Secrets Manager keyenv
: Environment variable namefallback
: Default value if no other source provides onerequired
: Mark field as requiredpattern
: Regex pattern for validationbase64
: Enable base64 decodingjson
: Parse value as JSONyaml
: Parse value as YAMLttl
: Cache duration
// Basic usage
`secret:"env=MY_VAR"`
// Multiple options
`secret:"aws=my/secret,env=MY_VAR,fallback=default"`
// With validation
`secret:"env=MY_VAR,pattern=^[A-Z]+$"`
// With encoding
`secret:"env=MY_VAR,base64"`
// With caching
`secret:"aws=my/secret,ttl=5m"`
- AWS Secrets Manager (if configured and key exists)
- Environment Variables
- Fallback Value
- Return error if no value found and field is required
// Single value
type Config struct {
APIKey string `secret:"aws=prod/api/key"`
}
// JSON object
type Config struct {
Database struct {
Host string `json:"host"`
Port int `json:"port"`
} `secret:"aws=prod/db/config,json"`
}
// Basic usage
type Config struct {
LogLevel string `secret:"env=LOG_LEVEL"`
}
// With prefix (via Options)
opts := &secretfetch.Options{
Prefix: "MYAPP_", // Will look for MYAPP_LOG_LEVEL
}
type Config struct {
// String fallback
Host string `secret:"env=HOST,fallback=localhost"`
// Numeric fallback
Port int `secret:"env=PORT,fallback=8080"`
// Duration fallback
Timeout time.Duration `secret:"env=TIMEOUT,fallback=30s"`
// Boolean fallback
Debug bool `secret:"env=DEBUG,fallback=false"`
}
string
bool
int
,int8
,int16
,int32
,int64
uint
,uint8
,uint16
,uint32
,uint64
float32
,float64
time.Duration
[]byte
- Any struct that implements
json.Unmarshaler
oryaml.Unmarshaler
type Config struct {
// String to int
Port int `secret:"env=PORT,fallback=8080"`
// "8080" -> 8080
// String to bool
Debug bool `secret:"env=DEBUG,fallback=true"`
// "true", "1", "yes", "on" -> true
// "false", "0", "no", "off" -> false
// String to duration
Timeout time.Duration `secret:"env=TIMEOUT,fallback=30s"`
// "30s", "5m", "2h" -> time.Duration
// Base64 to []byte
Cert []byte `secret:"env=TLS_CERT,base64"`
// "aGVsbG8=" -> []byte("hello")
}
type Config struct {
// Email validation
Email string `secret:"env=EMAIL,pattern=^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"`
// IP address
IP string `secret:"env=IP,pattern=^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$"`
// Semantic version
Version string `secret:"env=VERSION,pattern=^v\\d+\\.\\d+\\.\\d+$"`
// UUID
ID string `secret:"env=ID,pattern=[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"`
}
type Config struct {
Password string `secret:"env=PASSWORD"`
}
opts := &secretfetch.Options{
Validators: map[string]secretfetch.ValidatorFunc{
"PASSWORD": func(value string) error {
if len(value) < 8 {
return fmt.Errorf("too short")
}
if !strings.ContainsAny(value, "0123456789") {
return fmt.Errorf("must contain a number")
}
return nil
},
},
}
type Config struct {
// Decode to string
Token string `secret:"env=TOKEN,base64"`
// Decode to bytes
Key []byte `secret:"env=KEY,base64"`
}
type Config struct {
// Parse object
Database struct {
Host string `json:"host"`
Port int `json:"port"`
} `secret:"aws=db/config,json"`
// Parse array
AllowedIPs []string `secret:"env=ALLOWED_IPS,json"`
// Parse complex types
Settings map[string]interface{} `secret:"aws=app/settings,json"`
}
opts := &secretfetch.Options{
Transformers: map[string]secretfetch.TransformerFunc{
// Trim whitespace
"USERNAME": strings.TrimSpace,
// Convert to lowercase
"EMAIL": strings.ToLower,
// Custom transformation
"API_KEY": func(v string) (string, error) {
if !strings.HasPrefix(v, "key_") {
v = "key_" + v
}
return v, nil
},
},
}
type Config struct {
// Cache for specific duration
APIKey string `secret:"aws=api/key,ttl=5m"`
// Cache forever
StaticConfig string `secret:"aws=static/config,ttl=-1"`
}
// Global cache settings
opts := &secretfetch.Options{
DefaultTTL: 10 * time.Minute,
}
- Thread-safe access via mutex
- Lazy loading - only fetches when needed
- Automatic expiration
- Memory-efficient storage
- No persistence across restarts
// Validation error
type ValidationError struct {
Field string
Err error
}
// Pattern match error
type PatternError struct {
Field string
Pattern string
Value string
}
// Required field error
type RequiredError struct {
Field string
}
// Type conversion error
type ConversionError struct {
Field string
FromType string
ToType string
Value string
}
err := secretfetch.Fetch(context.Background(), cfg, opts)
switch e := err.(type) {
case *secretfetch.ValidationError:
log.Printf("Validation failed for %s: %v", e.Field, e.Err)
case *secretfetch.PatternError:
log.Printf("Pattern match failed for %s: %v", e.Field, e.Value)
case *secretfetch.RequiredError:
log.Printf("Required field missing: %s", e.Field)
case *secretfetch.ConversionError:
log.Printf("Type conversion failed for %s: %v to %v",
e.Field, e.FromType, e.ToType)
default:
log.Printf("Unknown error: %v", err)
}
opts := &secretfetch.Options{
AWS: &aws.Config{
Region: aws.String("us-west-2"),
Credentials: credentials.NewStaticCredentialsProvider(
"ACCESS_KEY",
"SECRET_KEY",
"",
),
},
}
- Uses AWS SDK v2
- Supports IAM roles
- Automatic retry with backoff
- Cross-region access
- Supports versioned secrets
- Binary secret values
- All operations are thread-safe
- Uses sync.RWMutex for cache access
- Safe for concurrent reads and writes
- No global state
// Safe for concurrent use
for i := 0; i < 100; i++ {
go func() {
cfg := &Config{}
if err := secretfetch.Fetch(context.Background(), cfg, opts); err != nil {
log.Printf("Error: %v", err)
}
}()
}
- In-memory caching reduces AWS API calls
- Cache hits have near-zero overhead
- Configurable TTL per field
- Smart cache invalidation
- Only caches actively used values
- Efficient string interning
- No unnecessary allocations
- Garbage collector friendly
- Batches AWS requests when possible
- Reuses HTTP connections
- Implements exponential backoff
- Respects AWS rate limits
- Use appropriate TTL values
- Group related secrets in JSON
- Use fallback values for development
- Implement custom validators efficiently
- Handle errors gracefully
- Monitor AWS API usage
- Use IAM roles with minimal permissions
- Regularly rotate secrets
- Implement proper logging
- Follow security best practices