Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
9cbee2b
feat: distributed scanning
hojmark Sep 21, 2026
b3f891a
bump nugets
hojmark Sep 21, 2026
602a9fa
remove unused package
hojmark Sep 21, 2026
67282d9
f
hojmark Sep 21, 2026
d2a2b78
f
hojmark Sep 21, 2026
0844273
fix tests
hojmark Sep 22, 2026
f99a282
fix build warnings
hojmark Sep 22, 2026
774cd97
fix windows snapshots
hojmark Sep 23, 2026
c2bc845
more fixes
hojmark Sep 23, 2026
eb4bf2f
more fixes
hojmark Sep 23, 2026
3034a0f
more fixes
hojmark Sep 23, 2026
42b9eb2
f
hojmark Sep 23, 2026
2e2864e
f
hojmark Sep 23, 2026
e3c2b46
f
hojmark Sep 23, 2026
42beb23
f
hojmark Sep 23, 2026
93a544a
f
hojmark Sep 28, 2026
cf2479f
extract markdownlint command from ci
hojmark Sep 28, 2026
bbd69f3
markdown lint
hojmark Sep 28, 2026
a10ebd7
tidy
hojmark Sep 29, 2026
979cefd
f
hojmark Sep 29, 2026
9d355d5
refacotr hosts
hojmark Sep 29, 2026
29deec5
f
hojmark Sep 29, 2026
06b3e37
separate service configuration for cli, agent and coordinator
hojmark Sep 29, 2026
c09535d
settings and data location refactor
hojmark Sep 29, 2026
2a16ae7
f
hojmark Sep 29, 2026
2d4f7b6
fix ci
hojmark Sep 29, 2026
233bade
f
hojmark Sep 30, 2026
305fe2d
f
hojmark Sep 30, 2026
49985a3
refactor
hojmark Oct 1, 2026
79a91ef
refactor
hojmark Oct 1, 2026
42bf28e
f
hojmark Oct 1, 2026
d417ddd
f
hojmark Oct 1, 2026
1d540bb
refactor
hojmark Oct 1, 2026
185e9f1
f
hojmark Oct 1, 2026
4a7f564
f
hojmark Oct 2, 2026
c350e1c
f
hojmark Oct 2, 2026
de6c9b6
f
hojmark Oct 2, 2026
bb58fa6
f
hojmark Oct 2, 2026
741554b
clab fixes
hojmark Oct 2, 2026
41887ac
UnsupportedOptionsTests
hojmark Oct 3, 2026
c5940ff
arch tests
hojmark Oct 3, 2026
002fb15
snapshots
hojmark Oct 3, 2026
92c8a6e
f
hojmark Oct 3, 2026
c9f0823
rename
hojmark Oct 3, 2026
06dd7d3
f
hojmark Oct 3, 2026
c89268b
log connection state changes
hojmark Oct 3, 2026
4f809e9
improved docs
hojmark Oct 3, 2026
132e6a1
improved logging
hojmark Oct 3, 2026
89f8843
f
hojmark Oct 4, 2026
6069193
f
hojmark Oct 5, 2026
7b25e56
f
hojmark Oct 5, 2026
0f23da6
f
hojmark Oct 5, 2026
5f736f6
f
hojmark Oct 5, 2026
6d7d5f8
f
hojmark Oct 5, 2026
f2e9f42
f
hojmark Oct 6, 2026
cd1133b
f
hojmark Oct 6, 2026
96e6982
f
hojmark Oct 6, 2026
dd98907
f
hojmark Oct 6, 2026
884cffe
update snapshots
hojmark Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
"trx"
],
"rollForward": false
},
"Microsoft.OpenApi.Kiota": {
"version": "1.35.0",
"commands": [
"kiota"
],
"rollForward": false
}
}
}
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ root = true
indent_style = space

# Code files
[*.{cs,csproj,slnx,props,json}]
[*.{cs,csproj,slnx,props,targets,json}]
indent_size = 2
insert_final_newline = false
charset = utf-8
Expand Down Expand Up @@ -159,6 +159,7 @@ dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion
dotnet_diagnostic.CA1303.severity = none
dotnet_diagnostic.CA1848.severity = none
dotnet_diagnostic.CA1068.severity = warning
# Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# Braces for multi-line statements should not share line
Expand Down Expand Up @@ -225,6 +226,8 @@ dotnet_diagnostic.SA1502.severity = none
dotnet_diagnostic.SA1508.severity = none
# SA1516 Elements should be separated by blank line (but reports false positives)
dotnet_diagnostic.SA1516.severity = none
# SA1201 An element within a C# code file is out of order in relation to the other elements in the code.
dotnet_diagnostic.SA1201.severity = none


