# 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` – 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.` so the plugin loader can discover it with the default search pattern. ### 1.1 csproj snippet ```xml net10.0 enable enable true ``` 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` in your connector constructor or `ValidateAsync`. 2. **Validator** – implement `IVexConnectorOptionsValidator` 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 { public void Validate(VexConnectorDescriptor descriptor, MyConnectorOptions options, IList 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 logger, TimeProvider timeProvider) : base(descriptor, logger, timeProvider) { // `settings` comes from the orchestrator; validators registered via DI. _options = VexConnectorOptionsBinder.Bind( 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>(); var timeProvider = services.GetRequiredService(); 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//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//`. 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*