// Package config provides TOML configuration loading and validation. package config import ( "fmt" "os" "github.com/pelletier/go-toml/v2" ) // Config is the top-level configuration for Metacrypt. type Config struct { Server ServerConfig `toml:"server"` Database DatabaseConfig `toml:"database"` MCIAS MCIASConfig `toml:"mcias"` Seal SealConfig `toml:"seal"` Log LogConfig `toml:"log"` } // ServerConfig holds HTTP/gRPC server settings. type ServerConfig struct { ListenAddr string `toml:"listen_addr"` GRPCAddr string `toml:"grpc_addr"` TLSCert string `toml:"tls_cert"` TLSKey string `toml:"tls_key"` ExternalURL string `toml:"external_url"` // public base URL for ACME directory, e.g. "https://metacrypt.example.com" } // DatabaseConfig holds SQLite database settings. type DatabaseConfig struct { Path string `toml:"path"` } // MCIASConfig holds MCIAS integration settings. type MCIASConfig struct { ServerURL string `toml:"server_url"` CACert string `toml:"ca_cert"` } // SealConfig holds Argon2id parameters for the seal process. type SealConfig struct { Argon2Time uint32 `toml:"argon2_time"` Argon2Memory uint32 `toml:"argon2_memory"` Argon2Threads uint8 `toml:"argon2_threads"` } // LogConfig holds logging settings. type LogConfig struct { Level string `toml:"level"` } // Load reads and parses a TOML config file. func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("config: read file: %w", err) } var cfg Config if err := toml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("config: parse: %w", err) } if err := cfg.Validate(); err != nil { return nil, err } return &cfg, nil } // Validate checks required fields and applies defaults. func (c *Config) Validate() error { if c.Server.ListenAddr == "" { return fmt.Errorf("config: server.listen_addr is required") } if c.Server.TLSCert == "" { return fmt.Errorf("config: server.tls_cert is required") } if c.Server.TLSKey == "" { return fmt.Errorf("config: server.tls_key is required") } if c.Database.Path == "" { return fmt.Errorf("config: database.path is required") } if c.MCIAS.ServerURL == "" { return fmt.Errorf("config: mcias.server_url is required") } // Apply defaults for seal parameters. if c.Seal.Argon2Time == 0 { c.Seal.Argon2Time = 3 } if c.Seal.Argon2Memory == 0 { c.Seal.Argon2Memory = 128 * 1024 } if c.Seal.Argon2Threads == 0 { c.Seal.Argon2Threads = 4 } if c.Log.Level == "" { c.Log.Level = "info" } return nil }