Passive scanner architectureΒΆ

The architecture behind the πŸ™ Passive scanner spike @GitHub: its components, how data moves through them, and who is responsible for what. The aim is a spike that can grow without being rebuilt, while the Desk β†’ Forge β†’ Scanner workflow stays intact.

PrinciplesΒΆ

  • The fingerprint logic is immutable design input. fingerprint.yaml defines the probes and the match logic, and that layer does not change when a provider is swapped underneath it.

  • Providers are replaceable services. External datasets (Netlas, Censys, and the rest) are reached through one defined interface.

  • The evaluator is deterministic. Match logic applies to observations and produces no side effects.

  • The controller orchestrates rather than decides. scan.py sequences the flow; it does not implement probe logic.

Directory structureΒΆ

The top-level layout maps straight onto the subsystems:

passive-scanner/
β”œβ”€β”€ scan.py                  # CLI & orchestration
β”œβ”€β”€ fingerprint/
β”‚   β”œβ”€β”€ loader.py            # fingerprint.yaml β†’ internal model
β”‚   └── model.py             # Probe, MatchLogic, Fingerprint classes
β”œβ”€β”€ providers/
β”‚   β”œβ”€β”€ base.py              # Abstract search provider interface
β”‚   β”œβ”€β”€ netlas.py            # Netlas implementation
β”‚   └── censys.py            # Optional Censys implementation
β”œβ”€β”€ engine/
β”‚   β”œβ”€β”€ planner.py           # Probe β†’ provider query translation
β”‚   β”œβ”€β”€ evaluator.py         # Apply match_logic
β”‚   └── evidence.py          # Normalize and store evidence
β”œβ”€β”€ io/
β”‚   β”œβ”€β”€ targets.py           # Load IPs / netblocks
β”‚   └── output.py            # JSONL results writer
└── FINDINGS.md              # Spike results & limitations

Component responsibilitiesΒΆ

Fingerprint layerΒΆ

  • loader.py: parses fingerprint.yaml into internal data structures.

  • model.py: defines the Probe, Fingerprint, and MatchResult objects.

  • The job: a tool-agnostic representation of probes and match logic.

ProvidersΒΆ

  • base.py: the abstract class defining search_probe(probe, targets).

  • netlas.py: the Netlas-specific queries.

  • censys.py: an optional fallback provider.

  • The job: return observations per probe, and leave the match logic well alone.

PlannerΒΆ

  • planner.py: maps probes to provider query syntax.

  • For example: http.headers.server:"DeviceOS/2.1.4"

  • The job: keep the API-specific logic in one place.

Evaluation engineΒΆ

  • evaluator.py: applies match_logic to grouped observations.

  • evidence.py: structures the evidence per IP for JSONL output.

  • The job: deterministic, testable logic evaluation.

CLI / controllerΒΆ

  • scan.py: loads fingerprints, reads targets, calls planner, provider, and evaluator in turn, and emits results.

  • The job: glue, and no logic decisions of its own.

IOΒΆ

Not wired into the spike yet. It is for later.

  • targets.py: handles IP and netblock input.

  • output.py: emits JSONL lines: <timestamp>, <ip>, <match_result>, <evidence_snippet>.

Data flowΒΆ

  1. scan.py loads fingerprint.yaml and targets.txt.

  2. planner.py turns each probe into provider-specific queries.

  3. The provider runs the queries and returns raw observations.

  4. evaluator.py groups observations per IP, applies match_logic, and produces a MatchResult.

  5. output.py writes JSONL with the matched IPs and the evidence.

No packets are sent. This is passive, internet-facing scanning.

Spike success criteriaΒΆ

  • The CLI tool runs and consumes one fingerprint.yaml.

  • It queries Netlas for 100 test IPs and outputs structured results.

  • The findings are documented, API limits, data quality, and gaps included.

  • The end-to-end flow is verified, with no assumptions made about full scale or active scanning.

Scaling considerationsΒΆ

  • More providers: Netlas β†’ Censys β†’ local datasets, eventually.

  • Caching and pagination: wrap the providers.

  • Parallelisation: a later enhancement; the controller stays as it is.

  • Bulk fingerprints: the evaluator is untouched, the planner handles the translation.

  • Active probes: a separate provider module, with no effect on the fingerprint model.

Non-goals, for the current spikeΒΆ

  • GUI

  • Concurrent fingerprint evaluation

  • Active probing

  • Optimisation for speed

  • Full error handling

This is the backbone for the passive-scanner spike: testable, modular, and ready to grow a piece at a time. Last updated: 01 June 2026