Skip to content

Bootloader

The wippy/bootloader module orchestrates application initialization by discovering and running bootloader functions in a defined order at startup. Other framework modules (migrations, encryption, index refresh) register bootloaders to run their own initialization steps.

Add the module to your project:

Terminal window
wippy add wippy/bootloader
wippy install

Declare the dependency and the required application host:

version: "1.0"
namespace: app
entries:
- name: processes
kind: process.host
lifecycle:
auto_start: true
- name: os_env
kind: env.storage.os
- name: dep.bootloader
kind: ns.dependency
component: wippy/bootloader
version: "*"
parameters:
- name: application_host
value: app:processes
- name: env_storage
value: app:os_env

The bootloader itself runs as wippy.bootloader:bootloader.service (a process.service with auto_start: true). Nothing else is required to activate it.

At startup the bootloader:

  1. Discovers every entry with meta.type: bootloader from the registry.
  2. Sorts them by meta.order ascending (lowest first).
  3. Executes each one sequentially as a Lua function.
  4. Stops on the first error that returns status = "error".
  5. Reports total / success / failed / skipped counts when finished.

Bootloaders are autonomous — each one checks its own conditions, does its work, and reports a structured result.

A bootloader is any function.lua entry with meta.type: bootloader:

- name: seed_defaults
kind: function.lua
meta:
type: bootloader
order: 50
description: Seed default rows for a new install
source: file://seed_defaults.lua
method: run
modules:
- logger
imports:
sql: :sql
FieldRequiredDescription
meta.typeYesMust be bootloader
meta.orderNoExecution order (default 999); lower runs first
meta.descriptionNoHuman-readable summary
meta.requiresNoBootloader IDs and/or service IDs that must complete/be available first; the runtime waits for services and fails the bootloader (stopping the boot sequence) if a requirement is unmet

The method returns a table describing the outcome:

local function run()
local ok, err = apply_seed()
if err then
return {
status = "error",
message = "seed failed: " .. tostring(err)
}
end
if not ok then
return {
status = "skipped",
message = "already seeded"
}
end
return {
status = "success",
message = "seeded default rows"
}
end
return { run = run }
StatusMeaning
successWork completed
skippedNo-op (already done, precondition unmet)
errorFailure — stops the boot sequence

A bootloader that raises a Lua error is treated as error.

Lower order values run first. Reserve low orders for infrastructure:

OrderTypical Use
10Secrets and encryption keys (provided by the module)
20Schema migrations (provided by wippy/migration)
50Data seeding, search index warmup
100Application-level tasks (convention)

When two bootloaders share an order, they run in alphabetical order by their fully-qualified entry ID.

Generates a 256-bit ENCRYPTION_KEY and stores it through the configured env_storage if no value is present. Other modules (security, usage tracking) read this variable for envelope encryption. Skipped when the variable already exists.

Provided by wippy/migration. Discovers every entry with meta.type: migration, groups them by meta.target_db, and applies the pending ones. See Migrations.

The service logs one line per bootloader (SUCCESS, FAILED, SKIPPED) with the entry ID, order, and duration. The final summary line reports aggregate counts. A failed bootloader aborts startup — the supervisor’s restart policy then applies to bootloader.service.

Keep bootloaders idempotent. They may run again after a crash restart, so check preconditions (row exists, file present, env var set) before doing work.