config

package
v0.0.0-...-d267593 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package config loads and validates Forge runtime configuration.

Index

Constants

View Source
const EnvMigrationDatabaseURL = "FORGE_MIGRATION_DATABASE_URL"

EnvMigrationDatabaseURL names the connection URL of the migration role: the role that owns the schema and applies migrations, separate from the less-privileged role FORGE_DATABASE_URL serves with.

Variables

This section is empty.

Functions

func CheckTrustedProxy

func CheckTrustedProxy(prefix netip.Prefix) error

CheckTrustedProxy reports why prefix is not a safe trusted proxy network, or nil. It is the single definition of the rules: ParseTrustedProxies, Config.Validate and web.NewTrustedProxies all apply it. The error text completes the sentence "entry N ...", names no value, and never echoes the prefix.

It refuses invalid prefixes, IPv4-mapped IPv6 prefixes (peers are compared in their IPv4 form, so they would never match), IPv4 prefixes shorter than /8 and IPv6 prefixes shorter than /16 (they would trust far more than a proxy network; this includes /0, which trusts every address). A bare address is a /32 or /128 and is unaffected.

func LoopbackHost

func LoopbackHost(host string) bool

LoopbackHost reports whether host is "localhost", a 127.0.0.0/8 address or ::1: a destination the traffic never leaves the machine for. Host names other than "localhost" are not resolved, so they are never loopback here.

func ParseMailbox

func ParseMailbox(value string) (name, email string, err error)

ParseMailbox parses "user@example.com" or "Display Name <user@example.com>" with net/mail and validates the result with ValidateMailbox.

func ParseTrustedProxies

func ParseTrustedProxies(list string) ([]netip.Prefix, error)

ParseTrustedProxies parses FORGE_HTTP_TRUSTED_PROXIES: a comma-separated list of CIDR prefixes or bare addresses (a bare address is a /32 or /128), such as "172.18.0.0/16, fd00:1::/64, 10.0.0.7". A blank list trusts nobody. Empty entries, malformed entries, overly broad prefixes (IPv4 shorter than /8, IPv6 shorter than /16, which includes /0) and IPv4-mapped IPv6 prefixes (peers are compared in their IPv4 form, so they would never match) are errors naming the entry's position, never its text (see CheckTrustedProxy). Prefixes are returned masked.

func ValidMailDomain

func ValidMailDomain(domain string) bool

ValidMailDomain reports whether domain is an LDH host name of up to 253 bytes. A single label such as "localhost" is allowed for development.

func ValidateMailbox

func ValidateMailbox(name, email string) error

ValidateMailbox checks a display name and an addr-spec.

The address must be ASCII dot-atom `local@domain` with an LDH host name; quoted local parts, comments, route addresses, domain literals and display forms are rejected, which removes the ambiguity used to smuggle recipients or SMTP commands. The display name may be any text without control, line-break or bidirectional-override characters.

Types

type Adapter

type Adapter string

Adapter identifies the database adapter selected by FORGE_DATABASE_URL's scheme.

const (
	AdapterPostgres Adapter = "postgres"
	// AdapterMySQL serves both MySQL and MariaDB.
	AdapterMySQL  Adapter = "mysql"
	AdapterSQLite Adapter = "sqlite"
)

The database adapters.

type Auth

type Auth struct {
	// MFAKey is the canonical unpadded base64url encoding of 32 random bytes
	// used by the built-in AES-256-GCM TOTP secret cipher.
	MFAKey Secret
	// PasswordResetURL is the absolute URL of the page password-reset emails
	// link to, such as https://app.example.com/passwords/edit
	// (FORGE_AUTH_PASSWORD_RESET_URL). It must be https, or http on a
	// loopback host for development. Empty disables reset emails.
	PasswordResetURL string
	// SessionIdleTimeout ends a session nobody used for this long, before
	// its absolute expiry (FORGE_AUTH_SESSION_IDLE_TIMEOUT, default 2h,
	// between 5m and 720h). auth/postgres.WithSessionIdleTimeout applies it.
	SessionIdleTimeout time.Duration
}

Auth contains optional secrets for identity features. Zero values keep the corresponding feature disabled rather than inventing insecure defaults.

type Cache

