Skip to content

Latest commit

 

History

662 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MX Map Italia — Sovranità digitale della posta elettronica della PA

Nightly CI Licenza CC BY-SA 4.0

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 · ⚠️ Anomalie

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.


Ecosistema a due progetti

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).

Come funziona — dalla A alla Z

1. La pipeline notturna

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
Loading

Tre stadi classici (preprocesspostprocessvalidate) operano su data.json, poi una coda deterministica lo arricchisce e ne deriva gli artefatti pubblici. Dettaglio in CLAUDE.md e methodology.html.

2. Il modello di sovranità

La classificazione canonica è sovereignty_of() / material_row() in src/mail_sovereignty/historicize.pyunica 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.

3. I due assi di lettura

  • 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-sede ipa_codice_comune_istat): 20/20 regioni, 100% di copertura. → stats.compute_by_region produce data/summary/stats_by_region.json e alimenta la sezione "Analisi per aree" del report (classifica regionale per ISD, sintesi per macroarea, regione più sovrana / più esposta).

4. Artefatti pubblici (alla root del deploy)

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.

5. Pagine per ente e hub geografici (SEO aggressivo — #15)

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.xmlsitemap-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 in src/mail_sovereignty/pages.py con unit-test, integrity-assert a fine generazione.

Dati aperti — download (sempre l'ultima versione)

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).

Licenza CC BY-SA 4.0.

Corner case (la fonte è sporca — è il cuore del progetto)

  • 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 region del seed è sporco (a volte è il nome dell'ente); usiamo la chiave-sede ipa_codice_comune_istat sul crosswalk ISTAT. Sardegna: IndicePA usa i codici provincia legacy pre-riforma 2016 (prefissi 112-119) assenti dal crosswalk → mappati esplicitamente a regione Sardegna (vedi geo.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/dcat sono 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.

Avvio rapido

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 locale

Sviluppo & test

uv 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/tests

Regola: 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.

Roadmap

La roadmap completa è in docs/ROADMAP.md, derivata dalle issue aperte:

  1. Baseline dato → sistemare le ~700 anomalie (#4) → run #1 → storicizzazione live.
  2. Asse geografico → ✅ crosswalk comune→regione strutturale + ✅ sezione "Analisi per aree" del report (classifica regionale); resta l'affinamento comune-esatto Sardegna (oggi regione+provincia).
  3. Bonifica fonte → software qualità IndicePA (#2).
  4. Attendibilità → metodo Email-Bounce (#5).
  5. Diagnostica → pagina dimensioni + segnala-errore (#6).
  6. Attivazione stakeholder → emailing per-stakeholder + integrazione Osservatorio (#3).

Architettura & come contribuire

  • 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_OVERRIDES in postprocess.py.

Attribuzione & progetto originale

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.

Licenza

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).

About

make mxmap for italy (maybe)

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages