Documentation
¶
Overview ¶
Package bootstrap handles first-run detection and persistence of the runtime configuration that must exist before the database does.
When the caller's application starts with no database configured, its own setup wizard collects connection details and generated secrets, and SaveState persists them to a small JSON state file. Subsequent boots load that file and feed it into the normal env-based config.Load via ExportToEnv, so config.go needs no changes to its environment-first contract. The file holds secrets, so it is written 0600 under a 0700 directory.
StatePath and NeedsSetup are app-scoped: an App built via NewApp derives the environment variable names and default state-file path from the caller's own identity, so more than one application can share this package without reading or writing each other's environment or state file.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrEmptyAppName = errors.New("bootstrap: app name must not be empty")
ErrEmptyAppName is returned by NewApp when name is empty or all whitespace.
var ErrInvalidAppName = errors.New("bootstrap: app name must contain only letters, digits, underscores, and hyphens")
ErrInvalidAppName is returned by NewApp when name contains a character outside its allowed alphabet (see isValidAppName). name feeds directly into an environment variable name and a filesystem path (StatePath), so an unrestricted name would let a caller mint an unintended env var or — via "/" or ".." — escape defaultStateDir entirely.
Functions ¶
func ExportToEnv ¶
ExportToEnv sets the persisted configuration into the process environment for variables that are not already set — the real environment always wins, mirroring godotenv — so the unchanged env-based config.Load can consume it.
SESSION_SECRET and ENCRYPTION_KEY are independent secrets, applied per variable. DATABASE_URL, DB_PROVIDER, DB_POOL_MODE, and DB_SSL_ROOT_CERT describe a single connection profile and are applied as a unit: if the operator has set ANY of them in the environment, that configuration wins wholesale and none of the persisted database settings are applied. This prevents a hybrid config such as an operator DATABASE_URL paired with a persisted Supabase DB_PROVIDER/DB_POOL_MODE.
func GenerateSecret ¶
GenerateSecret returns a cryptographically random secretLen-byte value encoded as hex (64 characters). It backs the session secret and the at-rest encryption key when the operator has not supplied them via the environment.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the calling application's identity: it derives the app-scoped environment variable names and default state-file path bootstrap uses, so a package shared by more than one application never reads or writes another application's environment or state file.
func NewApp ¶
NewApp constructs an App identified by name (e.g. "nestova", "nestorage"). name is trimmed of leading/trailing whitespace; NewApp rejects an empty or all-whitespace result with ErrEmptyAppName, and a trimmed result containing anything outside letters, digits, underscores, and hyphens with ErrInvalidAppName (see that error's own doc for why).
func (App) ForceSetupEnv ¶
ForceSetupEnv is the environment variable that forces setup mode for a when truthy, even where the trigger would not otherwise fire: "<NAME>_FORCE_SETUP". It lets dev (which keeps a localhost default DSN) exercise the wizard on demand.
func (App) NeedsSetup ¶
NeedsSetup reports whether a's application should enter first-run setup mode rather than booting normally. Setup is needed only when nothing is configured: no persisted DSN (state) and no DATABASE_URL in the environment. To preserve the dev happy-path (config.Load's localhost default), dev is exempt unless a's ForceSetupEnv is set; the force flag also lets any environment exercise the wizard on demand. A configured-but-unreachable database therefore stays fail-fast and never drops a live server into reconfigure mode.
func (App) StateFileEnv ¶
StateFileEnv is the environment variable that overrides a's default state-file path: "<NAME>_STATE_FILE", NAME being a's name upper-cased.
type State ¶
type State struct {
DatabaseURL string `json:"database_url"`
SessionSecret string `json:"session_secret"`
EncryptionKey string `json:"encryption_key"`
// Provider selects the database backend (empty means the default postgres).
// Persisted so the post-restart boot sets DB_PROVIDER.
Provider string `json:"provider,omitempty"`
// PoolMode is the Supabase pooler mode (session|transaction); consulted only
// for the supabase provider. Maps to DB_POOL_MODE.
PoolMode string `json:"pool_mode,omitempty"`
// SSLRootCert is an optional CA-bundle path for verify-full TLS. Maps to
// DB_SSL_ROOT_CERT.
SSLRootCert string `json:"ssl_root_cert,omitempty"`
}
State is the persisted first-run configuration. Each field maps to an environment variable that config.Load consumes, applied via ExportToEnv.