Restructure solution layout by module
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		| @@ -1,220 +1,220 @@ | ||||
| # Excititor Connector Packaging Guide | ||||
|  | ||||
| > **Audience:** teams implementing new Excititor provider plug‑ins (CSAF feeds, | ||||
| > OpenVEX attestations, etc.)   | ||||
| > **Prerequisites:** read `docs/ARCHITECTURE_EXCITITOR.md` and the module | ||||
| > `AGENTS.md` in `src/StellaOps.Excititor.Connectors.Abstractions/`. | ||||
|  | ||||
| The Excititor connector SDK gives you: | ||||
|  | ||||
| - `VexConnectorBase` – deterministic logging, SHA‑256 helpers, time provider. | ||||
| - `VexConnectorOptionsBinder` – strongly typed YAML/JSON configuration binding. | ||||
| - `IVexConnectorOptionsValidator<T>` – custom validation hooks (offline defaults, auth invariants). | ||||
| - `VexConnectorDescriptor` & metadata helpers for consistent telemetry. | ||||
|  | ||||
| This guide explains how to package a connector so the Excititor Worker/WebService | ||||
| can load it via the plugin host. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1. Project layout | ||||
|  | ||||
| Start from the template under | ||||
| `docs/dev/templates/excititor-connector/`. It contains: | ||||
|  | ||||
| ``` | ||||
| Excititor.MyConnector/ | ||||
| ├── src/ | ||||
| │   ├── Excititor.MyConnector.csproj | ||||
| │   ├── MyConnectorOptions.cs | ||||
| │   ├── MyConnector.cs | ||||
| │   └── MyConnectorPlugin.cs | ||||
| └── manifest/ | ||||
|     └── connector.manifest.yaml | ||||
| ``` | ||||
|  | ||||
| Key points: | ||||
|  | ||||
| - Target `net10.0`, enable `TreatWarningsAsErrors`, reference the | ||||
|   `StellaOps.Excititor.Connectors.Abstractions` project (or NuGet once published). | ||||
| - Keep project ID prefix `StellaOps.Excititor.Connectors.<Provider>` so the | ||||
|   plugin loader can discover it with the default search pattern. | ||||
|  | ||||
| ### 1.1 csproj snippet | ||||
|  | ||||
| ```xml | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net10.0</TargetFramework> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\..\..\src\StellaOps.Excititor.Connectors.Abstractions\StellaOps.Excititor.Connectors.Abstractions.csproj" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| ``` | ||||
|  | ||||
| Adjust the `ProjectReference` for your checkout (or switch to a NuGet package | ||||
| once published). | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2. Implement the connector | ||||
|  | ||||
| 1. **Options model** – create an options POCO with data-annotation attributes. | ||||
|    Bind it via `VexConnectorOptionsBinder.Bind<TOptions>` in your connector | ||||
|    constructor or `ValidateAsync`. | ||||
| 2. **Validator** – implement `IVexConnectorOptionsValidator<TOptions>` to add | ||||
|    complex checks (e.g., ensure both `clientId` and `clientSecret` are present). | ||||
| 3. **Connector** – inherit from `VexConnectorBase`. Implement: | ||||
|    - `ValidateAsync` – run binder/validators, log configuration summary. | ||||
|    - `FetchAsync` – stream raw documents to `context.RawSink`. | ||||
|    - `NormalizeAsync` – convert raw documents into `VexClaimBatch` via | ||||
|      format-specific normalizers (`context.Normalizers`). | ||||
| 4. **Plugin adapter** – expose the connector via a plugin entry point so the | ||||
|    host can instantiate it. | ||||
|  | ||||
| ### 2.1 Options binding example | ||||
|  | ||||
| ```csharp | ||||
| public sealed class MyConnectorOptions | ||||
| { | ||||
|     [Required] | ||||
|     [Url] | ||||
|     public string CatalogUri { get; set; } = default!; | ||||
|  | ||||
|     [Required] | ||||
|     public string ApiKey { get; set; } = default!; | ||||
|  | ||||
|     [Range(1, 64)] | ||||
|     public int MaxParallelRequests { get; set; } = 4; | ||||
| } | ||||
|  | ||||
| public sealed class MyConnectorOptionsValidator : IVexConnectorOptionsValidator<MyConnectorOptions> | ||||
| { | ||||
|     public void Validate(VexConnectorDescriptor descriptor, MyConnectorOptions options, IList<string> errors) | ||||
|     { | ||||
|         if (!options.CatalogUri.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             errors.Add("CatalogUri must use HTTPS."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Bind inside the connector: | ||||
|  | ||||
| ```csharp | ||||
| private readonly MyConnectorOptions _options; | ||||
|  | ||||
| public MyConnector(VexConnectorDescriptor descriptor, ILogger<MyConnector> logger, TimeProvider timeProvider) | ||||
|     : base(descriptor, logger, timeProvider) | ||||
| { | ||||
|     // `settings` comes from the orchestrator; validators registered via DI. | ||||
|     _options = VexConnectorOptionsBinder.Bind<MyConnectorOptions>( | ||||
|         descriptor, | ||||
|         VexConnectorSettings.Empty, | ||||
|         validators: new[] { new MyConnectorOptionsValidator() }); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Replace `VexConnectorSettings.Empty` with the actual settings from context | ||||
| inside `ValidateAsync`. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3. Plugin adapter & manifest | ||||
|  | ||||
| Create a simple plugin class that implements | ||||
| `StellaOps.Plugin.IConnectorPlugin`. The Worker/WebService plugin host uses | ||||
| this contract today. | ||||
|  | ||||
| ```csharp | ||||
| public sealed class MyConnectorPlugin : IConnectorPlugin | ||||
| { | ||||
|     private static readonly VexConnectorDescriptor Descriptor = | ||||
|         new("excititor:my-provider", VexProviderKind.Vendor, "My Provider VEX"); | ||||
|  | ||||
|     public string Name => Descriptor.DisplayName; | ||||
|  | ||||
|     public bool IsAvailable(IServiceProvider services) => true; // inject feature flags if needed | ||||
|  | ||||
|     public IFeedConnector Create(IServiceProvider services) | ||||
|     { | ||||
|         var logger = services.GetRequiredService<ILogger<MyConnector>>(); | ||||
|         var timeProvider = services.GetRequiredService<TimeProvider>(); | ||||
|         return new MyConnector(Descriptor, logger, timeProvider); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| > **Note:** the Excititor Worker currently instantiates connectors through the | ||||
| > shared `IConnectorPlugin` contract. Once a dedicated Excititor plugin interface | ||||
| > lands you simply swap the base interface; the descriptor/connector code | ||||
| > remains unchanged. | ||||
|  | ||||
| Provide a manifest describing the assembly for operational tooling: | ||||
|  | ||||
| ```yaml | ||||
| # manifest/connector.manifest.yaml | ||||
| id: excititor-my-provider | ||||
| assembly: StellaOps.Excititor.Connectors.MyProvider.dll | ||||
| entryPoint: StellaOps.Excititor.Connectors.MyProvider.MyConnectorPlugin | ||||
| description: > | ||||
|   Official VEX feed for ExampleCorp products (CSAF JSON, daily updates). | ||||
| tags: | ||||
|   - excititor | ||||
|   - csaf | ||||
|   - vendor | ||||
| ``` | ||||
|  | ||||
| Store manifests under `/opt/stella/excititor/plugins/<connector>/manifest/` in | ||||
| production so the deployment tooling can inventory and verify plug‑ins. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4. Packaging workflow | ||||
|  | ||||
| 1. `dotnet publish -c Release` → copy the published DLLs to | ||||
|    `/opt/stella/excititor/plugins/<Provider>/`. | ||||
| 2. Place `connector.manifest.yaml` next to the binaries. | ||||
| 3. Restart the Excititor Worker or WebService (hot reload not supported yet). | ||||
| 4. Verify logs: `VEX-ConnectorLoader` should list the connector descriptor. | ||||
|  | ||||
| ### 4.1 Offline kits | ||||
|  | ||||
| - Add the connector folder (binaries + manifest) to the Offline Kit bundle. | ||||
| - Include a `settings.sample.yaml` demonstrating offline-friendly defaults. | ||||
| - Document any external dependencies (e.g., SHA mirrors) in the manifest `notes` | ||||
|   field. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5. Testing checklist | ||||
|  | ||||
| - Unit tests around options binding & validators. | ||||
| - Integration tests (future `StellaOps.Excititor.Connectors.Abstractions.Tests`) | ||||
|   verifying deterministic logging scopes: | ||||
|   `logger.BeginScope` should produce `vex.connector.id`, `vex.connector.kind`, | ||||
|   and `vex.connector.operation`. | ||||
| - Deterministic SHA tests: repeated `CreateRawDocument` calls with identical | ||||
|   content must return the same digest. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6. Reference template | ||||
|  | ||||
| See `docs/dev/templates/excititor-connector/` for the full quick‑start including: | ||||
|  | ||||
| - Sample options class + validator. | ||||
| - Connector implementation inheriting from `VexConnectorBase`. | ||||
| - Plugin adapter + manifest. | ||||
|  | ||||
| Copy the directory, rename namespaces/IDs, then iterate on provider-specific | ||||
| logic. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-17* | ||||
| # Excititor Connector Packaging Guide | ||||
|  | ||||
| > **Audience:** teams implementing new Excititor provider plug‑ins (CSAF feeds, | ||||
| > OpenVEX attestations, etc.)   | ||||
| > **Prerequisites:** read `docs/ARCHITECTURE_EXCITITOR.md` and the module | ||||
| > `AGENTS.md` in `src/Excititor/__Libraries/StellaOps.Excititor.Connectors.Abstractions/`. | ||||
|  | ||||
| The Excititor connector SDK gives you: | ||||
|  | ||||
| - `VexConnectorBase` – deterministic logging, SHA‑256 helpers, time provider. | ||||
| - `VexConnectorOptionsBinder` – strongly typed YAML/JSON configuration binding. | ||||
| - `IVexConnectorOptionsValidator<T>` – custom validation hooks (offline defaults, auth invariants). | ||||
| - `VexConnectorDescriptor` & metadata helpers for consistent telemetry. | ||||
|  | ||||
| This guide explains how to package a connector so the Excititor Worker/WebService | ||||
| can load it via the plugin host. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1. Project layout | ||||
|  | ||||
| Start from the template under | ||||
| `docs/dev/templates/excititor-connector/`. It contains: | ||||
|  | ||||
| ``` | ||||
| Excititor.MyConnector/ | ||||
| ├── src/ | ||||
| │   ├── Excititor.MyConnector.csproj | ||||
| │   ├── MyConnectorOptions.cs | ||||
| │   ├── MyConnector.cs | ||||
| │   └── MyConnectorPlugin.cs | ||||
| └── manifest/ | ||||
|     └── connector.manifest.yaml | ||||
| ``` | ||||
|  | ||||
| Key points: | ||||
|  | ||||
| - Target `net10.0`, enable `TreatWarningsAsErrors`, reference the | ||||
|   `StellaOps.Excititor.Connectors.Abstractions` project (or NuGet once published). | ||||
| - Keep project ID prefix `StellaOps.Excititor.Connectors.<Provider>` so the | ||||
|   plugin loader can discover it with the default search pattern. | ||||
|  | ||||
| ### 1.1 csproj snippet | ||||
|  | ||||
| ```xml | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net10.0</TargetFramework> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\..\..\src\StellaOps.Excititor.Connectors.Abstractions\StellaOps.Excititor.Connectors.Abstractions.csproj" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| ``` | ||||
|  | ||||
| Adjust the `ProjectReference` for your checkout (or switch to a NuGet package | ||||
| once published). | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2. Implement the connector | ||||
|  | ||||
| 1. **Options model** – create an options POCO with data-annotation attributes. | ||||
|    Bind it via `VexConnectorOptionsBinder.Bind<TOptions>` in your connector | ||||
|    constructor or `ValidateAsync`. | ||||
| 2. **Validator** – implement `IVexConnectorOptionsValidator<TOptions>` to add | ||||
|    complex checks (e.g., ensure both `clientId` and `clientSecret` are present). | ||||
| 3. **Connector** – inherit from `VexConnectorBase`. Implement: | ||||
|    - `ValidateAsync` – run binder/validators, log configuration summary. | ||||
|    - `FetchAsync` – stream raw documents to `context.RawSink`. | ||||
|    - `NormalizeAsync` – convert raw documents into `VexClaimBatch` via | ||||
|      format-specific normalizers (`context.Normalizers`). | ||||
| 4. **Plugin adapter** – expose the connector via a plugin entry point so the | ||||
|    host can instantiate it. | ||||
|  | ||||
| ### 2.1 Options binding example | ||||
|  | ||||
| ```csharp | ||||
| public sealed class MyConnectorOptions | ||||
| { | ||||
|     [Required] | ||||
|     [Url] | ||||
|     public string CatalogUri { get; set; } = default!; | ||||
|  | ||||
|     [Required] | ||||
|     public string ApiKey { get; set; } = default!; | ||||
|  | ||||
|     [Range(1, 64)] | ||||
|     public int MaxParallelRequests { get; set; } = 4; | ||||
| } | ||||
|  | ||||
| public sealed class MyConnectorOptionsValidator : IVexConnectorOptionsValidator<MyConnectorOptions> | ||||
| { | ||||
|     public void Validate(VexConnectorDescriptor descriptor, MyConnectorOptions options, IList<string> errors) | ||||
|     { | ||||
|         if (!options.CatalogUri.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             errors.Add("CatalogUri must use HTTPS."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Bind inside the connector: | ||||
|  | ||||
| ```csharp | ||||
| private readonly MyConnectorOptions _options; | ||||
|  | ||||
| public MyConnector(VexConnectorDescriptor descriptor, ILogger<MyConnector> logger, TimeProvider timeProvider) | ||||
|     : base(descriptor, logger, timeProvider) | ||||
| { | ||||
|     // `settings` comes from the orchestrator; validators registered via DI. | ||||
|     _options = VexConnectorOptionsBinder.Bind<MyConnectorOptions>( | ||||
|         descriptor, | ||||
|         VexConnectorSettings.Empty, | ||||
|         validators: new[] { new MyConnectorOptionsValidator() }); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Replace `VexConnectorSettings.Empty` with the actual settings from context | ||||
| inside `ValidateAsync`. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3. Plugin adapter & manifest | ||||
|  | ||||
| Create a simple plugin class that implements | ||||
| `StellaOps.Plugin.IConnectorPlugin`. The Worker/WebService plugin host uses | ||||
| this contract today. | ||||
|  | ||||
| ```csharp | ||||
| public sealed class MyConnectorPlugin : IConnectorPlugin | ||||
| { | ||||
|     private static readonly VexConnectorDescriptor Descriptor = | ||||
|         new("excititor:my-provider", VexProviderKind.Vendor, "My Provider VEX"); | ||||
|  | ||||
|     public string Name => Descriptor.DisplayName; | ||||
|  | ||||
|     public bool IsAvailable(IServiceProvider services) => true; // inject feature flags if needed | ||||
|  | ||||
|     public IFeedConnector Create(IServiceProvider services) | ||||
|     { | ||||
|         var logger = services.GetRequiredService<ILogger<MyConnector>>(); | ||||
|         var timeProvider = services.GetRequiredService<TimeProvider>(); | ||||
|         return new MyConnector(Descriptor, logger, timeProvider); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| > **Note:** the Excititor Worker currently instantiates connectors through the | ||||
| > shared `IConnectorPlugin` contract. Once a dedicated Excititor plugin interface | ||||
| > lands you simply swap the base interface; the descriptor/connector code | ||||
| > remains unchanged. | ||||
|  | ||||
| Provide a manifest describing the assembly for operational tooling: | ||||
|  | ||||
| ```yaml | ||||
| # manifest/connector.manifest.yaml | ||||
| id: excititor-my-provider | ||||
| assembly: StellaOps.Excititor.Connectors.MyProvider.dll | ||||
| entryPoint: StellaOps.Excititor.Connectors.MyProvider.MyConnectorPlugin | ||||
| description: > | ||||
|   Official VEX feed for ExampleCorp products (CSAF JSON, daily updates). | ||||
| tags: | ||||
|   - excititor | ||||
|   - csaf | ||||
|   - vendor | ||||
| ``` | ||||
|  | ||||
| Store manifests under `/opt/stella/excititor/plugins/<connector>/manifest/` in | ||||
| production so the deployment tooling can inventory and verify plug‑ins. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4. Packaging workflow | ||||
|  | ||||
| 1. `dotnet publish -c Release` → copy the published DLLs to | ||||
|    `/opt/stella/excititor/plugins/<Provider>/`. | ||||
| 2. Place `connector.manifest.yaml` next to the binaries. | ||||
| 3. Restart the Excititor Worker or WebService (hot reload not supported yet). | ||||
| 4. Verify logs: `VEX-ConnectorLoader` should list the connector descriptor. | ||||
|  | ||||
| ### 4.1 Offline kits | ||||
|  | ||||
| - Add the connector folder (binaries + manifest) to the Offline Kit bundle. | ||||
| - Include a `settings.sample.yaml` demonstrating offline-friendly defaults. | ||||
| - Document any external dependencies (e.g., SHA mirrors) in the manifest `notes` | ||||
|   field. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5. Testing checklist | ||||
|  | ||||
| - Unit tests around options binding & validators. | ||||
| - Integration tests (future `StellaOps.Excititor.Connectors.Abstractions.Tests`) | ||||
|   verifying deterministic logging scopes: | ||||
|   `logger.BeginScope` should produce `vex.connector.id`, `vex.connector.kind`, | ||||
|   and `vex.connector.operation`. | ||||
| - Deterministic SHA tests: repeated `CreateRawDocument` calls with identical | ||||
|   content must return the same digest. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6. Reference template | ||||
|  | ||||
| See `docs/dev/templates/excititor-connector/` for the full quick‑start including: | ||||
|  | ||||
| - Sample options class + validator. | ||||
| - Connector implementation inheriting from `VexConnectorBase`. | ||||
| - Plugin adapter + manifest. | ||||
|  | ||||
| Copy the directory, rename namespaces/IDs, then iterate on provider-specific | ||||
| logic. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-17* | ||||
|   | ||||
		Reference in New Issue
	
	Block a user