Repository navigation
Development
Getting the agent building, tested and running on a development machine.
git clone https://github.com/reportmate/reportmate-client-win.git
cd reportmate-client-winYou need PowerShell 7+, the .NET 10 SDK, and Windows. osquery is only needed if you want a module to return real rows.
Build once through the script — this is not optional:
.\build.ps1 -NoSign -SkipMSI -SkipNUPKG -SkipZIP -Configuration Releasebuild.ps1 generates src/Generated/ModuleVersions.g.cs from git history
before compiling. A bare dotnet build on a clean checkout fails because that
file does not exist yet. Once it has been generated, incremental
dotnet build src/ReportMate.WindowsClient.csproj works normally.
dotnet test tests\ReportMate.WindowsClient.Tests\ReportMate.WindowsClient.Tests.csproj --configuration ReleaseThe suite is unit-level and covers the parts where a silent wrong answer is expensive: settings precedence, the trusted-settings-file check, credential migration into the protected store, Prefs elevation and which settings a save writes, idempotency key derivation, unified payload bundling, transmission log retention, bounded child processes, hardware serial selection, storage volume letters and the storage-analysis cache, DNS-correlated management addresses, install event messages, usage launch counting, tracker merging and app-name resolution, monitor EDID parsing, USB serial matching for graphics tablets, policy registry grouping, managed log survey parsing, run-log location for the app, source-OS markers, and TPM manufacturer NUL stripping.
Fixtures live in tests/ReportMate.WindowsClient.Tests/Fixtures/ and are
hand-authored. Do not add captured endpoint data as a fixture — provenance from
a real machine is not a virtue here, and it is not publishable.
The test project uses InternalsVisibleTo from the client, so internal helpers
are directly testable.
| Path | What is in it |
|---|---|
src/Program.cs |
Entry point, CLI, configuration layering, DI |
src/Configuration/ |
Settings layering (SettingsLoader), trusted-file check, typed settings |
src/Shared/ |
Code compiled into both the runner and the app: registry key paths, credential store, legacy-key migration, Prefs rules, log reading |
src/Models/Modules/ |
The payload shape, one file per module |
src/Models/JsonContext.cs |
Source-generated System.Text.Json context |
src/Services/ |
osquery, API, WMI, PowerShell, EDID, usage, logging |
src/Services/Modules/ |
One processor per collection module |
src/App/ |
The Managed Reports Runner App (WinUI 3). Built by build.ps1, not in the solution |
usagetracker/ |
Per-user session tracker |
build/resources/osquery/ |
Query packs and enabled-modules.json
|
build/resources/ |
module-schedules.json, task install and uninstall scripts, the Cimian postflight, Speedtest CLI |
build/pkg/, build/nupkg/
|
cimipkg package projects and install scripts |
tests/ |
xUnit tests and fixtures |
Collect a single module and print it without touching the network:
dotnet run --project src/ReportMate.WindowsClient.csproj -- --run-module system --collect-only -vvvOr run the published binary directly:
.\.publish\managedreportsrunner.exe --run-module network --collect-only -vvvRun from an elevated shell. Credentials live in a registry key only an elevated
process can read, so an unelevated run sends no passphrase or API key. Modules
that read the registry under HKLM, query WMI, or read the Security event log
need elevation too. Several also behave differently as SYSTEM
than as an interactive user — the displays code exists precisely because
HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY returns nothing to osquery running
as SYSTEM. If a result looks wrong, check which identity you are running under
before assuming a bug.
The --collect-only output is also written to
C:\ProgramData\ManagedReports\cache\<timestamp>\<module>.json, which is
usually easier to read than terminal output.
There are four coupled pieces:
-
build/resources/osquery/modules/<id>.json— the query pack. -
src/Models/Modules/<Name>Models.cs— the model, deriving fromBaseModuleData. -
src/Services/Modules/<Name>ModuleProcessor.cs— deriving fromBaseModuleProcessor<T>, implementingProcessModuleAsyncand optionallyGenerateEventsAsyncandValidateModuleDataAsync. - Registration in three places:
ConfigureServices()inProgram.cs, both dictionaries inModuleProcessorFactory, and themoduleFileMapinbuild.ps1so the module gets a version.
Add the id to enabled-modules.json, to module-schedules.json if it should
run on a schedule, and to ReportMateConstants.AllModules in src/App/ so the
app's Run tab lists it. Add the property to UnifiedDevicePayload and to the
JSON source-generation context.
JSON names are mostly not written by hand. Apart from the peripherals model,
the Cimian session records in InstallsModels.cs and two usage fields in
ApplicationsModels.cs, the module models carry no [JsonPropertyName]
attributes — the serializer's camelCase naming policy derives the name from the
C# property, so TotalPhysical becomes
totalPhysical and ScopeID becomes scopeID. Renaming a property renames a
payload field.
The project is published trimmed, so reflection-heavy code needs care.
Serilog and System.Management are already rooted against the trimmer.
- Branch from
origin/mainas<type>/<slug>, type beingfeature,fix,chore,ciordocs. Never pushmaindirectly. - Plain imperative commit subjects. No emoji, no bracketed tags, no
Co-Authored-Bytrailers. - Track work with GitHub issues in this repo; close from a pull request with
Closes #<n>. - Never commit collected endpoint data,
.envfiles, keys or certificates.
MIT licensed. reportmate.app