diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/NotificationRecipient.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/NotificationRecipient.cs
index 71ab9d89..055176e7 100644
--- a/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/NotificationRecipient.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/NotificationRecipient.cs
@@ -71,8 +71,9 @@ public class NotificationRecipient
}
///
- /// Parameterless constructor used by EF Core materialization and the SMS factory, where the
- /// contact field is assigned via property setters rather than constructor parameters.
+ /// Private parameterless constructor that backs the factory path,
+ /// where the contact field is assigned via property setters rather than constructor
+ /// parameters — without exposing a half-initialized public constructor to callers.
///
private NotificationRecipient()
{
diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/SmsConfiguration.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/SmsConfiguration.cs
index 7db9f6ad..ececac0f 100644
--- a/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/SmsConfiguration.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Notifications/SmsConfiguration.cs
@@ -6,7 +6,10 @@ public class SmsConfiguration
public int Id { get; set; }
/// Gets or sets the Twilio Account SID.
public string AccountSid { get; set; }
- /// Gets or sets the Twilio Auth Token (secret), or null when not applicable.
+ ///
+ /// Gets or sets the Twilio Auth Token (secret). Stored encrypted; null only transiently
+ /// during configuration, never a valid production value.
+ ///
public string? AuthToken { get; set; }
/// Gets or sets the sender phone number (E.164) placed in the From field.
public string FromNumber { get; set; }
diff --git a/src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs b/src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs
index b00b5cfd..19d6828c 100644
--- a/src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs
@@ -179,7 +179,7 @@ public class ArtifactDeploymentService
// Map notification lists
var notificationListArtifacts = notificationLists.Select(nl =>
- new NotificationListArtifact(nl.Name, nl.Recipients.Select(r => r.EmailAddress).ToList())).ToList();
+ new NotificationListArtifact(nl.Name, nl.Recipients.Where(r => r.EmailAddress is not null).Select(r => r.EmailAddress!).ToList())).ToList();
// Map SMTP configurations — use Host as the artifact name (matches SQLite PK on site)
var smtpArtifacts = smtpConfigurations.Select(smtp =>
diff --git a/src/ZB.MOM.WW.ScadaBridge.NotificationOutbox/Delivery/EmailNotificationDeliveryAdapter.cs b/src/ZB.MOM.WW.ScadaBridge.NotificationOutbox/Delivery/EmailNotificationDeliveryAdapter.cs
index 97baed46..57d3ec4f 100644
--- a/src/ZB.MOM.WW.ScadaBridge.NotificationOutbox/Delivery/EmailNotificationDeliveryAdapter.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.NotificationOutbox/Delivery/EmailNotificationDeliveryAdapter.cs
@@ -106,7 +106,10 @@ public sealed class EmailNotificationDeliveryAdapter : INotificationDeliveryAdap
return DeliveryOutcome.Permanent(addressError);
}
- var recipientAddresses = recipients.Select(r => r.EmailAddress).ToList();
+ var recipientAddresses = recipients
+ .Where(r => r.EmailAddress is not null)
+ .Select(r => r.EmailAddress!)
+ .ToList();
try
{
diff --git a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Repositories/SiteNotificationRepository.cs b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Repositories/SiteNotificationRepository.cs
index 5fea2b96..e3fb966a 100644
--- a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Repositories/SiteNotificationRepository.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Repositories/SiteNotificationRepository.cs
@@ -193,6 +193,24 @@ public class SiteNotificationRepository : INotificationRepository
public Task DeleteSmtpConfigurationAsync(int id, CancellationToken cancellationToken = default)
=> throw new NotSupportedException("Managed via artifact deployment from Central");
+ // ── SmsConfiguration (central-only — never deployed to or served from the site) ──
+
+ ///
+ public Task GetSmsConfigurationAsync(CancellationToken cancellationToken = default)
+ => throw new NotSupportedException("Managed via artifact deployment from Central");
+
+ ///
+ public Task> GetAllSmsConfigurationsAsync(CancellationToken cancellationToken = default)
+ => throw new NotSupportedException("Managed via artifact deployment from Central");
+
+ ///
+ public Task AddSmsConfigurationAsync(SmsConfiguration smsConfiguration, CancellationToken cancellationToken = default)
+ => throw new NotSupportedException("Managed via artifact deployment from Central");
+
+ ///
+ public Task UpdateSmsConfigurationAsync(SmsConfiguration smsConfiguration, CancellationToken cancellationToken = default)
+ => throw new NotSupportedException("Managed via artifact deployment from Central");
+
///
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
=> throw new NotSupportedException("Managed via artifact deployment from Central");
diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntitySerializer.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntitySerializer.cs
index 579182e9..e5f7807b 100644
--- a/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntitySerializer.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntitySerializer.cs
@@ -138,9 +138,11 @@ public sealed class EntitySerializer
NotificationLists: aggregate.NotificationLists.Select(nl => new NotificationListDto(
Name: nl.Name,
Type: nl.Type,
- Recipients: nl.Recipients.Select(r => new NotificationRecipientDto(
- Name: r.Name,
- EmailAddress: r.EmailAddress)).ToList())).ToList(),
+ Recipients: nl.Recipients
+ .Where(r => r.EmailAddress is not null)
+ .Select(r => new NotificationRecipientDto(
+ Name: r.Name,
+ EmailAddress: r.EmailAddress!)).ToList())).ToList(),
SmtpConfigs: aggregate.SmtpConfigurations.Select(smtp =>
{
SecretsBlock? secrets = null;