- Implemented EmailChannelTestProvider to generate email preview payloads. - Implemented SlackChannelTestProvider to create Slack message previews. - Implemented TeamsChannelTestProvider for generating Teams Adaptive Card previews. - Implemented WebhookChannelTestProvider to create webhook payloads. - Added INotifyChannelTestProvider interface for channel-specific preview generation. - Created ChannelTestPreviewContracts for request and response models. - Developed NotifyChannelTestService to handle test send requests and generate previews. - Added rate limit policies for test sends and delivery history. - Implemented unit tests for service registration and binding. - Updated project files to include necessary dependencies and configurations.
		
			
				
	
	
		
			220 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Markdown
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Markdown
		
	
	
		
			Executable File
		
	
	
	
	
# 10 · Plug‑in SDK Guide — **Stella Ops**
 | 
						||
*(v 1.5 — 11 Jul 2025 · template install, no reload, IoC)*  
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 0 Audience & Scope  
 | 
						||
Guidance for developers who extend Stella Ops with schedule jobs, scanner adapters, TLS providers, notification channels, etc.  Everything here is OSS; commercial variants simply ship additional signed plug‑ins.
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 1 Prerequisites  
 | 
						||
 | 
						||
| Tool                    | Min Version                                                       |
 | 
						||
| ----------------------- | ----------------------------------------------------------------- |
 | 
						||
| .NET SDK                | {{ dotnet }}                                                      |
 | 
						||
| **StellaOps templates** | install once via `bash dotnet new install StellaOps.Templates::*` |
 | 
						||
| **Cosign**              | 2.3 + — used to sign DLLs                                         |
 | 
						||
| xUnit                   | 2.6                                                               |
 | 
						||
| Docker CLI              | only if your plug‑in shells out to containers                     |
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 2 Repository & Build Output  
 | 
						||
 | 
						||
Every plug‑in is hosted in **`git.stella‑ops.org`**.  
 | 
						||
At publish time it must copy its signed artefacts to:
 | 
						||
 | 
						||
~~~text
 | 
						||
src/backend/Stella.Ops.Plugin.Binaries/<MyPlugin>/
 | 
						||
        ├── MyPlugin.dll
 | 
						||
        └── MyPlugin.dll.sig
 | 
						||
~~~
 | 
						||
 | 
						||
The back‑end scans this folder on start‑up, verifies the **Cosign** signature, confirms the `[StellaPluginVersion]` gate, then loads the DLL inside an **isolated AssemblyLoadContext** to avoid dependency clashes
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 3 Project Scaffold  
 | 
						||
 | 
						||
Generate with the installed template:
 | 
						||
 | 
						||
~~~bash
 | 
						||
dotnet new stellaops-plugin-schedule \
 | 
						||
       -n MyPlugin.Schedule \
 | 
						||
       --output src
 | 
						||
~~~
 | 
						||
 | 
						||
Result:
 | 
						||
 | 
						||
~~~text
 | 
						||
src/
 | 
						||
 ├─ MyPlugin.Schedule/
 | 
						||
 │    ├─ MyJob.cs
 | 
						||
 │    └─ MyPlugin.Schedule.csproj
 | 
						||
 └─ tests/
 | 
						||
      └─ MyPlugin.Schedule.Tests/
 | 
						||
~~~
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 4 MSBuild Wiring  
 | 
						||
 | 
						||
Add this to **`MyPlugin.Schedule.csproj`** so the signed DLL + `.sig` land in the canonical plug‑in folder:
 | 
						||
 | 
						||
~~~xml
 | 
						||
<PropertyGroup>
 | 
						||
  <StellaPluginOut>$(SolutionDir)src/backend/Stella.Ops.Plugin.Binaries/$(MSBuildProjectName)</StellaPluginOut>
 | 
						||
</PropertyGroup>
 | 
						||
 | 
						||
<ItemGroup>
 | 
						||
  
 | 
						||
  <ProjectReference Include="..\..\StellaOps.Common\StellaOps.Common.csproj"
 | 
						||
                    PrivateAssets="all" /> 
 | 
						||
</ItemGroup>
 | 
						||
 | 
						||
<Target Name="CopyStellaPlugin" AfterTargets="Publish">
 | 
						||
  <MakeDir Directories="$(StellaPluginOut)" />
 | 
						||
  <Copy SourceFiles="$(PublishDir)$(AssemblyName).dll;$(PublishDir)$(AssemblyName).dll.sig"
 | 
						||
        DestinationFolder="$(StellaPluginOut)" />
 | 
						||
</Target>
 | 
						||
~~~
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 5 Dependency‑Injection Entry‑point  
 | 
						||
 | 
						||
Back‑end auto‑discovers restart‑time bindings through two mechanisms:
 | 
						||
 | 
						||
1. **Service binding metadata** for simple contracts.
 | 
						||
2. **`IDependencyInjectionRoutine`** implementations when you need full control.
 | 
						||
 | 
						||
### 5.1 Service binding metadata
 | 
						||
 | 
						||
Annotate implementations with `[ServiceBinding]` to declare their lifetime and service contract.  
 | 
						||
The loader honours scoped lifetimes and will register the service before executing any custom DI routines.
 | 
						||
 | 
						||
~~~csharp
 | 
						||
using Microsoft.Extensions.DependencyInjection;
 | 
						||
using StellaOps.DependencyInjection;
 | 
						||
 | 
						||
[ServiceBinding(typeof(IJob), ServiceLifetime.Scoped, RegisterAsSelf = true)]
 | 
						||
public sealed class MyJob : IJob
 | 
						||
{
 | 
						||
    // IJob dependencies can now use scoped services (Mongo sessions, etc.)
 | 
						||
}
 | 
						||
~~~
 | 
						||
 | 
						||
Use `RegisterAsSelf = true` when you also want to resolve the concrete type.  
 | 
						||
Set `ReplaceExisting = true` to override default descriptors if the host already provides one.
 | 
						||
 | 
						||
### 5.2 Dependency injection routines
 | 
						||
 | 
						||
For advanced scenarios continue to expose a routine:
 | 
						||
 | 
						||
~~~csharp
 | 
						||
namespace StellaOps.DependencyInjection;
 | 
						||
 | 
						||
public sealed class IoCConfigurator : IDependencyInjectionRoutine
 | 
						||
{
 | 
						||
    public IServiceCollection Register(IServiceCollection services, IConfiguration cfg)
 | 
						||
    {
 | 
						||
        services.AddSingleton<IJob, MyJob>();        // schedule job
 | 
						||
        services.Configure<MyPluginOptions>(cfg.GetSection("Plugins:MyPlugin"));
 | 
						||
        return services;
 | 
						||
    }
 | 
						||
}
 | 
						||
~~~
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 6 Schedule Plug‑ins  
 | 
						||
 | 
						||
### 6.1 Minimal Job  
 | 
						||
 | 
						||
~~~csharp
 | 
						||
using StellaOps.Scheduling;             // contract
 | 
						||
 | 
						||
[StellaPluginVersion("2.0.0")]
 | 
						||
public sealed class MyJob : IJob
 | 
						||
{
 | 
						||
    public async Task ExecuteAsync(CancellationToken ct)
 | 
						||
    {
 | 
						||
        Console.WriteLine("Hello from plug‑in!");
 | 
						||
        await Task.Delay(500, ct);
 | 
						||
    }
 | 
						||
}
 | 
						||
~~~
 | 
						||
 | 
						||
### 6.2 Cron Registration  
 | 
						||
 | 
						||
```csharp
 | 
						||
services.AddCronJob<MyJob>("0 15 * * *"); // everyday 
 | 
						||
```
 | 
						||
 | 
						||
15:00
 | 
						||
Cron syntax follows Hangfire rules 
 | 
						||
 | 
						||
## 7 Scanner Adapters
 | 
						||
 | 
						||
Implement IScannerRunner.
 | 
						||
Register inside Configure:
 | 
						||
```csharp
 | 
						||
services.AddScanner<MyAltScanner>("alt"); // backend 
 | 
						||
```
 | 
						||
 | 
						||
selects by --engine alt
 | 
						||
If the engine needs a side‑car container, include a Dockerfile in your repo and document resource expectations.
 | 
						||
## 8 Packaging & Signing
 | 
						||
 | 
						||
```bash
 | 
						||
dotnet publish -c Release -p:PublishSingleFile=true -o out
 | 
						||
cosign sign --key $COSIGN_KEY out/MyPlugin.Schedule.dll   # sign binary only
 | 
						||
sha256sum out/MyPlugin.Schedule.dll > out/.sha256          # optional checksum
 | 
						||
zip MyPlugin.zip out/* README.md
 | 
						||
```
 | 
						||
 | 
						||
Unsigned DLLs are refused when StellaOps:Security:DisableUnsigned=false.
 | 
						||
 | 
						||
## 9 Deployment
 | 
						||
 | 
						||
```bash
 | 
						||
docker cp MyPlugin.zip <backend>:/opt/plugins/ && docker restart <backend>
 | 
						||
```
 | 
						||
 | 
						||
Check /health – "plugins":["MyPlugin.Schedule@2.0.0"].
 | 
						||
(Hot‑reload was removed to keep the core process simple and memory‑safe.)
 | 
						||
 | 
						||
## 10 Configuration Patterns
 | 
						||
 | 
						||
| Need         | Pattern                                                   |
 | 
						||
| ------------ | --------------------------------------------------------- |
 | 
						||
| Settings     | Plugins:MyPlugin:* in appsettings.json.                   |
 | 
						||
| Secrets      | Redis secure:<plugin>:<key> (encrypted per TLS provider). |
 | 
						||
| Dynamic cron | Implement ICronConfigurable; UI exposes editor.           |
 | 
						||
 | 
						||
## 11 Testing & CI
 | 
						||
 | 
						||
| Layer       | Tool                       | Gate                |
 | 
						||
| ----------- | -------------------------- | ------------------- |
 | 
						||
| Unit        | xUnit + Moq                | ≥ 50 % lines        |
 | 
						||
| Integration | Testcontainers ‑ run in CI | Job completes < 5 s |
 | 
						||
| Style       | dotnet                     | format	0 warnings   |
 | 
						||
 | 
						||
Use the pre‑baked workflow in StellaOps.Templates as starting point.
 | 
						||
 | 
						||
## 12 Publishing to the Community Marketplace
 | 
						||
 | 
						||
Tag Git release plugin‑vX.Y.Z and attach the signed ZIP.
 | 
						||
Submit a PR to stellaops/community-plugins.json with metadata & git URL.
 | 
						||
On merge, the plug‑in shows up in the UI Marketplace.
 | 
						||
 | 
						||
## 13 Common Pitfalls
 | 
						||
 | 
						||
| Symptom             | Root cause                 | Fix                                         |
 | 
						||
| ------------------- | -------------------------- | ------------------------------------------- |
 | 
						||
| NotDetected         | .sig missing               | cosign sign …                               |
 | 
						||
| VersionGateMismatch | Backend 2.1 vs plug‑in 2.0 | Re‑compile / bump attribute                 |
 | 
						||
| FileLoadException   | Duplicate                  | StellaOps.Common	Ensure PrivateAssets="all" |
 | 
						||
| Redis               | timeouts	Large writes      | Batch or use Mongo                          |
 |