Vault

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

FieldTypeDefaultDescription
DefaultLocalestring"en"BCP 47 locale tag used when a SendRequest does not specify a locale.
MaxBatchSizeint100Maximum recipients per batch send via Herald.Notify.
TruncateBodyAtint4096Maximum 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 = 4096

Option functions

Herald uses the functional options pattern. Option is a function type that configures a Herald instance:

type Option func(*Herald) error

Options 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

OptionDescription
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"`
}
FieldTypeDefaultDescription
BasePathstring"/herald"URL prefix for all Herald API routes.
DisableRoutesboolfalseWhen true, the extension does not register routes automatically.
DisableMigrateboolfalseWhen true, the extension skips automatic schema migration.
GroveDatabasestring""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{}),
)
ExtOptionDescription
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:

  1. extensions.herald (namespaced, preferred)
  2. 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 driverStore backend
pgstore/postgres.Store
sqlitestore/sqlite.Store
mongostore/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 delivery
  • email.ResendDriver -- Resend API email delivery
  • sms.TwilioDriver -- Twilio SMS delivery
  • push.FCMDriver -- Firebase Cloud Messaging push notifications
  • inapp.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

On this page