type Cache struct {
	// MaxEntries bounds the entries of a cache (FORGE_CACHE_MAX_ENTRIES,
	// default 10000).
	MaxEntries int
	// MaxBytes bounds the memory of the in-memory cache
	// (FORGE_CACHE_MAX_BYTES, default 64 MiB).
	MaxBytes int64
	// MaxValueBytes bounds one cached value (FORGE_CACHE_MAX_VALUE_BYTES,
	// default 1 MiB, at most 8 MiB).
	MaxValueBytes int
	// DefaultTTL applies when a write names no time to live
	// (FORGE_CACHE_DEFAULT_TTL, default 1h).
	DefaultTTL time.Duration
}

Cache configures the application cache (ADR 0032). Zero values select the defaults.

type Config

type Config struct {
	Environment Environment
	HTTP        HTTP
	Auth        Auth
	Database    Database
	Mail        Mail
	RateLimit   RateLimit
	Cache       Cache
	Storage     Storage
	Telemetry   Telemetry
	Log         Log
	I18n        I18n
	OpenAPI     OpenAPI
	Jobs        Jobs
}

Config is the application's configuration. Default returns safe defaults; Load reads FORGE_* variables over them.

func Default

func Default() Config

Default returns the defaults: the development environment, plain HTTP on 127.0.0.1:8080, conservative timeouts and size limits, and no database.

func Load

func Load() (Config, error)

Load reads the configuration from the process environment, over Default, and validates it.

func LoadWithLookup

func LoadWithLookup(lookup func(string) (string, bool)) (Config, error)

LoadWithLookup is Load with lookup in place of the process environment. It returns an error for an unparsable or invalid setting.

func (Config) EventLevel

func (c Config) EventLevel() slog.Level

EventLevel returns the level of the request, SQL and job lines, which follow a request or a job step by step: Info in development, where they are the development log, and Debug elsewhere, where FORGE_LOG_LEVEL=debug turns them on.

func (Config) MigrationDatabase

func (c Config) MigrationDatabase(lookup func(string) (string, bool)) (Database, error)

MigrationDatabase returns the connection the migrate and rollback commands use. When FORGE_MIGRATION_DATABASE_URL is set (read through lookup), it supplies the role: its user, password, host, port and parameters, validated like FORGE_DATABASE_URL (production TLS rules included) and of the same adapter. Its database is ignored: migrations always change the database FORGE_DATABASE_URL names, the one the application serves. Without it, or for SQLite, which has no roles, the result is c.Database unchanged.

func (Config) ServesOpenAPI

func (c Config) ServesOpenAPI() bool

ServesOpenAPI reports whether the application serves its OpenAPI document at GET /openapi.json.

func (Config) ShowsMissingTranslations

func (c Config) ShowsMissingTranslations() bool

ShowsMissingTranslations reports whether missing translations are shown as "translation missing: LOCALE.KEY" (development and test) instead of falling back to the default locale (staging and production).

func (Config) Validate

func (c Config) Validate() error

Validate reports the first invalid or unsafe setting, such as an unknown environment, plain HTTP in production or a missing TLS certificate.

type Database

type Database struct {
	// URL is a postgres://, mysql:// or sqlite: URL. It usually embeds a
	// password.
	URL             Secret
	MaxConns        int32
	MinConns        int32
	MaxConnLifetime time.Duration
	MaxConnIdleTime time.Duration
	ConnectTimeout  time.Duration
	// StatementTimeout bounds statements server-side: every statement on
	// PostgreSQL and MariaDB, SELECT only on MySQL. SQLite has no server; its
	// queries stop through context cancellation.
	StatementTimeout time.Duration
	// AllowPlaintext permits production connections without TLS, for
	// databases reachable only over a trusted private network.
	AllowPlaintext bool
	// Environment is the environment the pool serves in. Outside
	// development and test, postgres.Open refuses a role that could bypass
	// row-level security unless postgres.AllowRowSecurityBypass is passed.
	// Load and forge.New copy Config.Environment into it. Default leaves it
	// empty, like a Database built in code: the empty value is neither
	// development nor test, so such a pool is checked (fail closed) until
	// the environment is set explicitly.
	Environment Environment
}

Database configures the database connection pool. The URL's scheme selects the adapter (see Adapter). An empty URL means the application runs without a database.

func (Database) Adapter

func (d Database) Adapter() Adapter

Adapter reports the adapter the URL selects, or "" when no database is configured. The URL has been validated, so any other value cannot occur.

func (Database) SQLitePath

func (d Database) SQLitePath() string

SQLitePath returns the database file of a sqlite: URL, or "".

type Environment

type Environment string

Environment is the deployment environment selected by FORGE_ENV.

const (
	Development Environment = "development"
	Test        Environment = "test"
	Staging     Environment = "staging"
	Production  Environment = "production"
)

The supported environments. Production and staging are stricter about transport security and fallbacks.

type HTTP

type HTTP struct {
	Address           string
	Transport         TransportMode
	TLSCertFile       string
	TLSKeyFile        string
	ReadHeaderTimeout time.Duration
	ReadTimeout       time.Duration
	WriteTimeout      time.Duration
	IdleTimeout       time.Duration
	ShutdownTimeout   time.Duration
	MaxHeaderBytes    int
	MaxBodyBytes      int64
	// TrustedProxies are the networks of the reverse proxies whose
	// X-Forwarded-For header is believed when resolving the client address
	// (FORGE_HTTP_TRUSTED_PROXIES, ADR 0037). Empty, the default, trusts
	// nobody: the client address is the TCP peer.
	TrustedProxies []netip.Prefix
}

HTTP configures the HTTP server: address, transport, timeouts and size limits.

type I18n

type I18n struct {
	// DefaultLocale is the locale requests fall back to
	// (FORGE_I18N_DEFAULT_LOCALE), such as "en" or "pt-BR". Empty lets the
	// application choose; generated applications use the locale given to
	// `forge new --locale`, "en" by default.
	DefaultLocale string
}

I18n configures translations (package i18n, ADR 0042).

type Jobs

type Jobs struct {
	// Notify is FORGE_JOBS_NOTIFY: enqueues announce due jobs and job workers
	// LISTEN for them, the meaning of queuepostgres.WithNotify(). Nil (unset)
	// keeps the code's choice; false turns an announcing store off per
	// deployment. Only PostgreSQL has a wake-up.
	Notify *bool
}

Jobs configures durable jobs (package queue, ADR 0047).

func (Jobs) Notifies

func (j Jobs) Notifies(code bool) bool

Notifies reports whether job enqueues announce and workers listen: the environment's FORGE_JOBS_NOTIFY when it is set, otherwise code, the choice the application made in code (for example passing queuepostgres.WithNotify()).

type Log

type Log struct {
	// Format is FORGE_LOG_FORMAT: auto (default), pretty, text or json.
	Format LogFormat
	// Level is FORGE_LOG_LEVEL, the lowest level written: debug, info
	// (default), warn or error.
	Level string
	// SlowQuery is FORGE_LOG_SLOW_QUERY (default 100ms): a SQL line of a
	// statement slower than this is marked slow, and yellow in the pretty
	// format.
	SlowQuery time.Duration
	// RedactKeys is FORGE_LOG_REDACT_KEYS, a comma-separated list of key
	// fragments added to the default ones (password, secret, token, email...):
	// the value of every log attribute whose key contains one of them,
	// whatever its case, is written as [REDACTED].
	RedactKeys []string
}

Log configures the application log: its format, the slowest query that is not flagged and the attribute keys that are never written.

func (Log) LevelValue

func (l Log) LevelValue() (slog.Level, bool)

LevelValue returns the slog level Level names, and false when it names none.

type LogFormat

type LogFormat string

LogFormat is how the application writes its logs (FORGE_LOG_FORMAT).

const (
	LogFormatAuto   LogFormat = "auto"
	LogFormatPretty LogFormat = "pretty"
	LogFormatText   LogFormat = "text"
	LogFormatJSON   LogFormat = "json"
)

The supported log formats. Auto picks pretty on a terminal in development, text elsewhere in development and test, and JSON in staging and production.

type Mail

