Lifecycle¶
Vanguard separates registration, initialization, startup scheduling, and readiness. Choosing the correct phase prevents race conditions between services and controllers.
Lifecycle Hooks¶
Services and controllers may define:
function Object:VanguardInit()
-- Required setup that must complete before start hooks are scheduled.
end
function Object:VanguardStart()
-- Runtime work scheduled after every init hook completes.
end
For Knit migration, KnitInit and KnitStart are recognized when the corresponding Vanguard hook is absent.
Registration Phase¶
Before Start:
CreatePluginregisters extension definitions.CreateServiceorCreateControllerregisters explicit definitions.AddPlugins,AddServices,AddControllers, and their deep variants require modules and register plain returned definitions.- classes and components may be registered from their configured folders.
- duplicate names fail registration.
Module loading is isolated. If one ModuleScript throws or does not return exactly one value, Vanguard logs the module path and continues loading other modules.
Registration does not call lifecycle hooks.
Bootstrap loads folders in this order when they are present:
Plugins load first so extension hooks can observe later class, service, controller, component, and remote registration.
Plugin Lifecycle¶
Plugins can define VanguardInit / Init and VanguardStart / Start.
Plugin lifecycle hooks run in dependency order, then priority order.
Startup timing:
plugin init hooks
-> service/controller init hooks
-> plugin start hooks
-> service/controller start hooks
DependsOn entries always run before the dependent plugin. Missing plugin
dependencies and dependency cycles fail with VG-PLUGIN-003.
By default, hook errors are isolated and logged with VG-PLUGIN-002. Set
Plugins.Strict = true in start options, or Strict = true on one plugin, to
make hook failures reject startup.
Server Initialization¶
Services support numeric Priority values. Higher values initialize first.
local DatabaseService = Vanguard.CreateService({
Name = "DatabaseService",
Priority = 100,
Client = {},
})
Server initialization follows these rules:
- services are sorted by descending priority;
- ties are sorted alphabetically by
Name; - every init hook in one priority group runs concurrently;
- Vanguard waits for the entire group;
- the next lower priority group begins.
Example:
| Service | Priority | Init phase |
|---|---|---|
DatabaseService |
100 | 1 |
ProfileService |
50 | 2 |
InventoryService |
50 | 2 |
AnalyticsService |
0 | 3 |
ProfileService and InventoryService initialize concurrently. If Inventory truly requires Profile to finish first, assign them different priorities or explicitly await a shared promise.
Client Initialization¶
Controllers are sorted alphabetically and all controller init hooks run concurrently. Controllers do not currently have a priority field.
Use explicit promises or shared state when one controller must wait for another. Avoid relying on alphabetical launch order as a completion guarantee.
Init Hook Errors¶
Init hooks run inside Vanguard promises. A thrown error rejects startup and prevents the normal startup-complete sequence.
Use init for:
- loading required configuration;
- connecting required internal dependencies;
- creating state needed by other systems;
- work that must finish before remotes become available.
Do not silently spawn required init work. Return only after required setup is complete.
Start Hooks¶
After all init hooks complete, Vanguard schedules start hooks with task.spawn.
Server start hooks are scheduled in service priority order, then alphabetical order for ties. Client start hooks are scheduled alphabetically.
Important: Vanguard does not await start hooks.
Long-running loops belong in VanguardStart because they do not block readiness. Required one-time setup belongs in VanguardInit.
An error thrown after a start hook is spawned does not reject the already-running startup chain. Handle runtime failures inside long-lived start tasks when recovery or reporting is required.
Components¶
After start hooks are scheduled, Vanguard starts registered components when StartComponents is enabled.
Each component then:
- attaches to currently-tagged matching Instances;
- subscribes to tag-added events;
- subscribes to tag-removed events.
Component instance Start callbacks are also scheduled asynchronously.
Readiness APIs¶
Start¶
The promise resolves after init hooks complete, start hooks are scheduled, components are started, and readiness is marked complete.
OnStart¶
OnStart resolves immediately when startup already completed. Otherwise it waits for the startup-complete event.
IsStarted¶
IsStarted reports completed startup, not merely that Start was called.
Dependency Guidance¶
Prefer this pattern:
function InventoryService:VanguardInit()
self.ProfileService = Vanguard.GetService("ProfileService")
self.Ready = false
-- Perform required initialization here.
self.Ready = true
end
All services are registered before any init hook runs, so dependency lookup is safe during init. Priority controls completion order, not whether a service exists in the registry.
Avoid using arbitrary waits such as task.wait(1) to coordinate systems. Use priorities, explicit promises, events, or well-defined readiness methods.
Startup Timeline¶
require modules
-> register definitions
-> run plugin BeforeStart hooks
-> run plugin init hooks
-> build server remotes and guards
-> run init hooks (awaited)
-> run plugin start hooks
-> schedule start hooks (not awaited)
-> start components
-> run plugin AfterStart hooks
-> mark ready
-> resolve Start and OnStart
Related guides: Services, Controllers, Components, Plugins, and Promise.