diff --git a/src/ScadaLink.ManagementService/ManagementActor.cs b/src/ScadaLink.ManagementService/ManagementActor.cs
new file mode 100644
index 0000000..e1c1702
--- /dev/null
+++ b/src/ScadaLink.ManagementService/ManagementActor.cs
@@ -0,0 +1,692 @@
+using System.Security.Cryptography;
+using Akka.Actor;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using ScadaLink.Commons.Entities.ExternalSystems;
+using ScadaLink.Commons.Entities.InboundApi;
+using ScadaLink.Commons.Entities.Instances;
+using ScadaLink.Commons.Entities.Notifications;
+using ScadaLink.Commons.Entities.Security;
+using ScadaLink.Commons.Entities.Sites;
+using ScadaLink.Commons.Interfaces.Repositories;
+using ScadaLink.Commons.Messages.Management;
+using ScadaLink.DeploymentManager;
+using ScadaLink.HealthMonitoring;
+using ScadaLink.TemplateEngine;
+using ScadaLink.TemplateEngine.Services;
+
+namespace ScadaLink.ManagementService;
+
+///
+/// Central actor that handles all management commands from the CLI (via ClusterClient).
+/// Receives ManagementEnvelope messages, authorizes based on roles, then delegates to
+/// the appropriate service or repository using scoped DI.
+///
+public class ManagementActor : ReceiveActor
+{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ILogger _logger;
+
+ public ManagementActor(IServiceProvider serviceProvider, ILogger logger)
+ {
+ _serviceProvider = serviceProvider;
+ _logger = logger;
+ Receive(HandleEnvelope);
+ }
+
+ private void HandleEnvelope(ManagementEnvelope envelope)
+ {
+ var sender = Sender;
+ var correlationId = envelope.CorrelationId;
+ var user = envelope.User;
+
+ // Check authorization
+ var requiredRole = GetRequiredRole(envelope.Command);
+ if (requiredRole != null && !user.Roles.Contains(requiredRole, StringComparer.OrdinalIgnoreCase))
+ {
+ sender.Tell(new ManagementUnauthorized(correlationId,
+ $"Role '{requiredRole}' required for {envelope.Command.GetType().Name}"));
+ return;
+ }
+
+ // Process command asynchronously with scoped DI
+ Task.Run(async () =>
+ {
+ using var scope = _serviceProvider.CreateScope();
+ try
+ {
+ var result = await DispatchCommand(scope.ServiceProvider, envelope.Command, user.Username);
+ sender.Tell(new ManagementSuccess(correlationId, result));
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Management command {Command} failed (CorrelationId={CorrelationId})",
+ envelope.Command.GetType().Name, correlationId);
+ sender.Tell(new ManagementError(correlationId, ex.Message, "COMMAND_FAILED"));
+ }
+ });
+ }
+
+ private static string? GetRequiredRole(object command) => command switch
+ {
+ // Admin operations
+ CreateSiteCommand or UpdateSiteCommand or DeleteSiteCommand
+ or CreateAreaCommand or DeleteAreaCommand
+ or ListRoleMappingsCommand or CreateRoleMappingCommand
+ or UpdateRoleMappingCommand or DeleteRoleMappingCommand
+ or ListApiKeysCommand or CreateApiKeyCommand or DeleteApiKeyCommand => "Admin",
+
+ // Design operations
+ CreateTemplateCommand or UpdateTemplateCommand or DeleteTemplateCommand
+ or ValidateTemplateCommand
+ or CreateExternalSystemCommand or UpdateExternalSystemCommand
+ or DeleteExternalSystemCommand
+ or CreateNotificationListCommand or UpdateNotificationListCommand
+ or DeleteNotificationListCommand
+ or UpdateSmtpConfigCommand
+ or CreateDataConnectionCommand or UpdateDataConnectionCommand
+ or DeleteDataConnectionCommand
+ or AssignDataConnectionToSiteCommand
+ or UnassignDataConnectionFromSiteCommand => "Design",
+
+ // Deployment operations
+ CreateInstanceCommand or MgmtDeployInstanceCommand or MgmtEnableInstanceCommand
+ or MgmtDisableInstanceCommand or MgmtDeleteInstanceCommand
+ or SetConnectionBindingsCommand
+ or MgmtDeployArtifactsCommand => "Deployment",
+
+ // Read-only queries -- any authenticated user
+ _ => null
+ };
+
+ private async Task