type Mail struct {
	// URL selects and configures the transport and usually embeds a
	// password:
	//
	//	smtp://user:password@smtp.example.com:587   STARTTLS (default port 587)
	//	smtps://user:password@smtp.example.com:465  implicit TLS (default 465)
	//	file:///var/tmp/forge-mail                  development .eml files
	//
	// Percent-encode "@", ":" and "/" inside the user name and password.
	URL Secret
	// From is the default sender, "name@example.com" or
	// "Name <name@example.com>". It is required when URL is set.
	From string
	// AllowPlaintext lets the SMTP sender proceed when the server does not
	// offer STARTTLS, including authentication over that connection. Use only
	// on a trusted private network or against a local development server.
	AllowPlaintext bool
	// Timeout bounds one delivery attempt. Zero means 30 seconds.
	Timeout time.Duration
	// Preview enables the development mail preview handler
	// (FORGE_MAIL_PREVIEW=true). It must be set deliberately: an unset
	// FORGE_ENV means development, so the environment alone is not a safe
	// gate. Only valid in development and test.
	Preview bool
	// CAFile is a PEM bundle of extra trusted roots for a private mail relay.
	// There is no setting to skip certificate verification.
	CAFile string
}

Mail configures outgoing email. An empty URL means the application sends no mail (features that need it, such as password recovery, fail closed).

func (Mail) EffectiveTimeout

func (m Mail) EffectiveTimeout() time.Duration

EffectiveTimeout returns Timeout, or the 30 second default when unset.

func (Mail) FilePath

func (m Mail) FilePath() string

FilePath returns the directory of a file:// URL, or "".

func (Mail) SMTP

func (m Mail) SMTP() (MailEndpoint, bool)

SMTP returns the server of an smtp:// or smtps:// URL.

func (Mail) Transport

func (m Mail) Transport() MailTransport

Transport reports the transport the URL selects, or MailDisabled.

type MailEndpoint

type MailEndpoint struct {
	Host     string
	Port     int
	Username string
	Password Secret
	// ImplicitTLS is true for smtps://.
	ImplicitTLS bool
}

MailEndpoint is the SMTP server described by Mail.URL.

type MailTransport

type MailTransport string

MailTransport identifies how FORGE_MAIL_URL's scheme delivers mail.

const (
	// MailDisabled means no mail URL is configured.
	MailDisabled MailTransport = ""
	// MailSTARTTLS is smtp://: connect, then upgrade with STARTTLS (port 587).
	MailSTARTTLS MailTransport = "smtp"
	// MailImplicitTLS is smtps://: TLS from the first byte (port 465).
	MailImplicitTLS MailTransport = "smtps"
	// MailFile is file://: the development sender writing .eml files. It is
	// refused in production.
	MailFile MailTransport = "file"
)

type MailboxError

type MailboxError struct {
	Reason string
	// Unsupported marks input that is valid in the wild but outside what
	// Forge implements: addresses with non-ASCII characters (SMTPUTF8 and
	// IDNA are not supported). Callers can report it distinctly.
	Unsupported bool
}

MailboxError explains why a mailbox cannot be used. Reason is a static sentence; it never contains the offending value.

func (*MailboxError) Error

func (e *MailboxError) Error() string

type OpenAPI

type OpenAPI struct {
	// Enabled is FORGE_OPENAPI_ENABLED. Nil (unset) serves the document in
	// development and test only: it lists every documented route,
	// including internal ones, so staging and production publish it only
	// when it is set to true.
	Enabled *bool
}

OpenAPI configures the GET /openapi.json route (package openapi, ADR 0043).

type RateLimit

type RateLimit struct {
	// MaxKeys bounds the buckets the in-memory limiter holds
	// (FORGE_RATE_LIMIT_MAX_KEYS, default 100000).
	MaxKeys int
}

RateLimit configures the in-memory rate limiter (ADR 0031). Zero values select the defaults, so a Config built without Default still validates.

func (RateLimit) EffectiveMaxKeys

func (r RateLimit) EffectiveMaxKeys() int

EffectiveMaxKeys returns MaxKeys, or the default when unset.

type Secret

type Secret struct {
	// contains filtered or unexported fields
}

Secret holds a sensitive value such as a database URL. Every formatting, logging and serialization path prints [REDACTED]; only Reveal returns the value, which makes each use explicit and easy to audit.

