Quickstart
Requirements
Go 1.24 or later.
Installation
go get github.com/zoobz-io/flux
go get github.com/zoobz-io/flux/file # For file watching
Basic Example
Watch a JSON config file and reload on changes:
package main
import (
"context"
"errors"
"log"
"os"
"os/signal"
"github.com/zoobz-io/flux"
"github.com/zoobz-io/flux/file"
)
type Config struct {
Port int `json:"port"`
Host string `json:"host"`
LogLevel string `json:"log_level"`
}
func (c Config) Validate() error {
if c.Port < 1 || c.Port > 65535 {
return errors.New("port must be between 1 and 65535")
}
if c.Host == "" {
return errors.New("host is required")
}
switch c.LogLevel {
case "debug", "info", "warn", "error":
// valid
default:
return errors.New("log_level must be one of: debug, info, warn, error")
}
return nil
}
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
capacitor := flux.New[Config](
file.New("/etc/myapp/config.json"),
func(ctx context.Context, prev, curr Config) error {
log.Printf("config updated: port=%d, host=%s", curr.Port, curr.Host)
return nil
},
)
if err := capacitor.Start(ctx); err != nil {
log.Printf("initial config failed: %v", err)
}
log.Printf("state: %s", capacitor.State())
<-ctx.Done()
}
Config File
{
"port": 8080,
"host": "localhost",
"log_level": "info"
}
Run It
go run main.go
# Output: config updated: port=8080, host=localhost
# Output: state: healthy
# Edit config.json in another terminal
# Output: config updated: port=9090, host=localhost
Validation
Configuration types must implement the Validator interface:
type Validator interface {
Validate() error
}
Your Validate() method has full control over validation logic:
type Config struct {
Port int `json:"port"`
Host string `json:"host"`
LogLevel string `json:"log_level"`
}
func (c Config) Validate() error {
if c.Port < 1 || c.Port > 65535 {
return fmt.Errorf("port must be between 1 and 65535, got %d", c.Port)
}
if c.Host == "" {
return errors.New("host is required")
}
return nil
}
For complex validation, you can use libraries like go-playground/validator inside your Validate() method.
Invalid configs are rejected. The previous valid config is retained.
Accessing Current Config
cfg, ok := capacitor.Current()
if !ok {
// No valid config yet (Loading or Empty state)
return
}
fmt.Printf("using port %d\n", cfg.Port)
Error Handling
// Check last error
if err := capacitor.LastError(); err != nil {
log.Printf("config error: %v", err)
}
// Check state
switch capacitor.State() {
case flux.StateHealthy:
// All good
case flux.StateDegraded:
// Update failed, using previous config
case flux.StateEmpty:
// No valid config ever loaded
}
YAML Support
capacitor := flux.New[Config](
file.New("/etc/myapp/config.yaml"),
callback,
).Codec(flux.YAMLCodec{})
Debouncing
Rapid changes are coalesced:
capacitor := flux.New[Config](
watcher,
callback,
).Debounce(200*time.Millisecond) // Default: 100ms
Next Steps
- Core Concepts - Understand Capacitor, Watcher, State
- Providers Guide - Redis, Consul, etcd, and more
- Testing Guide - Sync mode for deterministic tests