Motore dati dell'Osservatorio Nazionale Sovranità Digitale. Classifica, tramite analisi DNS pubblica (record MX, SPF, DKIM, CNAME), chi gestisce la posta elettronica di ~23.000 enti della Pubblica Amministrazione italiana (registrati in IndicePA) e ne misura la sovranità: italiana, UE o extra-UE soggetta al CLOUD Act statunitense. Ne produce una mappa interattiva, statistiche, un report e dati aperti.
🌐 mxmap.it · 📊 Statistiche · 📄 Report · 📖 Metodologia ·
Nota. Questo repo nasce come fork mondiale (166 paesi) di mxmap.ch; dal 2026 il focus attivo è l'Italia, come motore dell'Osservatorio. L'infrastruttura multi-paese resta disponibile e documentata (vedi
CLAUDE.md). Questo README descrive il progetto Italia.
| MxMap (questo repo) | Il motore dati. Misura e classifica; produce data.json + artefatti pubblici statici alla root del deploy. |
| Osservatorio Nazionale Sovranità Digitale | Il livello di presentazione/advocacy (sito Hugo). Scarica e pesca i nostri artefatti (kpi.json, report.json) e li renderizza per gli stakeholder. |
Il confine è netto: qui si misura, lì si racconta. Il contratto sono file statici (no API, no auth, CC BY-SA 4.0).
flowchart TD
ipa[/"IndicePA (anagrafe PA)"/] --> seed["fetch_indicepa.py<br/>seed + correzioni a 6 tier"]
seed --> aoo["enrich AOO/UO,<br/>recover, finalize"]
aoo --> dns["preprocess: DNS<br/>MX·SPF·DKIM·CNAME·ASN"]
dns --> classify["classify: provider<br/>+ gateway look-through"]
classify --> cleanup["cleanup + backfill<br/>+ aggregate scuole"]
cleanup --> geo["enrich_geo: regione/provincia<br/>da crosswalk ISTAT"]
geo --> conf["compute_confidence:<br/>sovranità + ISD (ESORICS)"]
conf --> build["build: frontend · dataset ·<br/>stats · kpi · report"]
build --> art["artefatti pubblici<br/>(root del deploy)"]
art --> deploy["upload-pages-artifact<br/>→ deploy (disaccoppiato dal git)"]
conf -. "gated fino al run #1" .-> hist["historicize · dcat<br/>(serie storiche)"]
style ipa fill:#fff8e6,stroke:#E8A33D
style art fill:#e1f5ee,stroke:#009246
style deploy fill:#e1f5ee,stroke:#009246
style hist fill:#f1efe8,stroke:#888,stroke-dasharray:4
Tre stadi classici (preprocess → postprocess → validate) operano su data.json, poi una
coda deterministica lo arricchisce e ne deriva gli artefatti pubblici. Dettaglio in
CLAUDE.md e methodology.html.
La classificazione canonica è sovereignty_of() / material_row() in
src/mail_sovereignty/historicize.py — unica fonte di
verità, riusata da stats, kpi e report.
- 6 bucket MxMap:
USA (CLOUD Act)·Altri provider esteri·Italia — Cloud sovrano·Italia — Provider commerciali·Italia — Infrastruttura autonoma·Sconosciuto. - 4 bucket Osservatorio (
kpi.provider_to_sov4):extra_eu(USA + esteri non europei) ·eu_non_it(provider europei non italiani: OVH, Hetzner, IONOS, Scaleway, Gandi, Infomaniak; CH/UK contati come europei) ·it·unknown. - ISD — Indice di Sovranità Digitale: % enti in giurisdizione italiana, sui classificati, basato sulla sovranità del provider (controllo legale).
mx_jurisdiction(dove risiede l'MX) è l'indicatore tecnico complementare — lo scarto fra i due è esso stesso un dato.
- Per gruppo — 15 cluster citizen (Comuni, Istruzione, Sanità, …) dal codice categoria del seed. Copertura totale. → sezione "Analisi per gruppi" del report.
- Per area — regione/provincia/macroarea da
enrich_geo(crosswalk ufficiale ISTAT sulla chiave-sedeipa_codice_comune_istat): 20/20 regioni, 100% di copertura. →stats.compute_by_regionproducedata/summary/stats_by_region.jsone alimenta la sezione "Analisi per aree" del report (classifica regionale per ISD, sintesi per macroarea, regione più sovrana / più esposta).
| File / pagina | Cosa |
|---|---|
kpi.json |
KPI aggregati (sovranità 4-bucket, top provider, per cluster). Fetchato dall'Osservatorio. |
report.json |
Il report strutturato (sintesi, fotografia, settori, aree, andamento, metodologia). |
statistiche.html |
Cruscotto KPI live. |
report.html |
Report stile management-consulting (grafici a torta, raccomandazioni, metodologia in calce). |
storia.html |
Andamento nel tempo (gated: parte dal run #1). |
dist/mxmap_it_dataset.{csv,json,xlsx} |
Dataset completo opendata. |
/ente/{prov}/{nome}/ · /aree/… · /categoria/… |
~53.000 pagine SEO (#15): una per ente con tutti i dati di rilevamento + hub regione/provincia/comune + facet per categoria. Git-ignored, solo nell'artifact. Vedi sotto. |
sitemap.xml · robots.txt · site.webmanifest |
SEO. sitemap.xml è un indice rigenerato ogni notte da scripts/build_entity_pages.py (<lastmod> = generated_at di kpi.json); robots.txt/manifest sono statici. Le pagine portano <meta> keywords/canonical; index.html espone JSON-LD (WebSite/Organization/Dataset, alternateName MxMap Italia) per Google Dataset Search. |
scripts/build_entity_pages.py genera, a ogni
nightly, una pagina statica per ogni ente (/ente/{sigla-prov}/{nome-ente}/)
con il verdetto di sovranità (6- e 4-bucket, riusando sovereignty_of /
provider_to_sov4), le evidenze DNS (MX/SPF/DKIM/autodiscover/ASN), l'affidabilità
(classification_confidence + regola + segnali), lo storico (gated) e gli enti
vicini come spinta reputazionale (#15). Più gli hub geografici (regione →
provincia → comune, con ISD e league-table) e le facet per categoria. Ogni
pagina ha il pulsante «Riporta un errore»; per gli enti anomali / a bassa
confidence diventa la CTA enfatizzata «Aiutaci a risolvere l'anomalia».
- URL: nome completo dell'ente, namespace per provincia, slug deterministici e collision-free (0 collisioni su 22.987 enti); stabili nel tempo.
- Sitemap a indice:
sitemap.xml→sitemap-core/aree/categorie+sitemap-enti-{regione}.xml(20 file) — dà in pasto ai motori tutta l'Italia. - Manutenibilità: ~53.000 file generati in ~50 s, git-ignored (solo
nell'artifact Pages, deploy disaccoppiato dal git), coperti dal job
smoke(subset). Logica URL pura insrc/mail_sovereignty/pages.pycon unit-test, integrity-assert a fine generazione.
data.json contiene solo l'Italia (gli enti del fork mondiale sono rimossi da
scripts/strip_to_it.py). I file sotto sono rigenerati ogni notte:
il link punta sempre all'ultima versione disponibile (la data è nel campo generated).
- CSV — https://mxmap.it/dist/mxmap_it_dataset.csv
- JSON — https://mxmap.it/dist/mxmap_it_dataset.json
- XLSX — https://mxmap.it/dist/mxmap_it_dataset.xlsx
- KPI aggregati — https://mxmap.it/kpi.json · Report — https://mxmap.it/report.json
Licenza CC BY-SA 4.0.
- IndicePA non è una base dati pulita. I domini email sono incoerenti/incompleti: l'intera pipeline esiste per rielaborarla. È una dipendenza funzionale core → issue #2.
- Geografia (ISTAT). Il campo
regiondel seed è sporco (a volte è il nome dell'ente); usiamo la chiave-sedeipa_codice_comune_istatsul crosswalk ISTAT. Sardegna: IndicePA usa i codici provincia legacy pre-riforma 2016 (prefissi 112-119) assenti dal crosswalk → mappati esplicitamente a regione Sardegna (vedigeo.py). - Storicizzazione gated. Le serie storiche partono dal run #1 (primo scan pulito dopo la chiusura delle ~700 anomalie, issue #4). Fino ad allora
historicize/dcatsono disattivati. - Una sola realtà. Nessuna distinzione "reality vs methodology": la metodologia si congela al run #1.
- Vincoli editoriali del report. Stile consulting; si guida con gli estremi segmentati; la PA Centrale (ministeri, numeri piccoli, tema sensibile) è tenuta fuori dall'allarme di testata (
SPOTLIGHT_EXCLUDE). - I numeri non devono mai sbagliare. Ogni KPI ha unit test +
assert_integrity()a runtime (vedi sotto). - Nightly indistruttibile. Il deploy è disaccoppiato dal commit git (pubblica l'artifact costruito nel run); un fallimento del commit non blocca il sito.
uv sync
uv run preprocess IT # DNS + classificazione (Italia)
uv run postprocess
uv run python3 scripts/enrich_geo.py --country IT # regione/provincia (ISTAT)
uv run python3 scripts/compute_confidence.py --country IT
uv run python3 scripts/build_stats.py # → data/summary/stats_*.json
uv run python3 scripts/build_kpi.py # → kpi.json
uv run python3 scripts/build_report.py # → report.json
python -m http.server # mappa + pagine in localeuv sync --group dev
uv run pytest --cov --cov-report=term-missing # soglia copertura: fail_under=84
uv run ruff check src tests
uv run ruff format src tests # OBBLIGATORIO prima di committare src/testsRegola: i numeri vanno sempre testati e verificati. Ogni generatore di KPI vive in
src/mail_sovereignty/ con (1) unit test su fixture a valori noti e (2) assert_integrity()
eseguito a ogni build (la pipeline fallisce se i numeri non tornano). Vedi
docs/STATS_KPI.md e CLAUDE.md.
La roadmap completa è in docs/ROADMAP.md, derivata dalle
issue aperte:
- Baseline dato → sistemare le ~700 anomalie (#4) → run #1 → storicizzazione live.
- Asse geografico → ✅ crosswalk comune→regione strutturale + ✅ sezione "Analisi per aree" del report (classifica regionale); resta l'affinamento comune-esatto Sardegna (oggi regione+provincia).
- Bonifica fonte → software qualità IndicePA (#2).
- Attendibilità → metodo Email-Bounce (#5).
- Diagnostica → pagina dimensioni + segnala-errore (#6).
- Attivazione stakeholder → emailing per-stakeholder + integrazione Osservatorio (#3).
CLAUDE.md— contesto completo per chi contribuisce (anche assistito da Claude): ecosistema, modello di sovranità, decisioni, vincoli, convenzioni.docs/—STATS_KPI.md(catalogo KPI),ROADMAP.md,HISTORICIZATION_DESIGN.md,BOUNCE_VERIFIER_DESIGN.md,countries/(guide per-paese).- Segnalazioni di misclassificazione: apri una issue con l'ID dell'ente e il provider corretto, oppure aggiungi una correzione a
MANUAL_OVERRIDESinpostprocess.py.
Fork di mxmap.ch di David Huser (mappa dei provider email dei comuni svizzeri). Esteso da livenson/mxmap a 166 paesi, e qui specializzato sull'Italia con il modello di sovranità, il crosswalk ISTAT, la storicizzazione e gli artefatti per l'Osservatorio.
- mxmap.ch — la mappa svizzera originale
- hpr4379 — Mapping Municipalities' Digital Dependencies
Dati e contenuti rilasciati sotto CC BY-SA 4.0.
🛠️ Questo README va tenuto aggiornato: ad ogni commit di una feature o di un cambiamento significativo, aggiorna le sezioni rilevanti (come funziona, corner case, roadmap, artefatti).