Assessor
The assessor turns the Findings produced by a scan into a structured
Assessment: per-dimension score, a completeness flag, and a list of
per-rule rationales that cite the Findings they drew their verdict
from.
This document covers: running the assessor, reading the output, and how to extend the wand rule set.
Inspired by
Wanderer's first-party rule pack is named wand (Wanderer-NL). Its rule semantics were inspired by the Dutch government's publicly available Toetsingsinstrument Soevereiniteit Clouddiensten, published by DICTU (Dienst ICT Uitvoering, EZK). The rule authoring, the implementation, and the ongoing maintenance are Conduction's; DICTU does not endorse, certify, or otherwise sanction Wanderer or the wand pack. See ADR-0011 for the rationale behind the rename.
Running it
From the CLI:
wanderer assess <scan-id> [--framework wand|eucsf|both] [--format text|markdown|json] [--db wanderer.db] [--persist=false]
--frameworkdefaults towand.eucsfruns the EU Cybersecurity Framework / SEAL pack instead;bothruns both packs and persists one Assessment record per framework, tagged onAssessment.Framework.dictuis accepted as a deprecated alias forwandfor one release (it prints a stderr deprecation warning).--formatdefaults totext. Markdown is the intended operator- facing format; JSON is for tooling.--persist=falsesuppresses the side-effect of writing the assessment to the store — useful for dry runs.
Over HTTP:
curl -XPOST http://localhost:8080/scans/<scan-id>/assessments
curl http://localhost:8080/assessments/<assessment-id>
POST always persists and always returns the assessment it just
produced. Each POST creates a new record — previous assessments for
the same scan remain retrievable by their own IDs.
Reading a score
Each wand dimension receives one of:
| Score | Meaning |
|---|---|
soeverein |
The best verdict the rule set issues for this dimension. |
voldoende |
Adequate. No red flags, but not actively sovereign. |
afhankelijk |
Low sovereignty — dependency on a non-sovereign party. |
onbekend |
No evidence-backed verdict could be reached. |
The dimension score is the worst score across every
evidence-backed rule in that dimension. One rule scoring afhankelijk
drags the whole dimension down even if four others scored soeverein
— because one material dependency is one material dependency.
Next to the score, a Completeness flag tells you how much of the
rule set was answerable:
| Completeness | Meaning |
|---|---|
complete |
Every rule in this dimension had evidence to work with. |
partial |
Some rules evaluated; others did not because data was absent. |
incomplete |
No rule in this dimension had any evidence. |
A dimension with no rules at all (for example, mens in the current
rule set) is rendered as n/a in the summary table.
Rule set (MVP)
The wand rule set today is deliberately small. Each rule is a Go
function in internal/assessor/wand/rules.go.
| Rule ID | Dimension |
|---|---|
wand.juridisch.cert_issuer_eea |
juridisch |
wand.juridisch.apex_ip_eea |
juridisch |
wand.juridisch.mx_vendor_jurisdiction |
juridisch |
wand.juridisch.registrar_jurisdiction |
juridisch |
wand.operationeel.cert_validity |
operationeel |
wand.operationeel.dns_redundancy |
operationeel |
wand.operationeel.caa_restricts_issuance |
operationeel |
wand.technologie.third_parties_eea |
technologie |
wand.technologie.no_us_hyperscaler |
technologie |
wand.data_ai.mx_present |
data_ai |
wand.data_ai.oidc_federation |
data_ai |
The oidc_federation rule always returns no evidence until the
egress probe lands — it is listed so the reader sees the future
coverage the dimension is waiting on.
The mens dimension has no rules. It appears in the output as
onbekend (n/a). This is explicit, not an omission: the MVP scanner
observes perimeter posture, not human processes.
EU CSF / SEAL framework
internal/assessor/eucsf ships the SEAL pack — a five-level
sovereignty scale (SEAL0–SEAL4) over the same Findings the wand pack
consumes. The two packs share no rule code; --framework both runs
each independently and persists one Assessment per framework.
| Level | Meaning |
|---|---|
| SEAL0 | No evidence-backed verdict could be produced (no relevant Findings). |
| SEAL1 | Verdict that fails the framework outright — clear non-EU exposure. |
| SEAL2 | Verdict with notable dependence on a non-EU party. |
| SEAL3 | Verdict that is adequate — minor or low-impact dependencies only. |
| SEAL4 | Full sovereignty — every checked surface is EU-resident. |
The MVP rules are intentionally narrow:
| Rule ID | What it checks |
|---|---|
eucsf.sov2.cert_issuer_eu |
The leaf certificate's issuer country is in the EEA (uses tls.issuer.issuer_country). |
eucsf.sov2.apex_jurisdiction |
The apex domain's resolved IPs sit in EEA jurisdictions (uses ip.asn.country joined to dns.a/aaaa). |
eucsf.sov3.mx_jurisdiction |
All MX hosts resolve to EEA jurisdictions (uses dns.mx + ip.asn). |
eucsf.sov4.operational_eu |
DNS authority and TLS termination are operated from EEA-resident infrastructure. |
eucsf.sov6.no_us_hyperscaler |
Apex and discovered third parties are not hosted on a US hyperscaler (AWS / GCP / Azure / Cloudflare). |
The SEAL level for a dimension is the worst rule outcome that contributed to it; absent evidence collapses to SEAL0 rather than silently inflating the score. This mirrors the wand "worst wins" collapse — a reader who sees SEAL3 knows every rule is at SEAL3 or better, not "on average".
A rule that consults Findings the scanner did not produce returns
SEAL0 with a Verdict naming what was missing. Operators who want
to lift a SEAL0 verdict to a real one need to look at the missing
ProbeID rather than re-running the assessor.
ADR-0009 records the wand/SEAL dual-framework choice; the rationale is that the two stakeholder groups (Dutch sovereignty reviewers and EU CSF reviewers) ask the same evidence questions but want different answer shapes, and we prefer to ship the second shape rather than translate at read time.
Evidence and auditability
Every rationale entry cites one or more Finding.ID values in its
Evidence field. The markdown report renders these as:
Evidence: f_abc, f_def
A reader who wants to verify the verdict can pull those findings out
of the scan and inspect their Attributes and Evidence fields — the
raw source material the probe captured. Two reviewers running the
assessor on the same stored scan will get the same verdicts,
rationales, and evidence lists, modulo assessment ID and timestamp.
Extending the rule set
Rules are Go functions, not a DSL. To add one:
- Add a function to
internal/assessor/wand/rules.gothat returns anassessor.Rulewith a stableID(prefixwand.<dimension>.), aDimension, a one-lineDescription, aRationale, and aMatchfunction. - Wire it into
DefaultRules. - Add a test in
rules_test.go. The pattern is: fabricate the minimal set of Findings the rule needs, callr.Match, assert onScoreandEvidence.
Rules MUST be total. On missing attributes, return a no-evidence
result (Score: onbekend, empty Evidence, Verdict explaining
what was missing) rather than panicking. The engine's panic recovery
is a safety net, not a design.
See ADR-0004 for why rules are Go functions and not a hot-reloadable DSL.