Configuration
Configuring a Herald instance and the Forge extension.
Herald can run standalone or as a Forge extension. Both modes share the same core Config struct, with the extension adding route and migration options on top.
herald.Config
Import: github.com/xraph/herald
The root Config struct controls core notification delivery behavior:
type Config struct {
// DefaultLocale is the default locale for template rendering (default: "en").
DefaultLocale string
// MaxBatchSize is the maximum number of notifications per batch send.
MaxBatchSize int
// TruncateBodyAt is the maximum message body length stored in the delivery log.
// Bodies longer than this are truncated. 0 means no truncation.
TruncateBodyAt int
}Field reference
| Field | Type | Default | Description |
|---|---|---|---|
DefaultLocale | string | "en" | BCP 47 locale tag used when a SendRequest does not specify a locale. |
MaxBatchSize | int | 100 | Maximum recipients per batch send via Herald.Notify. |
TruncateBodyAt | int | 4096 | Maximum character length for the Body field in delivery log messages. Set to 0 to disable truncation. |
DefaultConfig
DefaultConfig() returns a Config with sensible defaults:
cfg := herald.DefaultConfig()
// cfg.DefaultLocale = "en"
// cfg.MaxBatchSize = 100
// cfg.TruncateBodyAt = 4096Option functions
Herald uses the functional options pattern. Option is a function type that configures a Herald instance:
type Option func(*Herald) errorOptions are passed to the herald.New constructor:
h, err := herald.New(
herald.WithStore(myStore),
herald.WithDefaultLocale("fr"),
herald.WithMaxBatchSize(50),
herald.WithDriver(&email.SMTPDriver{}),
herald.WithLogger(slog.Default()),
)Available options
| Option | Description |
|---|---|
WithStore(s store.Store) | Sets the persistence backend. Required -- Herald returns ErrNoStore without it. |
WithLogger(l *slog.Logger) | Sets the structured logger. Defaults to slog.Default(). |
WithDriver(d driver.Driver) | Registers a notification driver (SMTP, Twilio, FCM, etc.). Can be called multiple times. |
WithDefaultLocale(locale string) | Overrides the default locale for template rendering. |
WithMaxBatchSize(n int) | Overrides the maximum batch size. |
Minimal standalone setup
import (
"log/slog"
"github.com/xraph/herald"
"github.com/xraph/herald/driver/email"
"github.com/xraph/herald/store/memory"
)
h, err := herald.New(
herald.WithStore(memory.New()),
herald.WithDriver(&email.SMTPDriver{}),
)
if err != nil {
log.Fatal(err)
}Extension configuration
Import: github.com/xraph/herald/extension
When running as a Forge extension, Herald wraps the core config with additional fields for route registration, migration control, and Grove database integration.
extension.Config
type Config struct {
herald.Config `json:",inline" yaml:",inline" mapstructure:",squash"`
// BasePath is the URL prefix for all herald routes (default: "/herald").
BasePath string `json:"base_path" yaml:"base_path" mapstructure:"base_path"`
// DisableRoutes disables automatic route registration with the Forge router.
DisableRoutes bool `json:"disable_routes" yaml:"disable_routes" mapstructure:"disable_routes"`
// DisableMigrate disables automatic database migration on Register.
DisableMigrate bool `json:"disable_migrate" yaml:"disable_migrate" mapstructure:"disable_migrate"`
// GroveDatabase is the name of a grove.DB registered in the DI container.
GroveDatabase string `json:"grove_database" yaml:"grove_database" mapstructure:"grove_database"`
}| Field | Type | Default | Description |
|---|---|---|---|
BasePath | string | "/herald" | URL prefix for all Herald API routes. |
DisableRoutes | bool | false | When true, the extension does not register routes automatically. |
DisableMigrate | bool | false | When true, the extension skips automatic schema migration. |
GroveDatabase | string | "" | Name of the Grove database in the DI container. Empty uses the default unnamed DB. |
Extension options
ext := extension.New(
extension.WithGroveDatabase("primary"),
extension.WithBasePath("/notifications"),
extension.WithDisableRoutes(),
extension.WithDisableMigrate(),
extension.WithDriver(&sms.TwilioDriver{}),
)| ExtOption | Description |
|---|---|
WithStore(s store.Store) | Sets the persistence backend directly (bypasses Grove auto-detection). |
WithBasePath(path string) | Sets the URL prefix for Herald API routes. |
WithConfig(cfg Config) | Sets the full extension configuration. |
WithHeraldOption(opt herald.Option) | Passes a raw herald.Option through to the core engine. |
WithDriver(d driver.Driver) | Registers a notification driver. |
WithDisableRoutes() | Disables automatic route registration. |
WithDisableMigrate() | Disables automatic schema migration. |
WithRequireConfig(bool) | Requires config to be present in YAML files (errors if missing). |
WithGroveDatabase(name string) | Sets the Grove database name for auto-detection of the store backend. |
YAML configuration
The extension loads configuration from Forge YAML config files. It checks two keys in order:
extensions.herald(namespaced, preferred)herald(legacy)
# config.yaml
extensions:
herald:
default_locale: "en"
max_batch_size: 100
truncate_body_at: 4096
base_path: "/herald"
disable_routes: false
disable_migrate: false
grove_database: "primary"YAML configuration takes precedence over programmatic defaults. Programmatic boolean flags (DisableRoutes, DisableMigrate) override YAML when set to true.
Grove auto-detection
When GroveDatabase is set (via YAML or WithGroveDatabase), the extension resolves a *grove.DB from the Forge DI container and auto-constructs the appropriate store backend based on the Grove driver type:
| Grove driver | Store backend |
|---|---|
pg | store/postgres.Store |
sqlite | store/sqlite.Store |
mongo | store/mongo.Store |
// The extension does this internally:
switch db.Driver().Name() {
case "pg":
return pgstore.New(db), nil
case "sqlite":
return sqlitestore.New(db), nil
case "mongo":
return mongostore.New(db), nil
}Built-in drivers
The Forge extension automatically registers all built-in drivers during Init:
email.SMTPDriver-- SMTP email deliveryemail.ResendDriver-- Resend API email deliverysms.TwilioDriver-- Twilio SMS deliverypush.FCMDriver-- Firebase Cloud Messaging push notificationsinapp.Driver-- In-app inbox notifications
You only need to call WithDriver for custom or third-party drivers.
Accessing the Herald instance
After extension registration, the *herald.Herald instance is available from the Forge DI container:
import "github.com/xraph/vessel"
h, err := vessel.Inject[*herald.Herald](app.Container())Or directly from the extension:
ext.Herald() // *herald.Herald (nil until Register is called)
ext.API() // *api.ForgeAPI
ext.Config() // herald.Config