# internal/config Centralized YAML configuration management with thread-safe state access and provider initialization. ## Overview The config package implements the core configuration management system for GoDoxy, handling YAML configuration loading, provider initialization, route loading, and state transitions. It uses atomic pointers for thread-safe state access and integrates all configuration components. ### Primary consumers - `cmd/main.go` - Initializes configuration state on startup - `internal/route/provider` - Accesses configuration for route creation - `internal/api/v1` - Exposes configuration via REST API - All packages that need to access active configuration ### Non-goals - Dynamic provider registration after initialization (require config reload) ### Stability Stable internal package. Public API consists of `State` interface and state management functions. ## Public API ### Exported types ```go type Config struct { ACL *acl.Config AutoCert *autocert.Config Entrypoint entrypoint.Config Providers Providers MatchDomains []string Homepage homepage.Config Defaults Defaults TimeoutShutdown int } type Providers struct { Files []string Docker map[string]types.DockerProviderConfig Agents []*agent.AgentConfig Notification []*notif.NotificationConfig Proxmox []proxmox.Config MaxMind *maxmind.Config } ``` ### State interface ```go type State interface { Task() *task.Task Context() context.Context Value() *Config Entrypoint() entrypoint.Entrypoint ShortLinkMatcher() config.ShortLinkMatcher AutoCertProvider() server.CertProvider LoadOrStoreProvider(key string, value types.RouteProvider) (actual types.RouteProvider, loaded bool) DeleteProvider(key string) IterProviders() iter.Seq2[string, types.RouteProvider] StartProviders() error NumProviders() int // Lifecycle management StartAPIServers() StartMetrics() FlushTmpLog() } ``` ### Exported functions ```go func NewState() config.State ``` Creates a new configuration state with empty providers map. ```go func GetState() config.State ``` Returns the active configuration state. Thread-safe via atomic load. ```go func SetState(state config.State) ``` Sets the active configuration state. Also updates active configs for ACL, entrypoint, homepage, and autocert. ```go func HasState() bool ``` Returns true if a state is currently active. ```go func Value() *config.Config ``` Returns the current configuration values. ```go func (state *state) InitFromFile(filename string) error ``` Initializes state from a YAML file. Uses default config if file doesn't exist. ```go func (state *state) Init(data []byte) error ``` Initializes state from raw YAML data. Validates, then initializes MaxMind, Proxmox, providers, AutoCert, notifications, access logger, and entrypoint. ```go func (state *state) StartProviders() error ``` Starts all route providers concurrently. ```go func (state *state) IterProviders() iter.Seq2[string, types.RouteProvider] ``` Returns an iterator over all providers. ## Architecture ### Core components ```mermaid graph TD A[config.yml] --> B[State] B --> C{Initialize} C --> D[Validate YAML] C --> E[Init MaxMind] C --> F[Init Proxmox] C --> G[Load Route Providers] C --> H[Init AutoCert] C --> I[Init Notifications] C --> J[Init Entrypoint] K[ActiveConfig] -.-> B subgraph Providers G --> L[Docker Provider] G --> M[File Provider] G --> N[Agent Provider] end subgraph State Management B --> O[xsync.Map Providers] B --> P[Entrypoint] B --> Q[AutoCert Provider] B --> R[task.Task] end ``` ### Initialization pipeline ```mermaid sequenceDiagram participant YAML participant State participant MaxMind participant Proxmox participant Providers participant AutoCert participant Notif participant Entrypoint YAML->>State: Parse & Validate par Initialize in parallel State->>MaxMind: Initialize State->>Proxmox: Initialize and State->>Providers: Load Route Providers Providers->>State: Store Providers end State->>AutoCert: Initialize State->>Notif: Initialize State->>Entrypoint: Configure State->>State: Start Providers ``` ### Thread safety model ```go var stateMu sync.RWMutex func GetState() config.State { return config.ActiveState.Load() } func SetState(state config.State) { stateMu.Lock() defer stateMu.Unlock() config.ActiveState.Store(state) } ``` Uses `sync.RWMutex` for write synchronization and `sync/atomic` for read operations. ## Configuration Surface ### Config sources Configuration is loaded from `config/config.yml`. ### Hot-reloading Configuration supports hot-reloading via editing `config/config.yml`. ## Dependency and Integration Map ### Internal dependencies - `internal/acl` - Access control configuration - `internal/autocert` - SSL certificate management - `internal/entrypoint` - HTTP entrypoint setup (now via interface) - `internal/route/provider` - Route providers (Docker, file, agent) - `internal/maxmind` - GeoIP configuration - `internal/notif` - Notification providers - `internal/proxmox` - LXC container management - `internal/homepage/types` - Dashboard configuration - `internal/api` - REST API servers - `internal/metrics/systeminfo` - System metrics polling - `internal/metrics/uptime` - Uptime tracking - `github.com/yusing/goutils/task` - Object lifecycle management ### External dependencies - `github.com/goccy/go-yaml` - YAML parsing - `github.com/puzpuzpuz/xsync/v4` - Concurrent map ### Integration points ```go // API uses config/query to access state providers := statequery.RouteProviderList() // Route providers access config state for _, p := range config.GetState().IterProviders() { // Process provider } ``` ## Observability ### Logs - Configuration parsing and validation errors - Provider initialization results - Route loading summary - Full configuration dump (at debug level) ### Metrics No metrics are currently exposed. ## Security Considerations - Configuration file permissions should be restricted (contains secrets) - TLS certificates are loaded from files specified in config - Agent credentials are passed via configuration - No secrets are logged (except in debug mode with full config dump) ## Failure Modes and Recovery | Failure | Behavior | Recovery | | ----------------------------- | ----------------------------------- | -------------------------- | | Invalid YAML | Init returns error | Fix YAML syntax | | Missing required fields | Validation fails | Add required fields | | Provider initialization fails | Error aggregated and returned | Fix provider configuration | | Duplicate provider key | Error logged, first provider kept | Rename provider | | Route loading fails | Error aggregated, other routes load | Fix route configuration | ## Performance Characteristics - Providers are loaded concurrently - Routes are loaded concurrently per provider - State access is lock-free for reads - Atomic pointer for state swap ## Usage Examples ### Loading configuration ```go state := config.NewState() err := state.InitFromFile("config.yml") if err != nil { log.Fatal(err) } config.SetState(state) ``` ### Accessing configuration ```go if config.HasState() { cfg := config.Value() log.Printf("Entrypoint middleware count: %d", len(cfg.Entrypoint.Middlewares)) log.Printf("Docker providers: %d", len(cfg.Providers.Docker)) } ``` ### Iterating providers ```go for name, provider := range config.GetState().IterProviders() { log.Printf("Provider: %s, Routes: %d", name, provider.NumRoutes()) } ``` ### Accessing entrypoint handler ```go state := config.GetState() // Get entrypoint interface for route management ep := state.Entrypoint() // Add routes directly to entrypoint ep.AddRoute(route) ```