# TODO TBD
Expand Down
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.sh text eol=lf
*.png binary
*.png binary
/src/Coordinator.Client/Generated/** linguist-generated=true
5 changes: 5 additions & 0 deletions .github/actions/setup-runner/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ runs:
- name: Restore .NET tools
shell: bash
run: dotnet tool restore

- name: Install Containerlab
if: runner.os == 'Linux'
shell: bash
run: bash -c "$(curl -sL https://get.containerlab.dev)"
21 changes: 19 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ jobs:
--project ./build/_build.csproj
--target CheckBuildWarnings

- name: Check for uncommitted files
shell: bash
run: |
if [[ -n "$(git status --porcelain)" ]]; then
echo "The build changed tracked or untracked files:"
git status --short
exit 1
fi

test-windows:
name: 🪟 Test
runs-on: windows-latest
Expand Down Expand Up @@ -113,6 +122,15 @@ jobs:
--project ./build/_build.csproj
--target CheckBuildWarnings

- name: Check for uncommitted files
shell: bash
run: |
if [[ -n "$(git status --porcelain)" ]]; then
echo "The build changed tracked or untracked files:"
git status --short
exit 1
fi

markdown-lint:
name: Markdown lint
runs-on: ubuntu-latest
Expand All @@ -125,5 +143,4 @@ jobs:

- name: Lint markdown
run: |
npm i -g markdownlint-cli@0.49.1
markdownlint --config .markdownlint.jsonc '**/*.md'
./lint-markdown.sh
10 changes: 6 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ obj/
#*.idea
.idea/.idea.Drift/.idea/watcherTasks.xml
.idea/.idea.Drift/.idea/encodings.xml
.idea/.idea.Drift.Build/.idea/encodings.xml
.air/
*.DotSettings
*.received.*
artifacts/
publish/
TestResults/
build.binlog
build.binlog-warnings-only.log
publish.binlog
publish.binlog-warnings-only.log
*.log
*.binlog
*.binlog-warnings-only.log
containerlab/*/
17 changes: 17 additions & 0 deletions .nuke/build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"CreateRelease",
"CreateWindowsArtifacts",
"DeleteUntaggedImages",
"GenerateSchemas",
"GenerateSettingsSchema",
"GenerateSpecSchema",
"OutputVersion",
"PackBinaries",
"PublishBinaries",
Expand All @@ -58,11 +61,13 @@
"Test",
"TestE2E",
"TestE2E_Binary",
"TestE2E_Clab",
"TestE2E_Container",
"TestE2E_General",
"TestLocal",
"TestSelf",
"TestUnit",
"TestUnitLocal",
"UpdateOui"
]
},
Expand Down Expand Up @@ -137,6 +142,10 @@
"allOf": [
{
"properties": {
"ClabTopology": {
"type": "string",
"description": "Run only this topology (e.g. 'simple-test'). Runs all topologies if not specified"
},
"Commit": {
"type": "string",
"description": "Commit - e.g. '4c16978aa41a3b435c0b2e34590f1759c1dc0763'"
Expand Down Expand Up @@ -167,6 +176,10 @@
"description": "GitHubToken - GitHub token used to create releases",
"default": "Secrets must be entered via 'nuke :secrets [profile]'"
},
"KeepClabRunning": {
"type": "boolean",
"description": "Keep Containerlab topology running after tests"
},
"MsBuildVerbosity": {
"type": "string",
"description": "MsBuildVerbosity - Console output verbosity - Default is 'normal'"
Expand Down Expand Up @@ -218,6 +231,10 @@
"description": "ReleaseType - None (default/safe), PreRelease, or Release",
"$ref": "#/definitions/ReleaseType"
},
"SkipClabDeploy": {
"type": "boolean",
"description": "Skip Containerlab deployment (useful for debugging when topology is already running)"
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
Expand Down
100 changes: 100 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# AGENTS.md

This file provides guidance to AI agents when working with code in this repository.

## Project Overview

Drift is a .NET 10 CLI tool for network drift detection — it compares a declarative YAML spec (desired network state) against live network scanning results and reports differences. It supports distributed scanning via agents communicating over gRPC.

## Build System

The build uses [NUKE](https://nuke.build/). Entry point is `dotnet nuke`.

Common targets:

```sh
dotnet nuke Build # Restore + compile
dotnet nuke TestUnit # Unit tests only (fast)
dotnet nuke Test # All tests (unit + E2E)
dotnet nuke TestE2E # E2E tests (General, Binary, Container image, Container network topologies using Containerlab)
dotnet nuke PublishBinaries # Self-contained binary for the current platform
dotnet nuke BuildContainerImage
```

Run a single test class or filter by name using standard `dotnet test` filters:

```sh
dotnet test src/Domain.Tests --filter "FullyQualifiedName~MyTest"
```

## Architecture

### Source layout (`src/`)

The solution is split into focused projects. The main ones:

| Project | Role |
| --- | --- |
| `Cli` | Entry point; commands: `init`, `scan`, `agent start`; AOT-compiled |
| `Cli.Abstractions` | Shared CLI constants: exit codes, env var names, port numbers, file names |
| `Cli.Settings` | User settings file (`~/.config/drift/settings.json`) |
| `Domain` | Core value types: `Network`, `Device`, `Inventory`, `CidrBlock`, `Port`, `AgentId` |
| `Spec` | YAML spec parsing and validation into declared-state domain types |
| `Scanning` | Network discovery: ARP, ping, port scanning |
| `Diff` | Compares declared spec state vs. discovered scan state to produce a drift report |
| `Networking.Grpc` | Generated gRPC/protobuf contracts for the messaging transport |
| `Networking.Core.Abstractions` | Interfaces for message streams, handlers, and client factories |
| `Networking.Core` | Message stream/manager implementation built on gRPC |
| `Networking.Client` | Default client factory for opening outbound messaging connections |
| `Networking.Server` | Hosts the inbound gRPC service for messaging endpoints |
| `Messaging.Protocol.Agent` | Agent request/response message contracts (e.g. scan, subnets) |
| `Messaging.Client` | Typed agent client built on top of `Networking.Client`/`Networking.Core` |
| `Agent.Host` | Hosts an agent's messaging/gRPC endpoint (Kestrel/ASP.NET Core) |
| `Coordinator.Host` | Coordinator-side host counterpart to `Agent.Host` (work in progress) |
| `Common` | Shared cross-cutting helpers: IO, logging, network utilities, embedded resources |
| `Common.Schemas` | Shared JSON Schema generation helpers (e.g. lowercase enum naming) |
| `Serialization` | Cross-module serialization helpers |
| `TestUtilities` | Shared test helpers (loggers, Verify/snapshot settings) used by `*.Tests` projects |
| `ArchTests` | ArchUnitNET tests enforcing dependency rules and naming conventions |

Schema generators live in `Spec.SchemaGenerator.Cli` and `Cli.Settings.SchemaGenerator.Cli` — they produce JSON Schema from C# types.

`Networking.*` and `Messaging.*` implement the role-agnostic transport layer (see naming rule below); `Agent.Host` and `Coordinator.Host` build role-specific hosting on top of it.

### Data flow

```text
YAML spec → Spec (parse/validate) → Domain types (declared state)
↓
Network → Scanning → Domain types (discovered state)
↓
Diff → Drift report → Cli (render)
```

Agents (remote Drift instances) report discovered state back to the coordinator over gRPC, extending scan coverage across subnets.

### Key conventions

- **Central package management**: all NuGet versions in `Directory.Packages.props`; do not add `Version=` attributes to `<PackageReference>` in individual project files.
- **Shared project defaults**: `Directory.Build.props` applies nullable refs, implicit usings, and logging config to all projects.
- **InternalsVisibleTo**: test projects access internal members for white-box testing; this is intentional.
- **Snapshot testing**: `Verify.NUnit` is used for golden file comparisons. Run tests to regenerate snapshots when output changes; committed `.verified.*` files are the source of truth.
- **AOT**: `Cli` is published with `PublishAot=true`. Avoid reflection-heavy patterns in the CLI project; use source generators instead.
- **Embedded resources**: schemas, default specs, and scripts are embedded in project assemblies under `embedded_resources/`.
- **`Networking.*` are role-agnostic**: No "Agent", "Peer", "Coordinator", or "Server" in type names, property names, parameter names, method names, or log strings inside `Networking.*`. These assemblies implement the transport layer only (streams, messages, connections). Role-specific concerns belong in `Agent.*`, `Coordinator.*`, or `Cli.*`.

## Testing

- **Unit tests**: `*.Tests` projects using NUnit 4 and NSubstitute for mocking.
- **E2E tests**: `Cli.E2ETests.*` projects (`General` install scripts and schemas, `Binary` against the published binary, `Container` against the container image).
- **Containerlab tests**: driven directly by the NUKE build (`build/NukeBuild.TestContainerlab.cs`, target `TestE2E_Clab`) against multi-node topologies — not a `Cli.E2ETests.*` project. Requires Containerlab installed and uses topology files in `containerlab/`.
- **Architecture tests**: `ArchTests` project validates project dependency graph and naming rules.

## Terminology (from domain model)

- **Spec**: declarative YAML definition of desired network state
- **Declared resource**: a device/subnet defined in the spec
- **Discovered resource**: a device/subnet found by scanning
- **Drift**: difference between declared and discovered state
- **Device ID**: one or more addresses (MAC, IPv4, IPv6, hostname) that uniquely identify a device; spec addresses with `is_id: false` are metadata only
- **Agent**: a Drift instance in agent mode that reports scan results to peers
1 change: 1 addition & 0 deletions CLAUDE.md
11 changes: 11 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
</ItemGroup>
<ItemGroup Label="Direct">
<PackageVersion Include="CsvHelper" Version="33.1.0" />
<PackageVersion Include="Google.Protobuf" Version="3.36.2" />
<PackageVersion Include="Grpc.AspNetCore" Version="2.84.0" />
<PackageVersion Include="Grpc.Core.Api" Version="2.84.0" />
<PackageVersion Include="Grpc.Net.Client" Version="2.84.0" />
<PackageVersion Include="Grpc.Tools" Version="2.84.0" />
<PackageVersion Include="HLabs.ImageReferences" Version="1.0.0-preview.3" />
<PackageVersion Include="HLabs.ImageReferences.Extensions.Nuke" Version="1.0.0-preview.3" />
<PackageVersion Include="Humanizer" Version="3.0.10" />
Expand All @@ -18,9 +23,13 @@
<PackageVersion Include="JsonSchema.Net.Generation" Version="7.3.11" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.12" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.12" />
<PackageVersion Include="Microsoft.Kiota.Bundle" Version="2.1.2" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.9.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageVersion Include="NaturalSort.Extension" Version="4.4.1" />
Expand All @@ -39,8 +48,10 @@
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.33.0.1635" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.55.0" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.2.3" />
<PackageVersion Include="System.CommandLine" Version="2.0.12" />
<PackageVersion Include="System.Text.Json" Version="10.0.12" />
<PackageVersion Include="System.Threading.Channels" Version="10.0.11" />
<PackageVersion Include="System.Threading.RateLimiting" Version="10.0.12" />
<PackageVersion Include="Testcontainers" Version="4.15.0" />
<PackageVersion Include="TngTech.ArchUnitNET.NUnit" Version="0.13.4" />
Expand Down
9 changes: 6 additions & 3 deletions Drift.Build.slnx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<!-- https://github.com/microsoft/vs-solutionpersistence/blob/main/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/Xml/Slnx.xsd -->
<Solution>
<Folder Name="/Workflows/">
<File Path=".github\workflows\audit.yaml" />
<File Path=".github\workflows\ci.yaml" />
<File Path=".github\workflows\codeql.yml" />
<File Path=".github\workflows\prerelease.yaml" />
<File Path=".github\workflows\release.yaml" />
<File Path=".github\workflows\renovate.yaml" />
</Folder>
<Folder Name="/Workflows/Actions/" />
<Folder Name="/Workflows/Actions/runner-setup/">
<File Path=".github\actions\setup-runner\action.yml" />
</Folder>
<Project Path="build\_build.csproj" Type="C#" />
<Project Path="build-utils\Build.Utilities.Tests\Build.Utilities.Tests.csproj" Type="C#" />
<Project Path="build-utils\Build.Utilities\Build.Utilities.csproj" Type="C#" />
<Project Path="build\_build.csproj" />
<Project Path="build-utils\Build.Utilities.Tests\Build.Utilities.Tests.csproj" />
<Project Path="build-utils\Build.Utilities\Build.Utilities.csproj" />
</Solution>
Loading
Loading