Documentation
¶
Overview ¶
Package app is wowapi's composition root helpers. Product binaries (cmd/api, cmd/worker, cmd/migrate in the product repo) construct an App, register their modules, and run it.
Phase 0 ships registration + whole-graph validation (duplicate names, invalid names, unknown dependencies, dependency cycles) and the deterministic registration order. Kernel construction and the RunAPI/RunWorker/RunMigrate helpers land in Phases 1–2.
Module registration context (D-0006/D-0040; blueprint 06 §2).
The context is capability-scoped: modules receive registries and services, never raw pools or global config. Accessors grow per phase alongside the kernel capability each delivers. Phase 5 wires the full set the current kernel supports (routes, permissions, resource types, authz, tx, migrations, seeds, openapi, health, inter-module ports) and injects the shared registries modules register into during boot.
Startup/shutdown lifecycle skeleton (phase-plan Phase 1).
RunHooks provides orderly start-then-stop sequencing for named components (HTTP server, outbox relay, …) wired in as Hook values. Signal wiring (SIGINT/SIGTERM) belongs to the process main via signal.NotifyContext; RunHooks stays testable with a plain context.
Real server construction arrives in Phases 2–3; this skeleton establishes the lifecycle contract that those phases plug into.
Process config narrowing (blueprint 12 §7).
One loaded config.Framework, three narrowed views: each process binary receives only the sections it actually needs. Unused sections are never wired, so a worker process has no HTTP section and a migrate process has no module namespaces. Each view carries a Fingerprint method so ops can detect per-section drift across processes on shared config sections.
Index ¶
- func CatalogsSeeded(ctx context.Context, db database.DBTX, b seeds.Bundle) error
- func Readiness(b *Booted, fingerprint config.Fingerprint, extra map[string]httpx.HealthCheck) *httpx.Health
- func RunHooks(ctx context.Context, logger *slog.Logger, stopTimeout time.Duration, ...) error
- func StartWorker(ctx context.Context, b *Booted, opts WorkerConfigOpts) error
- type APIConfig
- type App
- type BootOption
- type Booted
- type Hook
- type MigrateConfig
- type MigrateDB
- type RecurringJob
- type RuntimeDB
- type WorkerConfig
- type WorkerConfigOpts
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CatalogsSeeded ¶ added in v1.1.0
CatalogsSeeded is the GAP-003 "clear failure mode" check: it turns an empty authorization/resource catalog into one loud, actionable readiness failure instead of the scattered per-request 403s and resource-mirror FK violations that would otherwise be the only symptom (docs/upstream PF-9 in the product repo this was upstreamed from).
It compares what the booted module seeds DECLARE (b, in-memory — every module's Register already ran) against what the DATABASE actually holds. A mismatch — seeds declared but the catalog table is empty — means the deploy's migrate step never ran seeds.Sync, which is exactly the gap this check exists to catch: wire it as a readiness check (see app.Readiness's `extra` parameter) on a platform-privileged connection, so an unseeded pod never reports ready and never takes traffic.
A product that declares no seeds at all (b is the zero Bundle) has nothing to sync, so an empty catalog is expected and not an error.
func Readiness ¶
func Readiness(b *Booted, fingerprint config.Fingerprint, extra map[string]httpx.HealthCheck) *httpx.Health
Readiness assembles the /readyz aggregator from the booted app: every module's registered readiness check (ctx.Health) plus the framework checks the caller supplies as `extra` (typically a DB ping and a "migrations current" probe, which the composition root wires because it owns the pool). The redacted config fingerprint is reported in the response for drift correlation. Mount h.Liveness() at /healthz and h.Readiness() at /readyz in the product's api and worker mains (blueprint 07 §9).
func RunHooks ¶
func RunHooks(ctx context.Context, logger *slog.Logger, stopTimeout time.Duration, hooks ...Hook) error
RunHooks starts hooks in order and blocks until ctx is cancelled or a Start returns an error. It then stops the successfully-started hooks in reverse order, each bounded by stopTimeout.
Errors are handled as follows:
- A Start error aborts the remaining starts and triggers immediate shutdown.
- Stop errors are all collected (never short-circuited) and joined with any Start error via errors.Join.
- If every Start and Stop succeeds, RunHooks returns nil.
func StartWorker ¶
func StartWorker(ctx context.Context, b *Booted, opts WorkerConfigOpts) error
StartWorker runs the background worker process for a booted app: the outbox relay (dispatches events to subscribed handlers) and the job runner (executes enqueued jobs with retries/DLQ). It blocks until ctx is cancelled, then drains in-flight work within ShutdownDrain and returns. Requires the kernel's Platform pool (cross-tenant kernel work) — a worker/migrate posture, not api-only.
Signal wiring (SIGINT/SIGTERM) belongs to the process main via signal.NotifyContext; StartWorker stays testable with a plain context.
Types ¶
type APIConfig ¶
type APIConfig struct {
Environment config.Env `json:"environment"`
HTTP config.HTTP `json:"http"`
DB RuntimeDB `json:"db"`
Log config.Log `json:"log"`
Modules config.Namespaces `json:"modules"`
}
APIConfig is the narrowed view handed to cmd/api: HTTP server settings, runtime database, logging, and module namespaces. It deliberately omits the migration DSN and provider credentials the API process does not use (blueprint 12 §7).
func NewAPIConfig ¶
NewAPIConfig constructs the api process view from a loaded Framework and the full module namespace map. The runtime DSN is required here — DSNs are validated at narrowing, not by config tags, so DB-less tooling loads stay possible (D-0021).
func (APIConfig) Fingerprint ¶
func (c APIConfig) Fingerprint() (config.Fingerprint, error)
Fingerprint returns the SHA-256 of this view's canonical redacted JSON rendering. Shared sections fingerprinted separately let ops detect api-vs-worker drift on a half-rolled deploy (blueprint 12 §7).
func (APIConfig) SectionFingerprints ¶
func (c APIConfig) SectionFingerprints() (map[string]config.Fingerprint, error)
SectionFingerprints returns one fingerprint per top-level section so processes sharing a section (api/worker both carry log + modules) can be compared for config drift (blueprint 12 §7); comparing whole-view fingerprints across differently-shaped views is meaningless.
type App ¶
type App struct {
// contains filtered or unexported fields
}
App holds the registered module set. Construct with New, add modules with Register, then call Validate before starting anything.
func (*App) Boot ¶
func (a *App) Boot(ctx context.Context, k *kernel.Kernel, namespaces config.Namespaces, opts ...BootOption) (*Booted, error)
Boot runs the module lifecycle up to (not including) Start: it registers every module against a capability-scoped context built from k — in dependency order so a module's ports are available to its dependents — then validates the whole graph and the shared registries (blueprint 06 §2). Boot fails, before anything serves, on: a module graph error (dup/unknown/cycle), a registration error, a route whose permission is not registered, a duplicate/invalid permission or resource type, or a seed ownership/parse error.
namespaces is the loaded product config's module.* subtree; each module sees only its own slice via Context.Config().
func (*App) Ordered ¶
Ordered returns modules in dependency order (dependencies first, ties broken alphabetically for determinism), validating the graph on the way.
type BootOption ¶
type BootOption func(*bootOpts)
BootOption tunes Boot. See SkipRLSEnforcementCheck.
func SkipRLSEnforcementCheck ¶
func SkipRLSEnforcementCheck() BootOption
SkipRLSEnforcementCheck disables the boot-time assertion that the runtime pool cannot bypass row-level security. Use it ONLY for a process that does not serve tenant traffic and runs as a privileged role by design — namely the migrate command, which boots the app merely to COLLECT module migration sets and connects with DDL (app_migrate/superuser) credentials. api/worker processes must NOT use it: their tenant-serving runtime pool must be a non-privileged app_rt role, and the default check keeps that safe-by-default (finding M3).
func WithI18nLayers ¶ added in v1.1.0
func WithI18nLayers(layers ...i18n.Layer) BootOption
WithI18nLayers supplies product-configured i18n source layers (framework overrides, product/module catalog files, compiled Go bundles) to merge into the catalog after modules register and before it is frozen for serving. The generated api, worker, AND migrate binaries pass the SAME layers (resolved from the product's i18n config), so all three load one catalog through one lifecycle (B1 acceptance). Layers are applied in precedence order on top of the framework's embedded defaults; ownership violations fail boot like any other registration error. Omit it (zero-config) and boot ships the framework English catalog exactly as before.
type Booted ¶
type Booted struct {
Kernel *kernel.Kernel
Router *httpx.Router
Events *outbox.HandlerRegistry // event subscriptions (drives the relay)
Jobs *jobs.Registry // job kinds (drives the worker pools)
OpenAPI map[string][]byte
Health map[string]func(context.Context) error
Migrations map[string]fs.FS
Seeds seeds.Bundle // merged catalog seeds, ready for SeedSync
Recurring []RecurringJob // module-registered recurring jobs (run by the worker scheduler)
// I18n is the merged message catalog (framework English + every module's
// localized bundles). Pass it to httpx.Locale so responses negotiate and
// localize (GAP-001). Never nil — at minimum it carries the framework's
// English catalog.
I18n *i18n.Catalog
}
Booted is the result of App.Boot: everything the process layer needs to start serving (or to migrate/seed). Modules have registered; the whole graph and registries are validated; nothing has started yet.
type Hook ¶
type Hook struct {
// Name is used in log messages; should be short and unique within a run.
Name string
// Start launches background work and must return promptly (not block for
// the component's lifetime). The ctx is the run context; components should
// respect its cancellation on their own internal paths.
Start func(ctx context.Context) error
// Stop performs a graceful shutdown. nil means nothing to stop. Stop
// receives a fresh context bounded by the stopTimeout, independent of
// the (already-cancelled) run context.
Stop func(ctx context.Context) error
}
Hook is one startable/stoppable component: an HTTP server, outbox relay, background sweeper, or any other process-lifetime service.
type MigrateConfig ¶
type MigrateConfig struct {
Environment config.Env `json:"environment"`
DB MigrateDB `json:"db_migrate"`
Log config.Log `json:"log"`
}
MigrateConfig is the narrowed view handed to cmd/migrate: the migration DSN and logging only. Module namespaces, the HTTP section, and the runtime DSN are deliberately absent (blueprint 12 §7).
func NewMigrateConfig ¶
func NewMigrateConfig(f config.Framework) (MigrateConfig, error)
NewMigrateConfig constructs the migrate process view from a loaded Framework. Requires the migration DSN (D-0021).
func (MigrateConfig) Fingerprint ¶
func (c MigrateConfig) Fingerprint() (config.Fingerprint, error)
Fingerprint returns the SHA-256 of this view's canonical redacted JSON rendering.
func (MigrateConfig) SectionFingerprints ¶
func (c MigrateConfig) SectionFingerprints() (map[string]config.Fingerprint, error)
SectionFingerprints returns one fingerprint per top-level section so processes sharing a section (api/worker both carry log + modules) can be compared for config drift (blueprint 12 §7); comparing whole-view fingerprints across differently-shaped views is meaningless.
type MigrateDB ¶
MigrateDB is the migrate slice of config.DB: the app_migrate DSN plus the pool knobs. The runtime DSN is deliberately absent (blueprint 12 §7).
type RecurringJob ¶
type RecurringJob struct {
Name string
Every time.Duration
Run func(ctx context.Context, db database.TenantDB) error
}
RecurringJob is a leader-safe per-tenant recurring job a module registered via module.Context.RecurringJob (roadmap E5/CA-5). StartWorker registers each on the scheduler; Run is invoked once per active tenant every Every, in that tenant's transaction.
type RuntimeDB ¶
type RuntimeDB struct {
DSN config.Secret `json:"dsn"`
config.Pool // flattens: max_conns, query_timeout, …
}
RuntimeDB is the runtime slice of config.DB handed to api/worker: the app_rt DSN plus the embedded pool knobs. The migration DSN is deliberately absent — runtime processes never hold app_migrate credentials (12 §7). Embedding config.Pool (not re-listing its fields) means new pool knobs reach every view without touching the narrowing code (ARCH-17).
type WorkerConfig ¶
type WorkerConfig struct {
Environment config.Env `json:"environment"`
DB RuntimeDB `json:"db"`
Log config.Log `json:"log"`
Modules config.Namespaces `json:"modules"`
}
WorkerConfig is the narrowed view handed to cmd/worker: runtime database, logging, and module namespaces. The HTTP server section is deliberately absent — a worker does not bind a port (blueprint 12 §7).
func NewWorkerConfig ¶
func NewWorkerConfig(f config.Framework, mods config.Namespaces) (WorkerConfig, error)
NewWorkerConfig constructs the worker process view from a loaded Framework and the full module namespace map. Requires the runtime DSN (D-0021).
func (WorkerConfig) Fingerprint ¶
func (c WorkerConfig) Fingerprint() (config.Fingerprint, error)
Fingerprint returns the SHA-256 of this view's canonical redacted JSON rendering.
func (WorkerConfig) SectionFingerprints ¶
func (c WorkerConfig) SectionFingerprints() (map[string]config.Fingerprint, error)
SectionFingerprints returns one fingerprint per top-level section so processes sharing a section (api/worker both carry log + modules) can be compared for config drift (blueprint 12 §7); comparing whole-view fingerprints across differently-shaped views is meaningless.
type WorkerConfigOpts ¶
type WorkerConfigOpts struct {
RelayBatch int
RelayPoll time.Duration
JobPoll time.Duration
JobPoolSize int
ShutdownDrain time.Duration
// Scheduler (leader-safe kernel maintenance sweeps). Zero values use defaults.
SchedulerPoll time.Duration // how often to check for due tasks (default 30s)
SLAInterval time.Duration // workflow SLA sweep interval (default 1m)
IdempotencyInterval time.Duration // idempotency-key expiry sweep interval (default 1h)
DLQDepthInterval time.Duration // dlq_depth gauge refresh interval (default 1m)
AuditAnchorInterval time.Duration // audit-chain anchor-export interval (default 1h)
NotifySendInterval time.Duration // notify send/retry poll interval (default 1m)
WebhookRetryInterval time.Duration // webhook retry + inbound poll interval (default 1m)
}
WorkerConfigOpts tunes the worker loops.