# Plugin SDK Guide This guide explains how StellaOps loads, validates, and wires restart-time plugins. It is intentionally cross-cutting: module-specific plugin contracts (Authority identity providers, Concelier connectors, Scanner analyzers, CLI command modules, etc.) live in the corresponding module dossiers under `docs/modules/`. ## 1) What a "plugin" means in StellaOps StellaOps uses plugins to extend behavior without losing: - Determinism (stable ordering, stable identifiers, replayable outputs). - Offline posture (no hidden outbound calls; explicit trust roots and caches). - Security boundaries (no client-controlled identity injection; signed artifacts where enforced). Most services load plugins at process start (restart-time). Hot-reload is not a goal: restart-time loading keeps memory and dependency isolation predictable. ## 2) Service plugin loading model (restart-time) Service plugins are loaded by the `StellaOps.Plugin` library: - Discovery occurs in a configured plugin directory. - Assemblies are loaded in an isolated `AssemblyLoadContext`. - A compatibility gate runs before DI registration: - version attribute presence and host compatibility - optional signature verification ### 2.1 Plugin directory and discovery patterns Default behavior (from `StellaOps.Plugin.Hosting.PluginHostOptions`): - Base directory: `AppContext.BaseDirectory` (unless overridden). - Plugin directory: - `.PluginBinaries` when `PrimaryPrefix` is set, otherwise `PluginBinaries`. - Discovery glob(s): - `.Plugin.*.dll` for each configured prefix. Hosts may override the directory and/or add explicit `searchPatterns` in their config. Use module operations docs to see the authoritative configuration for a given service. ### 2.2 Deterministic ordering The loader is deterministic: - When no explicit order is configured, discovered plugin assemblies are sorted by filename (case-insensitive). - When an explicit order is configured, that order is applied first and the remainder stays sorted. ## 3) Version compatibility requirements Plugins should declare the assembly-level attribute: ```csharp using StellaOps.Plugin.Versioning; [assembly: StellaPluginVersion("1.2.3", MinimumHostVersion = "1.0.0")] ``` The host can enforce: - Required attribute presence (`RequireVersionAttribute`). - Compatibility bounds (`MinimumHostVersion` / `MaximumHostVersion`). - Strict major compatibility when `MaximumHostVersion` is not set (`StrictMajorVersionCheck`). ## 4) Dependency injection wiring StellaOps supports two DI registration mechanisms. ### 4.1 Simple bindings via `ServiceBindingAttribute` Annotate implementations with `ServiceBindingAttribute`: ```csharp using Microsoft.Extensions.DependencyInjection; using StellaOps.DependencyInjection; [ServiceBinding(typeof(IMyContract), ServiceLifetime.Singleton, RegisterAsSelf = true)] public sealed class MyPluginService : IMyContract { } ``` ### 4.2 Advanced wiring via `IDependencyInjectionRoutine` For full control, include a concrete `IDependencyInjectionRoutine` implementation. The host discovers and runs all routines in loaded plugin assemblies: ```csharp using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using StellaOps.DependencyInjection; public sealed class MyPluginDi : IDependencyInjectionRoutine { public IServiceCollection Register(IServiceCollection services, IConfiguration configuration) { services.AddSingleton(); return services; } } ``` ## 5) Signing and verification (Cosign) When enabled, the host can verify plugin assemblies using Cosign (`cosign verify-blob`). The signature file is expected adjacent to the assembly: - `MyPlugin.dll` - `MyPlugin.dll.sig` Verification is performed by `StellaOps.Plugin.Security.CosignPluginVerifier` and controlled by host configuration (for example `EnforceSignatureVerification` plus verifier options). Offline note: verification can be performed without transparency log access when the host is configured accordingly (for example by ignoring tlog or using an offline receipt flow). ## 6) Repo layout (this monorepo) In this repository, plugin binaries are typically staged under module-specific `*.PluginBinaries` directories (examples): - `src/StellaOps.Authority.PluginBinaries/` - `src/Concelier/StellaOps.Concelier.PluginBinaries/` The authoritative loader configuration is owned by the host module and documented in its operations/architecture docs. ## 7) Testing expectations Plugins should ship tests that protect determinism and compatibility: - Stable ordering of outputs and collections. - Stable timestamps (UTC ISO-8601). - Fixture-backed inputs for offline operation. - Compatibility checks for host version boundaries. Reference tests for the generic plugin host live under: - `src/__Libraries/__Tests/StellaOps.Plugin.Tests/` ## 8) Where to go next - Authority plugins and operations: `docs/modules/authority/` - Concelier connectors and operations: `docs/modules/concelier/` - Scanner analyzers and operations: `docs/modules/scanner/` - CLI command modules: `docs/modules/cli/`