The value sits behind a pointer on purpose: fmt cannot call String or GoString on an unexported struct field and would print the fields it finds, so a Secret held in an unexported field (`%+v` of a struct) would leak its text. Behind a pointer it prints an address. Consequently two Secrets must be compared with Equal, not ==, which compares identity.

func NewSecret

func NewSecret(value string) Secret

NewSecret wraps value. The empty string yields the zero Secret.

func (Secret) Equal

func (s Secret) Equal(other Secret) bool

Equal reports whether both secrets hold the same value, in constant time for values of equal length.

func (Secret) GoString

func (Secret) GoString() string

GoString prints [REDACTED] in %#v output.

func (Secret) IsZero

func (s Secret) IsZero() bool

IsZero reports whether the Secret holds no value.

func (Secret) LogValue

func (Secret) LogValue() slog.Value

LogValue logs [REDACTED].

func (Secret) MarshalText

func (Secret) MarshalText() ([]byte, error)

MarshalText encodes [REDACTED], so JSON and other text encodings never carry the value.

func (Secret) Reveal

func (s Secret) Reveal() string

Reveal returns the underlying value. Pass it straight to the component that needs it; never log or return it to clients.

func (Secret) String

func (Secret) String() string

type Storage

type Storage struct {
	// Root is the directory the disk adapter stores files in
	// (FORGE_STORAGE_ROOT). In production it must be an absolute path on a
	// persistent volume.
	Root string
	// SigningKey is the canonical unpadded base64url encoding of at least 32
	// random bytes (FORGE_STORAGE_SIGNING_KEY), the HMAC key of signed links.
	// Production requires it whenever Root is set. Generate one with
	// "openssl rand -base64 32 | tr '+/' '-_' | tr -d '='".
	SigningKey Secret
	// MaxUploadBytes bounds one uploaded file (FORGE_STORAGE_MAX_UPLOAD_BYTES,
	// default 512 KiB). When Root is set it cannot exceed
	// FORGE_HTTP_MAX_BODY_BYTES, which caps every request body.
	MaxUploadBytes int64
	// LinkTTL is the lifetime of signed links (FORGE_STORAGE_LINK_TTL, default
	// 15m, at most 7 days).
	LinkTTL time.Duration
}

Storage configures file storage (ADR 0033). An empty Root means the application does not use disk storage.

func (Storage) EffectiveLinkTTL

func (s Storage) EffectiveLinkTTL() time.Duration

EffectiveLinkTTL returns LinkTTL, or the default when unset.

func (Storage) EffectiveMaxUploadBytes

func (s Storage) EffectiveMaxUploadBytes() int64

EffectiveMaxUploadBytes returns MaxUploadBytes, or the default when unset.

func (Storage) SigningKeyBytes

func (s Storage) SigningKeyBytes() ([]byte, error)

SigningKeyBytes decodes SigningKey. It returns nil, nil when no key is configured and an error (that never contains the value) when it is not canonical unpadded base64url of 32 to 1024 bytes.

type Telemetry

