================================================================================
RMM (Remote Monitoring & Management) - Complete Project Reference
Built with ASP.NET Core 8, SignalR, SQLite, Entity Framework Core
================================================================================
SOLUTION STRUCTURE
==================
RmmSolution.sln
src/RmmAgent/ - Console agent (runs on managed machines)
src/RmmServer/ - ASP.NET Core web server + dashboard
Controllers/ - REST API controllers
Data/ - EF Core DbContext
Hubs/ - SignalR hub
Models/ - Entity models
wwwroot/ - Static dashboard (index.html)
FEATURES
========
- Real-time metrics (CPU, RAM, disk, network, uptime) via SignalR
- Remote command execution (terminal in browser)
- File browser with file download
- Remote desktop (Windows agents) - JPEG screen streaming + mouse/keyboard
- Alerts (auto-generated on high CPU/RAM/disk)
- Device management (add, remove, remove offline)
- Agent runs silently (WinExe, no console window, auto-starts on login)
- Self-contained agent binaries for Linux/Windows/macOS (~66MB each)
NuGet PACKAGES
==============
RmmServer:
- Microsoft.EntityFrameworkCore.Sqlite
- Microsoft.EntityFrameworkCore.Design
- Microsoft.AspNetCore.SignalR (built-in to ASP.NET Core 8)
RmmAgent:
- Microsoft.AspNetCore.SignalR.Client 8.0.11
- System.Drawing.Common 8.0.0 (screen capture on Windows)
================================================================================
FILE: RmmSolution.sln
================================================================================
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RmmServer", "src\RmmServer\RmmServer.csproj", "{SERVER-GUID}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RmmAgent", "src\RmmAgent\RmmAgent.csproj", "{AGENT-GUID}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
EndGlobal
================================================================================
FILE: src/RmmAgent/RmmAgent.csproj
================================================================================
WinExe
net8.0
enable
enable
Build self-contained single-file binaries:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish/win
dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -o publish/linux
dotnet publish -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -o publish/osx
================================================================================
FILE: src/RmmAgent/Program.cs
================================================================================
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.SignalR.Client;
string serverUrl;
var configFile = Path.Combine(AppContext.BaseDirectory, "rmm-agent.conf");
// Priority: command-line arg > saved config > first-run interactive prompt
if (args.Length > 0)
{
serverUrl = args[0].Trim();
File.WriteAllText(configFile, serverUrl);
RegisterStartup();
}
else if (File.Exists(configFile))
{
serverUrl = File.ReadAllText(configFile).Trim();
RegisterStartup();
}
else
{
// First run: temporarily create a console for URL input, then hide it
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
NativeWin32.AllocConsole();
Console.WriteLine("===========================================");
Console.WriteLine(" RMM Agent - First Time Setup");
Console.WriteLine("===========================================");
Console.WriteLine();
Console.Write("Enter your RMM dashboard URL (e.g. https://your-dashboard.replit.app): ");
serverUrl = Console.ReadLine()?.Trim() ?? "";
while (string.IsNullOrEmpty(serverUrl))
{
Console.Write("URL cannot be empty: ");
serverUrl = Console.ReadLine()?.Trim() ?? "";
}
File.WriteAllText(configFile, serverUrl);
RegisterStartup();
Console.WriteLine("Agent configured. Running silently in background...");
await Task.Delay(1500);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
NativeWin32.FreeConsole();
}
var agentId = Environment.GetEnvironmentVariable("RMM_AGENT_ID") ?? Guid.NewGuid().ToString("N")[..12];
var connection = new HubConnectionBuilder()
.WithUrl($"{serverUrl}/hubs/agent")
.WithAutomaticReconnect()
.Build();
CancellationTokenSource? rdCts = null;
// ---- Handler: Execute shell command sent from dashboard ----
connection.On("ExecuteCommand", async (commandId, commandText) =>
{
try
{
var (shell, args2) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? ("cmd.exe", $"/c {commandText}")
: ("/bin/sh", $"-c \"{commandText.Replace("\"", "\\\"")}\"");
var psi = new ProcessStartInfo(shell, args2)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var proc = Process.Start(psi)!;
var output = await proc.StandardOutput.ReadToEndAsync();
var error = await proc.StandardError.ReadToEndAsync();
await proc.WaitForExitAsync();
await connection.InvokeAsync("SendCommandResult", new
{
CommandId = commandId,
Output = output,
Error = error,
ExitCode = proc.ExitCode
});
}
catch (Exception ex)
{
await connection.InvokeAsync("SendCommandResult", new
{
CommandId = commandId,
Output = "",
Error = ex.Message,
ExitCode = -1
});
}
});
// ---- Handler: Browse directory ----
connection.On("BrowseDirectory", async (requestId, path) =>
{
try
{
var target = string.IsNullOrEmpty(path) ? GetDefaultRoot() : path;
var entries = new List