Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
2a6f00b
feat: distributed scanning
hojmark Sep 21, 2026
3d91719
bump nugets
hojmark Sep 21, 2026
c91c0fb
remove unused package
hojmark Sep 21, 2026
7affd26
f
hojmark Sep 21, 2026
85d612f
f
hojmark Sep 21, 2026
c07b383
fix tests
hojmark Sep 22, 2026
da40354
fix build warnings
hojmark Sep 22, 2026
c820df3
fix windows snapshots
hojmark Sep 23, 2026
87254e5
more fixes
hojmark Sep 23, 2026
710bf58
more fixes
hojmark Sep 23, 2026
8e11b8f
more fixes
hojmark Sep 23, 2026
b3a1dfa
f
hojmark Sep 23, 2026
a6fab35
f
hojmark Sep 23, 2026
1aff915
f
hojmark Sep 23, 2026
90c36f6
f
hojmark Sep 23, 2026
f74b00c
f
hojmark Sep 28, 2026
a98f0e5
extract markdownlint command from ci
hojmark Sep 28, 2026
53d1844
markdown lint
hojmark Sep 28, 2026
e838ba4
tidy
hojmark Sep 29, 2026
7d8c1ba
f
hojmark Sep 29, 2026
5cdbbea
refacotr hosts
hojmark Sep 29, 2026
16d4d1e
f
hojmark Sep 29, 2026
64eebbc
separate service configuration for cli, agent and coordinator
hojmark Sep 29, 2026
ede4a0e
settings and data location refactor
hojmark Sep 29, 2026
b96bebb
f
hojmark Sep 29, 2026
ba6c990
fix ci
hojmark Sep 29, 2026
094fc1f
f
hojmark Sep 30, 2026
b7e6f61
f
hojmark Sep 30, 2026
5c9c178
refactor
hojmark Oct 1, 2026
893eb29
refactor
hojmark Oct 1, 2026
b144cac
f
hojmark Oct 1, 2026
6f895d7
f
hojmark Oct 1, 2026
286d4a3
refactor
hojmark Oct 1, 2026
f137f9c
f
hojmark Oct 1, 2026
76a3e65
f
hojmark Oct 2, 2026
8bbf211
f
hojmark Oct 2, 2026
0bbcd6a
f
hojmark Oct 2, 2026
4868e2a
f
hojmark Oct 2, 2026
60f25a1
clab fixes
hojmark Oct 2, 2026
0ac927c
UnsupportedOptionsTests
hojmark Oct 3, 2026
e634542
arch tests
hojmark Oct 3, 2026
0ce46c0
snapshots
hojmark Oct 3, 2026
acd4852
f
hojmark Oct 3, 2026
54d6024
rename
hojmark Oct 3, 2026
ff4cf80
f
hojmark Oct 3, 2026
d3f9a90
log connection state changes
hojmark Oct 3, 2026
594a396
improved docs
hojmark Oct 3, 2026
243bea5
improved logging
hojmark Oct 3, 2026
eee6972
f
hojmark Oct 4, 2026
8ad4d52
f
hojmark Oct 5, 2026
22b8b1a
f
hojmark Oct 5, 2026
afe886d
f
hojmark Oct 5, 2026
0867cac
f
hojmark Oct 5, 2026
3b08d1e
f
hojmark Oct 5, 2026
8952b7b
f
hojmark Oct 6, 2026
4193953
f
hojmark Oct 6, 2026
c1da4ab
f
hojmark Oct 6, 2026
8d54e88
f
hojmark Oct 6, 2026
064085a
update snapshots
hojmark Oct 6, 2026
9e6d2e1
AgentConnectionMonitor + improved connection state
hojmark Oct 9, 2026
020aeec
snapshots
hojmark Oct 9, 2026
ad1d6b0
f
hojmark Oct 9, 2026
cc0b8cc
f
hojmark Oct 9, 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
13 changes: 12 additions & 1 deletion 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,11 +23,15 @@
<PackageVersion Include="JsonSchema.Net.Generation" Version="7.4.0" />
<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.10.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
<PackageVersion Include="NaturalSort.Extension" Version="4.4.1" />
<PackageVersion Include="NSubstitute" Version="6.2.0" />
<PackageVersion Include="Nuke.Common" Version="10.1.0" />
Expand All @@ -39,8 +48,10 @@
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.35.0.4138" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.57.2" />
<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