type Telemetry struct {
	// Enabled turns instrumentation on (FORGE_TELEMETRY_ENABLED, default
	// true). When false no telemetry middleware or instrument exists and
	// the cost is zero.
	Enabled bool
	// ServiceName is the service.name resource attribute. Empty means
	// "unknown_service:" followed by the executable name.
	ServiceName    string
	ServiceVersion string
	// Exporter is none (default), otlp or log.
	Exporter TelemetryExporter
	// LogsExporter is none (default) or otlp
	// (FORGE_TELEMETRY_LOGS_EXPORTER). It is independent of Exporter: logs
	// may go to the collector while spans and metrics do not, and the other
	// way round. otlp needs OTLPEndpoint.
	LogsExporter TelemetryLogsExporter
	// LogsLevel is the lowest slog level sent to the log exporter:
	// debug, info (default), warn or error (FORGE_TELEMETRY_LOGS_LEVEL).
	// Standard output keeps the application logger's own level.
	LogsLevel string
	// OTLPEndpoint is the collector's base URL, such as
	// http://otel-collector:4318. Required for the otlp exporters. Production
	// requires https unless OTLPAllowPlaintext is set.
	OTLPEndpoint string
	// OTLPHeaders are "name=value" pairs separated by commas, values
	// percent-encoded, sent with every export: typically an API key.
	OTLPHeaders        Secret
	OTLPTimeout        time.Duration
	OTLPCompression    string
	OTLPAllowPlaintext bool
	// SampleRatio is the fraction of new traces that are sampled, 0 to 1
	// (default 1). Child spans follow their parent's decision.
	SampleRatio float64
	// TrustTraceparent continues traces from incoming traceparent and
	// tracestate headers (FORGE_TELEMETRY_TRUST_TRACEPARENT, default false).
	// Untrusted, every request starts a new trace, so clients can neither
	// choose trace IDs, force sampling past SampleRatio, nor push tracestate
	// to downstream services. Enable it only behind a proxy or gateway that
	// sets or sanitizes these headers.
	TrustTraceparent bool
	// MetricsAddress is the host:port of the separate Prometheus listener,
	// serving GET /metrics. Empty (default) means no listener. It has no
	// authentication: a non-loopback address must be confirmed with
	// MetricsPublic, or production refuses to start and other environments
	// log a warning.
	MetricsAddress string
	// MetricsPublic (FORGE_TELEMETRY_METRICS_PUBLIC) confirms that a
	// non-loopback MetricsAddress is intended, for example 0.0.0.0:9464 in a
	// container whose port only the cluster network can reach.
	MetricsPublic bool
	// MetricsInterval is how often metrics are pushed with the otlp
	// exporter (default 1 minute).
	MetricsInterval time.Duration
}

Telemetry configures tracing, metrics and log correlation (package telemetry, docs/OBSERVABILITY.md).

func (Telemetry) LogLevel

func (t Telemetry) LogLevel() (slog.Level, bool)

LogLevel returns the slog level LogsLevel names, and false when it names none.

func (Telemetry) MetricsLoopback

func (t Telemetry) MetricsLoopback() bool

MetricsLoopback reports whether MetricsAddress binds only to loopback (localhost, 127.0.0.0/8 or ::1). An empty host, 0.0.0.0 and any other address are reachable from the network.

func (Telemetry) OTLPHeaderMap

func (t Telemetry) OTLPHeaderMap() (map[string]string, error)

OTLPHeaderMap parses OTLPHeaders: "name1=value1,name2=value2" with percent-encoded values, the OTEL_EXPORTER_OTLP_HEADERS format. Errors name the position of the bad pair, never a value.

type TelemetryExporter

type TelemetryExporter string

TelemetryExporter selects where sampled spans and pushed metrics go.

const (
	// TelemetryExporterNone exports nothing: traces still correlate logs and
	// propagate to downstream services, and metrics are served by the
	// metrics listener when FORGE_TELEMETRY_METRICS_ADDR is set.
	TelemetryExporterNone TelemetryExporter = "none"
	// TelemetryExporterOTLP sends spans and metrics to an OpenTelemetry
	// Collector over OTLP/HTTP with JSON encoding.
	TelemetryExporterOTLP TelemetryExporter = "otlp"
	// TelemetryExporterLog writes spans to the application log. It is for
	// development and refused in production.
	TelemetryExporterLog TelemetryExporter = "log"
)

type TelemetryLogsExporter

type TelemetryLogsExporter string

TelemetryLogsExporter selects where application log records go in addition to standard output.

const (
	// TelemetryLogsExporterNone (the default) leaves logs on standard output.
	TelemetryLogsExporterNone TelemetryLogsExporter = "none"
	// TelemetryLogsExporterOTLP also sends every log record, with the trace
	// and span IDs of its context, to the OpenTelemetry Collector's
	// /v1/logs over OTLP/HTTP with JSON encoding, using the same
	// FORGE_TELEMETRY_OTLP_* settings as spans and metrics.
	TelemetryLogsExporterOTLP TelemetryLogsExporter = "otlp"
)

type TransportMode

type TransportMode string

TransportMode is how the server terminates HTTP, selected by FORGE_HTTP_TRANSPORT.

const (
	TransportPlain        TransportMode = "plain"
	TransportTLS          TransportMode = "tls"
	TransportTrustedProxy TransportMode = "trusted-proxy"
)

The supported transports: plain HTTP, TLS terminated by the server, and plain HTTP behind a trusted TLS-terminating proxy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL