Skip to content

Plugin Developer API

Vanguard 0.1.15 introduces a public Plugin Developer API for framework extensions, project diagnostics, custom registries, metrics, and future Studio tooling. Plugins integrate through stable hooks instead of mutating Vanguard's private implementation tables.

Plugins are runtime-local. A server plugin and a client plugin with the same name are separate registrations unless the same ModuleScript is loaded on both runtimes.

Basic Plugin

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Vanguard = require(ReplicatedStorage.Packages.Vanguard)

return Vanguard.CreatePlugin({
    Name = "DiagnosticsPlugin",
    Version = "1.0.0",
    Runtime = "Shared",
    Priority = 10,

    VanguardInit = function(self, context)
        context.Logger:Info(`Loaded {self.Name} on {context.Runtime}`)
    end,

    Hooks = {
        ServiceRegistered = function(context, payload)
            context.Logger:Debug(`Service registered: {payload.Name}`)
        end,
    },
})

Load plugins before classes, services, controllers, and components:

Vanguard.Bootstrap({
    Plugins = script.Parent.Plugins,
    Services = script.Parent.Services,
    Classes = script.Parent.Classes,
    Options = {
        LogLevel = "info",
    },
})

On the client, use Controllers instead of Services.

Manifest Fields

Field Required Description
Name Yes Non-empty unique plugin name in the current runtime
Version No Human-readable plugin version for diagnostics
Runtime No Server, Client, Shared, or nil; nil behaves like Shared
Priority No Higher-priority plugins run lifecycle hooks first
DependsOn No Array of plugin names that must run before this plugin
Disabled No Skips the plugin without removing its registration
Strict No Makes this plugin's hook errors fail startup
Logger No Custom logger; otherwise the Vanguard root logger is used
Hooks No Table of named framework hook callbacks

Unknown ordinary fields are allowed so plugins can keep their own state and helper methods. Metadata keys beginning with __ are reserved by Vanguard.

Registration API

local plugin = Vanguard.CreatePlugin(definition)
Vanguard.RegisterPlugin(plugin)
Vanguard.AddPlugins(folder)
Vanguard.AddPluginsDeep(folder)
Vanguard.GetPlugin("DiagnosticsPlugin")
Vanguard.HasPlugin("DiagnosticsPlugin")
Vanguard.GetPlugins()
Vanguard.UnregisterPlugin("DiagnosticsPlugin")

LoadPlugins and LoadPluginsDeep are aliases for AddPlugins and AddPluginsDeep.

Plugins must be registered before Start. This keeps startup ordering and extension behavior deterministic. A plugin module may either call CreatePlugin or return a plain plugin definition; folder loaders register the returned table automatically.

Runtime Scope

Runtime = "Server" -- runs only on the server
Runtime = "Client" -- runs only on the client
Runtime = "Shared" -- runs on both runtimes when loaded on both

Runtime scope is evaluated by the current Vanguard runtime. It does not replicate a plugin to the other side. If a shared plugin should exist on both server and client, place it in a folder loaded by both bootstraps.

Dependency Ordering

DependsOn is an array of plugin names:

return Vanguard.CreatePlugin({
    Name = "MetricsUI",
    Runtime = "Client",
    DependsOn = { "MetricsCore" },
})

Vanguard topologically sorts plugins before lifecycle hooks and ordered framework hooks run. Dependencies run before the dependent plugin even when the dependent plugin has a higher priority.

Missing dependencies and dependency cycles fail with VG-PLUGIN-003 and link to the error reference.

Lifecycle Hooks

Plugins may define:

function Plugin:VanguardInit(context, payload)
    -- Required setup before framework services/controllers initialize.
end

function Plugin:VanguardStart(context, payload)
    -- Startup work after framework init hooks complete.
end

Init and Start are supported aliases. VanguardInit runs before service or controller init. VanguardStart runs after service or controller init and before framework start hooks are scheduled.

Plugin lifecycle hooks receive:

type PluginContext = {
    Vanguard: any,
    Runtime: "Server" | "Client",
    Plugin: PluginDefinition,
    Logger: Logger?,
    Hook: string?,
}

The startup payload includes:

{
    Options = Vanguard.GetConfig(),
    Runtime = "Server" or "Client",
}

Framework Hooks

Hooks can be declared in the Hooks table:

Hooks = {
    RemoteRegistered = function(context, payload)
        context.Logger:Info(payload.ServiceName, payload.RemoteName)
    end,
}

or as direct methods:

function Plugin:OnAfterStart(context, payload)
    context.Logger:Info("Framework ready")
end

Available hooks:

Hook Runtime Payload
PluginRegistered Both Plugin, Name
PluginUnregistered Both Plugin, Name
ClassRegistered Both Class, Name
ServiceRegistered Server Service, Name
ControllerRegistered Client Controller, Name
ComponentRegistered Both Component, Name
RemoteRegistered Server Service, ServiceName, RemoteName, RemoteType, Remote
RemoteDiscovered Client Service, ServiceName, RemoteName, RemoteType, Remote
NetworkRejected Server Context, Rejection
BeforeStart Both Options, Runtime
AfterStart Both Options, Runtime

Registration hooks that fire while modules are still loading do not enforce dependency ordering. Startup hooks and later framework hooks do enforce dependency ordering.

Error Handling

By default, plugin hook errors are isolated and logged with VG-PLUGIN-002. Startup continues when possible.

Use strict mode when a plugin is required for correctness:

Vanguard.Start({
    Plugins = {
        Strict = true,
    },
})

or on one plugin:

Strict = true

Strict hook failures throw a linked Vanguard error and reject startup.

Good Plugin Boundaries

Use plugins for:

  • diagnostics and metrics;
  • project-specific validation;
  • custom registries and generators;
  • development-only tracing;
  • integration glue for future Studio tools.

Avoid plugins that:

  • mutate service or controller internals unexpectedly;
  • bypass network validation/authentication/verification;
  • require remote code at runtime;
  • hide authoritative server checks in client code;
  • depend on private Vanguard tables not exposed in this guide.

Continue with API Reference, Lifecycle, and Errors.