Skip to content

feat: per-subnet scan profiles (P2-05 / 26.15) — closes P2 batch#18

Merged
CryptoJones merged 1 commit into
mainfrom
sprint/26.15-subnet-profiles
May 27, 2026
Merged

feat: per-subnet scan profiles (P2-05 / 26.15) — closes P2 batch#18
CryptoJones merged 1 commit into
mainfrom
sprint/26.15-subnet-profiles

Conversation

@CryptoJones
Copy link
Copy Markdown
Owner

Summary

Operators can now run aggressive hourly deep scans on critical infrastructure while leaving the guest network on a lazy daily liveness sweep — all from one config, one agent process.

```json
{
"scanner": {
"profiles": [
{ "subnet": "10.0.0.0/24", "scan_interval": "1h", "deep_probe": true },
{ "subnet": "192.168.1.0/24", "scan_interval": "24h" }
],
"scan_interval": "5m",
"timeout": "2s",
"workers": 50
}
}
```

Existing `scanner.subnets` flat-list deployments keep working — mutually exclusive with `profiles`, validated at boot.

Architecture

  • `config.SubnetProfile` with `*bool` fields so a profile can explicitly disable a globally-enabled flag (zero-value bools would be ambiguous).
  • `config.ScannerConfig.Resolve()` flattens `Subnets` + `Profiles` into `[]ResolvedProfile` with every field populated. Agent's runtime path has zero fallback logic.
  • `scanner.Scan(ctx, subnet) → Scan(ctx, subnet, SubnetOptions)`. Scanner-level fields stay as defaults consulted by an internal `resolve()` at the top of Scan.
  • Agent ticks at the shortest per-profile interval. Each profile keeps its own `nextDue` timestamp; only due profiles run on each tick. Housekeeping (prune, change-detect diff, tracker) runs every tick regardless, so zero-profile / watchdog-only deployments still work.
  • Tick safety floor of 1s prevents `"1ns"` typos from CPU-pegging.

Breaking changes (in-tree only)

  • `agent.New(...)` now returns `(*Agent, error)` — `Resolve()` runs at construction so duplicate-subnet / mutually-exclusive errors surface at boot.
  • `scanner.Scan(ctx, subnet)` now `Scan(ctx, subnet, scanner.SubnetOptions{})`. Out-of-tree callers pass `SubnetOptions{}` to retain pre-26.15 behaviour.

Test plan

  • `go test -race -timeout 120s ./...` clean across all packages
  • `golangci-lint run ./...` → 0 issues
  • 7 new config tests cover override semantics, pointer-bool `False()` beating global `True`, mutual exclusion, duplicate rejection, empty-profile rejection, zero-profile happy path
  • Existing scanner + agent suites updated to the new API surface

Closes the P2 operator-feedback batch

All five items shipped across 26.11–26.15:

Sprint Item
26.11 Device-type classifier
26.12 Service / app discovery
26.13 Change detection + webhook/syslog alerts
26.14 Query API beyond bulk export
26.15 Per-subnet scan profiles

🤖 Generated with Claude Code

Operators can now run aggressive hourly deep scans on critical
infrastructure while leaving the guest network on a lazy daily
liveness sweep — all from one config, one agent process.

New config shape:
  scanner:
    profiles:
      - subnet: 10.0.0.0/24
        scan_interval: 1h
        deep_probe: true
        deep_probe_ports: [443, 22, 3306, ...]
      - subnet: 192.168.1.0/24
        scan_interval: 24h
    scan_interval: 5m   # global default for any inherited fields
    timeout: 2s
    workers: 50

Existing scanner.subnets flat-list deployments keep working —
mutually exclusive with profiles, validated at boot.

Implementation:
- config.SubnetProfile with *bool fields so profiles can explicitly
  disable a globally-enabled flag.
- config.ScannerConfig.Resolve() flattens into ResolvedProfile with
  every field populated; agent's runtime has no further fallback.
- scanner.Scan(ctx, subnet) becomes Scan(ctx, subnet, SubnetOptions).
  Scanner-level fields remain as defaults consulted by resolve().
- Agent ticks at the SHORTEST per-profile interval; each profile
  keeps its own nextDue timestamp; only due profiles get scanned on
  each tick. Housekeeping (prune, diff, tracker) runs every tick.
- Tick interval safety floor of 1s prevents 1ns config typos from
  CPU-pegging the agent.
- agent.New now returns (*Agent, error) — Resolve runs at
  construction so duplicate-subnet / mutually-exclusive errors
  surface at boot, not on the first tick.

7 new config tests covering legacy path, override semantics,
pointer-bool False-beats-global-True, duplicate detection,
mutual-exclusion validation, empty-profile rejection, zero-profile
happy path (watchdog-only mode).

Closes the operator-feedback P2 batch — all five items shipped
across 26.11–26.15.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CryptoJones CryptoJones merged commit 2079534 into main May 27, 2026
@CryptoJones CryptoJones deleted the sprint/26.15-subnet-profiles branch May 27, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant