using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ZB.MOM.WW.Secrets.DependencyInjection;
using ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.DependencyInjection;
namespace ZB.MOM.WW.OtOpcUa.Host.Configuration;
///
/// Single registration point for the secrets subsystem on every OtOpcUa node, admin- and
/// driver-role alike. Exists as a named extension rather than an inline AddZbSecrets call
/// in Program.cs so the choice of ISecretStore implementation is covered by a DI
/// resolution test — Program.cs is top-level statements and cannot be exercised directly,
/// which is exactly how a "registered but never resolvable" wiring defect ships unnoticed.
///
public static class SecretsRegistration
{
/// Configuration key gating cluster secret replication. Absent or false = off.
public const string ReplicationEnabledKey = "Secrets:Replication:Enabled";
/// Configuration section holding AkkaSecretsReplicationOptions.
public const string ReplicationSectionPath = "Secrets:Replication";
/// Configuration section holding the core secrets options.
public const string SecretsSectionPath = "Secrets";
///
/// True when cluster secret replication is switched on in configuration. Defaults to false —
/// absent configuration must mean "off".
///
/// The application configuration.
/// true when replication is enabled.
public static bool IsReplicationEnabled(IConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(configuration);
return configuration.GetValue(ReplicationEnabledKey, defaultValue: false);
}
///
/// Registers the secrets subsystem. Replication is opt-in: unless
/// is true this is exactly the plain local SQLite wiring
/// the host has always used.
///
///
///
/// The gate is deliberately default-deny. This call decides which ISecretStore the
/// container hands to every node, including driver-role nodes with no auth or
/// AdminUI surface, where a bad store surfaces as drivers failing to open sessions rather
/// than as a failing test. Enabling replication by default would change secret resolution
/// across a production fleet with nobody having asked for it.
///
///
/// AddZbSecretsAkkaReplication calls AddZbSecrets internally, so the two
/// branches are alternatives, never additive — calling both would double-register.
///
///
/// The service collection to add to.
/// The application configuration.
/// The same instance, for chaining.
public static IServiceCollection AddOtOpcUaSecrets(
this IServiceCollection services,
IConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
if (!IsReplicationEnabled(configuration))
{
services.AddZbSecrets(configuration, SecretsSectionPath);
return services;
}
services.AddZbSecretsAkkaReplication(configuration, SecretsSectionPath, ReplicationSectionPath);
// Anti-entropy participation must not hinge on this node happening to touch a secret — see
// SecretReplicationStarter for why the library's lazy actor creation is insufficient here.
services.AddSingleton();
services.AddHostedService(sp => sp.GetRequiredService());
return services;
}
}