# 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 the static method below:

~~~csharp
namespace StellaOps.DependencyInjection;

public static class IoCConfigurator
{
    public static IServiceCollection Configure(this 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                          |