Skip to content

Configuration Model

Plumego services are configured through an explicit core.AppConfig struct. There is no hidden configuration registry, no global state, and no reflection-based auto-binding. You construct the config, override the fields you need, and pass it to core.New.

import "github.com/spcent/plumego/core"
func main() {
cfg, err := config.Load() // your app-local loader
if err != nil {
log.Fatalf("config: %v", err)
}
app := core.New(cfg.Core, core.AppDependencies{
Logger: myLogger,
})
// register routes and middleware ...
app.Start()
}

config.Load() is an application-level function you own. A minimal implementation:

func Load() (Config, error) {
addr := os.Getenv("ADDR")
if addr == "" {
addr = ":8080"
}
cfg := core.DefaultConfig()
cfg.Addr = addr
return Config{Core: cfg}, nil
}

core.DefaultConfig() returns a safe baseline. Override only what differs.

FieldTypeDefaultDescription
Addrstring:8080TCP address to listen on (e.g. ":9090", "0.0.0.0:443")
TLS.EnabledboolfalseEnable TLS
TLS.CertFilestring""Path to TLS certificate PEM
TLS.KeyFilestring""Path to TLS private key PEM
Router.MethodNotAllowedboolfalseReturn 405 + Allow header when method mismatches a known path
ReadTimeouttime.Duration30sMax duration for reading the entire request
ReadHeaderTimeouttime.Duration5sMax duration for reading request headers (slowloris protection)
WriteTimeouttime.Duration30sMax duration before timing out response writes
IdleTimeouttime.Duration60sMax time to wait for the next request on a keep-alive connection
MaxHeaderBytesint1 MiBMax size of request headers
HTTP2EnabledbooltrueEnable HTTP/2 (requires TLS for browsers; enabled by default)
DrainIntervaltime.Duration500msInterval for logging in-flight connection counts during graceful shutdown
type AppDependencies struct {
Logger log.StructuredLogger
}

Logger is the only injected dependency at the kernel level. Omitting it discards all log output. Pass log.NewLogger() for the default structured logger.

cfg := core.DefaultConfig()
cfg.Addr = ":443"
cfg.TLS.Enabled = true
cfg.TLS.CertFile = "/etc/tls/cert.pem"
cfg.TLS.KeyFile = "/etc/tls/key.pem"
app := core.New(cfg, deps)

In the bootstrap path, detect cfg.TLS.Enabled to choose the start method:

srv, _ := app.Server()
if cfg.TLS.Enabled {
log.Fatal(srv.ListenAndServeTLS("", ""))
} else {
log.Fatal(srv.ListenAndServe())
}

Plumego does not bind environment variables automatically. The canonical pattern is to read them in your application’s config.Load() function:

func Load() (Config, error) {
cfg := core.DefaultConfig()
if addr := os.Getenv("ADDR"); addr != "" {
cfg.Addr = addr
}
if v := os.Getenv("READ_TIMEOUT"); v != "" {
d, err := time.ParseDuration(v)
if err != nil {
return Config{}, fmt.Errorf("READ_TIMEOUT: %w", err)
}
cfg.ReadTimeout = d
}
if os.Getenv("TLS_ENABLED") == "true" {
cfg.TLS = core.TLSConfig{
Enabled: true,
CertFile: os.Getenv("TLS_CERT_FILE"),
KeyFile: os.Getenv("TLS_KEY_FILE"),
}
}
return Config{Core: cfg}, nil
}

See reference/standard-service/internal/config/config.go for a complete example.

For production deployments, review these timeout fields:

  • Set ReadHeaderTimeout to 5s or less to mitigate slowloris attacks.
  • Set WriteTimeout to match your longest expected handler latency plus a buffer.
  • Set IdleTimeout below your load balancer’s idle timeout to avoid race conditions on connection reuse.
  • Set MaxHeaderBytes to the minimum value your clients require (default 1 MiB is generous).