# stella CLI - Plugin Architecture **Sprint:** SPRINT_4100_0006_0006 - CLI Documentation Overhaul ## Overview The `stella` CLI is built with a plugin architecture that enables conditional compilation of regional cryptographic providers (GOST, eIDAS, SM) while maintaining a unified command interface. This design supports compliance with export control regulations and cryptographic standards across different jurisdictions. **Key Design Goals:** 1. **Conditional Compilation**: Include only authorized crypto providers per distribution 2. **Plugin Isolation**: Crypto providers as self-contained, testable modules 3. **Dependency Injection**: Runtime service resolution for providers 4. **Configuration-driven**: Profile-based provider selection 5. **Extensibility**: Easy addition of new providers without core CLI changes --- ## Architecture Layers ``` ┌─────────────────────────────────────────────────────────────┐ │ stella CLI │ ├─────────────────────────────────────────────────────────────┤ │ Command Groups │ │ ├─ scan, aoc, symbols, crypto, admin, ... │ │ └─ System.CommandLine 2.0 routing │ ├─────────────────────────────────────────────────────────────┤ │ Plugin System │ │ ├─ ICryptoProvider interface │ │ ├─ Plugin discovery (build-time + runtime) │ │ └─ DependencyInjection (Microsoft.Extensions.DI) │ ├─────────────────────────────────────────────────────────────┤ │ Crypto Plugins (Conditional) │ │ ├─ Default (.NET Crypto, BouncyCastle) [ALL] │ │ ├─ GOST (CryptoPro, OpenSSL-GOST, PKCS#11) [RUSSIA] │ │ ├─ eIDAS (TSP Client, Local Signer) [EU] │ │ └─ SM (GmSSL, SM Remote CSP) [CHINA] │ ├─────────────────────────────────────────────────────────────┤ │ Backend Integration │ │ ├─ Authority (OAuth2 + DPoP) │ │ ├─ Scanner, Concelier, Policy, ... │ │ └─ HTTP clients with retry policies │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Build-time Plugin Selection ### Conditional Compilation Workflow ```mermaid graph TD A[MSBuild Start] --> B{Check Build Flags} B -->|StellaOpsEnableGOST=true| C[Include GOST Plugin] B -->|StellaOpsEnableEIDAS=true| D[Include eIDAS Plugin] B -->|StellaOpsEnableSM=true| E[Include SM Plugin] B -->|No flags| F[Include Default Only] C --> G[Set STELLAOPS_ENABLE_GOST] D --> H[Set STELLAOPS_ENABLE_EIDAS] E --> I[Set STELLAOPS_ENABLE_SM] G --> J[Compile with Plugin] H --> J I --> J F --> J J --> K[Link Plugin Assembly] K --> L[Final Binary] ``` ### Project Structure ``` src/Cli/ ├── StellaOps.Cli/ │ ├── Program.cs # Entry point, DI setup │ ├── Commands/ │ │ ├── CommandFactory.cs # Command routing │ │ ├── Crypto/CryptoCommandGroup.cs # Crypto commands │ │ ├── Admin/AdminCommandGroup.cs # Admin commands │ │ └── ... │ └── StellaOps.Cli.csproj # Conditional │ ├── StellaOps.Cli.Crypto/ │ ├── ICryptoProvider.cs # Plugin interface │ ├── ICryptoProviderDiagnostics.cs # Diagnostics interface │ └── Models/ # Shared models │ ├── StellaOps.Cli.Crypto.Default/ # Always included │ ├── DotNetCryptoProvider.cs # .NET crypto │ ├── BouncyCastleCryptoProvider.cs # BouncyCastle │ └── ServiceCollectionExtensions.cs # DI registration │ ├── StellaOps.Cli.Crypto.Gost/ # Conditional (Russia) │ ├── GostCryptoProvider.cs # GOST implementation │ ├── CryptoProAdapter.cs # CryptoPro CSP adapter │ ├── OpenSslGostAdapter.cs # OpenSSL-GOST adapter │ └── ServiceCollectionExtensions.cs │ ├── StellaOps.Cli.Crypto.Eidas/ # Conditional (EU) │ ├── EidasCryptoProvider.cs # eIDAS implementation │ ├── TspClientAdapter.cs # TSP remote signing │ └── ServiceCollectionExtensions.cs │ └── StellaOps.Cli.Crypto.Sm/ # Conditional (China) ├── SmCryptoProvider.cs # SM implementation ├── GmSslAdapter.cs # GmSSL adapter └── ServiceCollectionExtensions.cs ``` ### StellaOps.Cli.csproj (Conditional References) ```xml net10.0 Exe $(DefineConstants);STELLAOPS_ENABLE_GOST $(DefineConstants);STELLAOPS_ENABLE_EIDAS $(DefineConstants);STELLAOPS_ENABLE_SM ``` ### Build Commands ```bash # International distribution (default, no flags) dotnet publish src/Cli/StellaOps.Cli --configuration Release --runtime linux-x64 # Russia distribution (GOST enabled) dotnet publish src/Cli/StellaOps.Cli \ --configuration Release \ --runtime linux-x64 \ -p:StellaOpsEnableGOST=true # EU distribution (eIDAS enabled) dotnet publish src/Cli/StellaOps.Cli \ --configuration Release \ --runtime linux-x64 \ -p:StellaOpsEnableEIDAS=true # China distribution (SM enabled) dotnet publish src/Cli/StellaOps.Cli \ --configuration Release \ --runtime linux-x64 \ -p:StellaOpsEnableSM=true ``` --- ## Runtime Plugin Discovery ### Program.cs - DI Registration ```csharp using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using StellaOps.Cli.Crypto; using StellaOps.Cli.Crypto.Default; #if STELLAOPS_ENABLE_GOST using StellaOps.Cli.Crypto.Gost; #endif #if STELLAOPS_ENABLE_EIDAS using StellaOps.Cli.Crypto.Eidas; #endif #if STELLAOPS_ENABLE_SM using StellaOps.Cli.Crypto.Sm; #endif namespace StellaOps.Cli; public class Program { public static async Task Main(string[] args) { // Build configuration var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true) .AddYamlFile("appsettings.yaml", optional: true) .AddYamlFile(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".stellaops", "config.yaml"), optional: true) .AddEnvironmentVariables("STELLAOPS_") .Build(); // Setup DI container var services = new ServiceCollection(); // Register configuration services.AddSingleton(configuration); // Register HTTP clients services.AddHttpClient("StellaOpsBackend", client => { var baseUrl = configuration["StellaOps:Backend:BaseUrl"]; if (!string.IsNullOrEmpty(baseUrl)) client.BaseAddress = new Uri(baseUrl); }); // Register default crypto providers (always available) services.AddDefaultCryptoProviders(configuration); // Register regional crypto providers (conditional compilation) #if STELLAOPS_ENABLE_GOST services.AddGostCryptoProviders(configuration); #endif #if STELLAOPS_ENABLE_EIDAS services.AddEidasCryptoProviders(configuration); #endif #if STELLAOPS_ENABLE_SM services.AddSmCryptoProviders(configuration); #endif // Build service provider var serviceProvider = services.BuildServiceProvider(); // Create root command and run var rootCommand = CommandFactory.CreateRootCommand(serviceProvider); return await rootCommand.InvokeAsync(args); } } ``` ### Plugin Discovery Flow ```mermaid sequenceDiagram participant Build as MSBuild participant CLI as stella CLI participant DI as DI Container participant Plugin as Crypto Plugin participant User as User Command Build->>Build: Check StellaOpsEnableGOST=true Build->>Build: Include GOST plugin Build->>Build: Set DefineConstants=STELLAOPS_ENABLE_GOST Build->>CLI: Compile with GOST plugin User->>CLI: stella crypto sign --provider gost CLI->>CLI: Program.cs startup CLI->>CLI: Check #if STELLAOPS_ENABLE_GOST CLI->>DI: services.AddGostCryptoProviders() DI->>Plugin: Register GostCryptoProvider as ICryptoProvider Plugin->>DI: Provider registered CLI->>DI: Resolve ICryptoProvider (name="gost") DI->>Plugin: Return GostCryptoProvider instance Plugin->>CLI: Execute sign operation CLI->>User: Signature created ``` --- ## Plugin Interfaces ### ICryptoProvider The core interface all crypto providers must implement: ```csharp namespace StellaOps.Cli.Crypto; public interface ICryptoProvider { /// /// Unique provider name (e.g., "gost", "eidas", "sm") /// string Name { get; } /// /// Supported algorithms (e.g., "GOST12-256", "ECDSA-P256") /// string[] SupportedAlgorithms { get; } /// /// Sign data with specified algorithm and key /// Task SignAsync( byte[] data, string algorithm, CryptoKeyReference keyRef, CancellationToken cancellationToken = default); /// /// Verify signature /// Task VerifyAsync( byte[] data, byte[] signature, string algorithm, CryptoKeyReference keyRef, CancellationToken cancellationToken = default); /// /// List available keys /// Task> ListKeysAsync( CancellationToken cancellationToken = default); } ``` ### ICryptoProviderDiagnostics Optional interface for provider diagnostics: ```csharp namespace StellaOps.Cli.Crypto; public interface ICryptoProviderDiagnostics { /// /// Run provider self-test /// Task HealthCheckAsync(CancellationToken cancellationToken = default); /// /// Get provider version and capabilities /// ProviderInfo GetInfo(); } public sealed record ProviderHealthCheck { public required string ProviderName { get; init; } public required bool IsHealthy { get; init; } public required string[] Checks { get; init; } public string? ErrorMessage { get; init; } } public sealed record ProviderInfo { public required string Name { get; init; } public required string Version { get; init; } public required string[] Capabilities { get; init; } public required string[] SupportedAlgorithms { get; init; } } ``` ### CryptoKeyReference Represents a reference to a cryptographic key: ```csharp namespace StellaOps.Cli.Crypto; public sealed record CryptoKeyReference { /// /// Key identifier (e.g., "prod-key-2024", file path, HSM slot) /// public required string KeyId { get; init; } /// /// Key source: "file", "hsm", "kms", "csp" /// public required string Source { get; init; } /// /// Additional parameters (e.g., HSM PIN, KMS region) /// public IReadOnlyDictionary? Parameters { get; init; } } ``` --- ## Configuration ### Profile-based Provider Selection ```yaml StellaOps: Crypto: # Default provider (when --provider not specified) DefaultProvider: "default" # Crypto profiles for easy switching Profiles: - name: "default-signing" provider: "default" algorithm: "ECDSA-P256" keyId: "default-key" - name: "gost-signing" provider: "gost" algorithm: "GOST12-256" keyId: "gost-key-2024" - name: "eidas-qes" provider: "eidas" algorithm: "ECDSA-P256-QES" keyId: "eidas-qes-key" # Provider-specific configuration Providers: Gost: CryptoProCsp: Enabled: true ContainerName: "StellaOps-GOST-2024" OpenSslGost: Enabled: false EnginePath: "/usr/lib/engines/gost.so" Eidas: TspClient: Enabled: true TspUrl: "https://tsp.example.eu/api/v1/sign" ApiKey: "${EIDAS_TSP_API_KEY}" Sm: GmSsl: Enabled: true LibraryPath: "/usr/lib/libgmssl.so" ``` ### Usage with Profiles ```bash # Use default profile stella crypto sign --file document.pdf # Use specific profile stella crypto sign --profile gost-signing --file document.pdf # Override provider explicitly stella crypto sign --provider gost --algorithm GOST12-256 --key-id key1 --file document.pdf ``` --- ## Distribution Matrix | Distribution | Default | GOST | eIDAS | SM | |--------------|---------|------|-------|-----| | **stella-international** | ✅ | ❌ | ❌ | ❌ | | **stella-russia** | ✅ | ✅ | ❌ | ❌ | | **stella-eu** | ✅ | ❌ | ✅ | ❌ | | **stella-china** | ✅ | ❌ | ❌ | ✅ | **Verification:** ```bash # Check available providers stella crypto providers # Output (International): # Available Crypto Providers: # - default (.NET Crypto, BouncyCastle) # Output (Russia): # Available Crypto Providers: # - default (.NET Crypto, BouncyCastle) # - gost (GOST R 34.10-2012, GOST R 34.11-2012) ``` --- ## Creating Custom Plugins ### 1. Create Plugin Project ```bash dotnet new classlib -n StellaOps.Cli.Crypto.MyCustom cd StellaOps.Cli.Crypto.MyCustom # Add reference to interface project dotnet add reference ../StellaOps.Cli.Crypto/StellaOps.Cli.Crypto.csproj ``` ### 2. Implement ICryptoProvider ```csharp using StellaOps.Cli.Crypto; namespace StellaOps.Cli.Crypto.MyCustom; public class MyCustomCryptoProvider : ICryptoProvider, ICryptoProviderDiagnostics { private readonly MyCustomCryptoOptions _options; public MyCustomCryptoProvider(IOptions options) { _options = options.Value; } public string Name => "mycustom"; public string[] SupportedAlgorithms => new[] { "MYCUSTOM-ALG1", "MYCUSTOM-ALG2" }; public async Task SignAsync( byte[] data, string algorithm, CryptoKeyReference keyRef, CancellationToken cancellationToken = default) { // Implementation throw new NotImplementedException(); } public async Task VerifyAsync( byte[] data, byte[] signature, string algorithm, CryptoKeyReference keyRef, CancellationToken cancellationToken = default) { // Implementation throw new NotImplementedException(); } public async Task> ListKeysAsync( CancellationToken cancellationToken = default) { // Implementation throw new NotImplementedException(); } public async Task HealthCheckAsync( CancellationToken cancellationToken = default) { return new ProviderHealthCheck { ProviderName = Name, IsHealthy = true, Checks = new[] { "Library loaded", "Keys accessible" } }; } public ProviderInfo GetInfo() { return new ProviderInfo { Name = Name, Version = "1.0.0", Capabilities = new[] { "sign", "verify" }, SupportedAlgorithms = SupportedAlgorithms }; } } ``` ### 3. Create DI Extension ```csharp using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace StellaOps.Cli.Crypto.MyCustom; public static class ServiceCollectionExtensions { public static IServiceCollection AddMyCustomCryptoProviders( this IServiceCollection services, IConfiguration configuration) { // Register provider services.AddSingleton(); // Bind configuration services.Configure( configuration.GetSection("StellaOps:Crypto:Providers:MyCustom")); return services; } } ``` ### 4. Update StellaOps.Cli.csproj ```xml $(DefineConstants);STELLAOPS_ENABLE_MYCUSTOM ``` ### 5. Update Program.cs ```csharp #if STELLAOPS_ENABLE_MYCUSTOM using StellaOps.Cli.Crypto.MyCustom; #endif // In Main(): #if STELLAOPS_ENABLE_MYCUSTOM services.AddMyCustomCryptoProviders(configuration); #endif ``` ### 6. Build Custom Distribution ```bash dotnet publish src/Cli/StellaOps.Cli \ --configuration Release \ --runtime linux-x64 \ -p:StellaOpsEnableMyCustom=true ``` --- ## Command Routing ### System.CommandLine 2.0 Integration ```csharp // CommandFactory.cs using System.CommandLine; public static class CommandFactory { public static Command CreateRootCommand(IServiceProvider services) { var root = new Command("stella", "StellaOps unified CLI"); // Add command groups root.Add(BuildScanCommand(services)); root.Add(BuildCryptoCommand(services)); root.Add(BuildAdminCommand(services)); root.Add(BuildAuthCommand(services)); // ... more commands return root; } private static Command BuildCryptoCommand(IServiceProvider services) { var crypto = new Command("crypto", "Cryptographic operations"); // crypto providers var providers = new Command("providers", "List available crypto providers"); providers.SetAction(async (parseResult, ct) => { var cryptoProviders = services.GetServices(); foreach (var provider in cryptoProviders) { Console.WriteLine($"- {provider.Name}: {string.Join(", ", provider.SupportedAlgorithms)}"); } return 0; }); crypto.Add(providers); // crypto sign var sign = new Command("sign", "Sign file"); // ... add options and handler crypto.Add(sign); return crypto; } } ``` --- ## Testing ### Unit Tests ```csharp using StellaOps.Cli.Crypto; using StellaOps.Cli.Crypto.Gost; using Xunit; public class GostCryptoProviderTests { [Fact] public void Name_ReturnsGost() { var provider = new GostCryptoProvider(Options.Create(new GostCryptoOptions())); Assert.Equal("gost", provider.Name); } [Fact] public void SupportedAlgorithms_IncludesGost12_256() { var provider = new GostCryptoProvider(Options.Create(new GostCryptoOptions())); Assert.Contains("GOST12-256", provider.SupportedAlgorithms); } [Fact] public async Task SignAsync_ProducesSignature() { var provider = new GostCryptoProvider(Options.Create(new GostCryptoOptions())); var data = "test"u8.ToArray(); var keyRef = new CryptoKeyReference { KeyId = "test-key", Source = "file" }; var signature = await provider.SignAsync(data, "GOST12-256", keyRef); Assert.NotNull(signature); Assert.NotEmpty(signature); } } ``` ### Integration Tests ```csharp using Microsoft.Extensions.DependencyInjection; using Xunit; public class CryptoPluginIntegrationTests { [Fact] public void ServiceProvider_ResolvesAllProviders() { var services = new ServiceCollection(); var configuration = new ConfigurationBuilder().Build(); services.AddDefaultCryptoProviders(configuration); #if STELLAOPS_ENABLE_GOST services.AddGostCryptoProviders(configuration); #endif var serviceProvider = services.BuildServiceProvider(); var providers = serviceProvider.GetServices().ToList(); Assert.NotEmpty(providers); Assert.Contains(providers, p => p.Name == "default"); #if STELLAOPS_ENABLE_GOST Assert.Contains(providers, p => p.Name == "gost"); #endif } } ``` --- ## Packaging ### NuGet Package Structure ``` StellaOps.Cli (metapackage) ├── StellaOps.Cli.Crypto (interfaces) ├── StellaOps.Cli.Crypto.Default (always included) ├── StellaOps.Cli.Crypto.Gost (optional) ├── StellaOps.Cli.Crypto.Eidas (optional) └── StellaOps.Cli.Crypto.Sm (optional) ``` ### Distribution Artifacts ``` releases/ ├── stella-international-linux-x64.tar.gz ├── stella-russia-linux-x64.tar.gz ├── stella-eu-linux-x64.tar.gz └── stella-china-linux-x64.tar.gz ``` Each artifact contains only the authorized crypto providers for that region. --- ## See Also - [Command Reference](command-reference.md) - Complete command documentation - [Crypto Plugin Development](crypto-plugins.md) - Detailed plugin development guide - [Compliance Guide](compliance-guide.md) - Regional compliance requirements - [Distribution Matrix](distribution-matrix.md) - Build and distribution guide - [Troubleshooting](troubleshooting.md) - Common plugin issues