Visura API
May 30, 2026 · View on GitHub
Servizio REST per l'estrazione automatizzata di dati catastali dal portale SISTER dell'Agenzia delle Entrate. Utilizza Playwright per pilotare un browser headless e FastAPI per esporre gli endpoint.
Disclaimer legale — Questo progetto è uno strumento indipendente e non è affiliato, approvato o supportato dall'Agenzia delle Entrate. L'utente è l'unico responsabile del rispetto dei termini di servizio del portale SISTER e della normativa vigente. L'uso di automazione sul portale potrebbe violare i termini d'uso del servizio.
Warning
Per poter attivare le API bisogna prima registrarsi e chiedere l'accesso ai servizi sister utilizzando l'Area Personale di Agenzia delle Entrate e poi cercando "sister" tra i servizi disponibili. L'operazione è veloce: https://www.agenziaentrate.gov.it/portale/adesione-ai-servizi-sister
iscriviti alla newsletter:
Indice
- Panoramica
- Architettura
- Prerequisiti
- Avvio rapido
- Configurazione
- Endpoint API
- Esempi d'uso
- Logging e debug
- Protezione dati e retention
- Dettagli tecnici
- Sviluppo e contribuzione
- Risoluzione dei problemi
- Autore
- Licenza
Panoramica
Visura API permette di interrogare i dati catastali italiani tramite una semplice interfaccia HTTP. Il flusso operativo è diviso in due fasi:
| Fase | Endpoint | Descrizione |
|---|---|---|
| 1 — Immobili | POST /visura | Cerca gli immobili associati a foglio + particella |
| 2 — Intestati | POST /visura/intestati | Recupera i titolari di uno specifico subalterno |
Entrambe le richieste vengono accodate ed eseguite sequenzialmente su un singolo browser autenticato al portale SISTER. I risultati si recuperano in polling con GET /visura/{request_id}.
Funzionalità principali
- Autenticazione SPID/SISTER automatizzata — provider Sielte ID, PosteID o login diretto SISTER (selezionabile via
SPID_PROVIDER) - Coda sequenziale — le richieste vengono processate una alla volta per non sovraccaricare il portale
- Ri-autenticazione automatica — alla scadenza della sessione, il servizio tenta prima un recovery diretto e, solo se necessario, un nuovo login SPID
- Keep-alive — la sessione viene mantenuta attiva con un light keep-alive ogni 30 secondi e un refresh profondo ogni 5 minuti
- Graceful shutdown — su
SIGINT/SIGTERMil servizio effettua il logout dal portale prima di chiudere il browser - Logging HTML completo — ogni pagina visitata dal browser viene salvata su disco per debug e audit
- Docker-ready — immagine pronta con tutte le dipendenze di sistema per Chromium headless
Provider di autenticazione supportati
Il provider è selezionato dalla variabile d'ambiente SPID_PROVIDER (case-insensitive, default sielte):
SPID_PROVIDER | Provider | Credenziali richieste | 2° fattore |
|---|---|---|---|
sielte (default) | SPID Sielte ID | ADE_USERNAME, ADE_PASSWORD | push notification su app MySielteID |
poste | SPID PosteID | POSTE_USERNAME, POSTE_PASSWORD | approvazione su app PosteID |
sister | Login diretto SISTER (tab dedicato sulla pagina ADE) | SISTER_USERNAME, SISTER_PASSWORD | nessuno (credenziali nominali professionali) |
Scope d'uso ammesso per il provider sister
Il login diretto SISTER è destinato esclusivamente all'intestatario della convenzione SISTER che desidera automatizzare le proprie consultazioni con le proprie credenziali nominali.
Non è destinato a chi vuole rivendere o esporre l'accesso al portale SISTER a terzi: la convenzione SISTER (Agenzia delle Entrate) richiede che l'utenza sia personale, non cedibile, e che le consultazioni siano riconducibili all'intestatario.
Questo progetto è una libreria di automazione: la responsabilità dell'uso delle credenziali e del rispetto dei termini di convenzione resta dell'intestatario, esattamente come per il flusso SPID.
Limitazioni note
- Alcune città presentano strutture catastali particolari (sezioni urbane, mappe speciali) che possono causare risultati parziali.
- Se la particella non esiste nel catasto, il portale restituisce "NESSUNA CORRISPONDENZA TROVATA" e l'API ritorna una lista vuota con il campo
errorvalorizzato. - Gli immobili con partita "Soppressa" vengono inclusi nei risultati ma senza intestati.
Architettura
Client HTTP
│
▼
┌──────────────────────────────────────────────────────┐
│ FastAPI (main.py) │
│ │
│ ┌─────────────┐ ┌──────────────────────────────┐ │
│ │ Endpoints │──│ VisuraService │ │
│ │ REST │ │ • asyncio.Queue │ │
│ └─────────────┘ │ • response_store (dict) │ │
│ │ • worker sequenziale │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌───────────────────────────▼───────────────────┐ │
│ │ BrowserManager │ │
│ │ • Playwright browser (Chromium headless) │ │
│ │ • Keep-alive task │ │
│ │ • Session recovery / re-login │ │
│ └───────────────────────────┬───────────────────┘ │
└──────────────────────────────┼───────────────────────┘
│
▼
┌──────────────────────────┐
│ Portale SISTER │
│ sister3.agenziaentrate │
│ .gov.it │
└──────────────────────────┘
File del progetto
| File | Descrizione |
|---|---|
main.py | Applicazione FastAPI: endpoint, modelli Pydantic, BrowserManager, VisuraService, lifespan |
utils.py | Automazione browser: login(), logout(), run_visura(), run_visura_immobile(), extract_all_sezioni(), PageLogger, parse_table() |
Dockerfile | Immagine basata su python:3.11-slim con dipendenze per Chromium |
docker-compose.yaml | Orchestrazione con healthcheck, volumi per log, restart automatico |
requirements.txt | Dipendenze Python |
pyproject.toml | Metadati di progetto e dipendenze opzionali di sviluppo |
Prerequisiti
- Python 3.11+ (testato fino a 3.13)
- Credenziali SPID tramite provider Sielte ID con app MySielteID configurata
- Convenzione SISTER attiva — l'utente deve avere un account abilitato sul portale SISTER
Per Docker:
- Docker Engine 20+
- Docker Compose v2
Avvio rapido
Con Docker (raccomandato)
git clone https://github.com/zornade/visura-api.git
cd visura-api
cp .env.example .env
# Modifica .env con le tue credenziali SPID
docker-compose up -d
# Verifica che il servizio sia attivo
curl http://localhost:8000/health
Installazione manuale
git clone https://github.com/zornade/visura-api.git
cd visura-api
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
playwright install chromium
cp .env.example .env
# Modifica .env con le tue credenziali SPID
uvicorn main:app --host 0.0.0.0 --port 8000
All'avvio il servizio:
- Lancia un browser Chromium headless
- Esegue il login SPID — approva la notifica push sull'app MySielteID entro 120 secondi
- Naviga fino alla sezione Visure catastali del portale SISTER
- Avvia il keep-alive e il worker della coda
- Inizia ad accettare richieste su porta 8000
Configurazione
Crea un file .env nella root del progetto (vedi .env.example):
# Obbligatorio — Credenziali SPID (Sielte ID)
ADE_USERNAME=RSSMRA85M01H501Z # Codice fiscale
ADE_PASSWORD=la_tua_password
# Opzionale
LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR
| Variabile | Obbligatoria | Default | Descrizione |
|---|---|---|---|
ADE_USERNAME | ✅ | — | Codice fiscale per il login SPID |
ADE_PASSWORD | ✅ | — | Password SPID (Sielte ID) |
LOG_LEVEL | INFO | Livello di log su console e file |
Endpoint API
Health check
GET /health
{
"status": "healthy",
"authenticated": true,
"queue_size": 0
}
Visura immobili (Fase 1)
POST /visura
Cerca tutti gli immobili su una particella catastale. Se tipo_catasto è omesso, vengono accodate due richieste (Terreni + Fabbricati).
Request body:
| Campo | Tipo | Obbligatorio | Default | Descrizione |
|---|---|---|---|---|
provincia | string | ✅ | — | Nome della provincia (es. "Trieste") |
comune | string | ✅ | — | Nome del comune (es. "TRIESTE") |
foglio | string | ✅ | — | Numero foglio |
particella | string | ✅ | — | Numero particella |
sezione | string | null | Sezione censuaria (se presente) | |
tipo_catasto | string | null | "T" = Terreni, "F" = Fabbricati. Se omesso: entrambi |
Esempio:
curl -X POST http://localhost:8000/visura \
-H "Content-Type: application/json" \
-d '{
"provincia": "Trieste",
"comune": "TRIESTE",
"foglio": "9",
"particella": "166",
"tipo_catasto": "F"
}'
Risposta:
{
"request_ids": ["req_F_1709312400000"],
"tipos_catasto": ["F"],
"status": "queued",
"message": "Richieste aggiunte alla coda per TRIESTE F.9 P.166"
}
Nota: per i Terreni (
T) gli intestati vengono estratti automaticamente. Per i Fabbricati (F) vengono restituiti solo gli immobili — per ottenere gli intestati di un singolo fabbricato, usa la Fase 2.
Visura intestati (Fase 2)
POST /visura/intestati
Estrae i titolari (intestati) di uno specifico immobile. Per i Fabbricati è necessario specificare il subalterno.
Request body:
| Campo | Tipo | Obbligatorio | Default | Descrizione |
|---|---|---|---|---|
provincia | string | ✅ | — | Nome della provincia |
comune | string | ✅ | — | Nome del comune |
foglio | string | ✅ | — | Numero foglio |
particella | string | ✅ | — | Numero particella |
tipo_catasto | string | ✅ | — | "T" o "F" |
subalterno | string | Per F | null | Subalterno (obbligatorio per Fabbricati, vietato per Terreni) |
sezione | string | null | Sezione censuaria |
Esempio:
curl -X POST http://localhost:8000/visura/intestati \
-H "Content-Type: application/json" \
-d '{
"provincia": "Trieste",
"comune": "TRIESTE",
"foglio": "9",
"particella": "166",
"tipo_catasto": "F",
"subalterno": "3"
}'
Risposta:
{
"request_id": "intestati_F_3_1709312500000",
"tipo_catasto": "F",
"subalterno": "3",
"status": "queued",
"message": "Richiesta intestati aggiunta alla coda per TRIESTE F.9 P.166 Sub.3",
"queue_position": 1
}
Polling risultati
GET /visura/{request_id}
Recupera lo stato e i dati di una richiesta precedentemente accodata.
| Status | Significato |
|---|---|
processing | La richiesta è in coda o in esecuzione |
completed | Dati disponibili nel campo data |
error | Errore — dettagli nel campo error |
Risposta completata (Fase 1):
{
"request_id": "req_F_1709312400000",
"tipo_catasto": "F",
"status": "completed",
"data": {
"immobili": [
{
"Foglio": "9",
"Particella": "166",
"Sub": "3",
"Categoria": "A/2",
"Classe": "5",
"Consistenza": "4.5",
"Rendita": "500,00",
"Indirizzo": "VIA ROMA 10",
"Partita": "12345"
}
],
"results": [
{
"result_index": 1,
"immobile": { },
"intestati": []
}
],
"total_results": 1,
"intestati": []
},
"error": null,
"timestamp": "2026-03-06T10:30:00"
}
Risposta completata (Fase 2 — intestati):
{
"request_id": "intestati_F_3_1709312500000",
"status": "completed",
"data": {
"immobile": {
"Foglio": "9",
"Particella": "166",
"Sub": "3"
},
"intestati": [
{
"Nominativo o denominazione": "ROSSI MARIO",
"Codice fiscale": "RSSMRA85M01H501Z",
"Titolarità": "Proprietà per 1/1"
}
],
"total_intestati": 1
}
}
Risposta con nessuna corrispondenza:
{
"request_id": "req_F_1709312400000",
"status": "completed",
"data": {
"immobili": [],
"results": [],
"total_results": 0,
"intestati": [],
"error": "NESSUNA CORRISPONDENZA TROVATA"
}
}
Sezioni territoriali
POST /sezioni/extract
Estrae le sezioni censuarie per tutte le province e comuni d'Italia. Operazione molto lenta — può richiedere ore.
| Campo | Tipo | Default | Descrizione |
|---|---|---|---|
tipo_catasto | string | "T" | "T" o "F" |
max_province | int | 200 | Numero massimo di province da processare (1–200) |
Shutdown
POST /shutdown
Esegue un shutdown controllato: logout dal portale SISTER e chiusura del browser.
Esempi d'uso
Flusso completo con cURL
# 1. Avvia l'estrazione dei fabbricati
curl -s -X POST http://localhost:8000/visura \
-H "Content-Type: application/json" \
-d '{"provincia":"Roma","comune":"ROMA","foglio":"100","particella":"50","tipo_catasto":"F"}' \
| jq .
# Salva il request_id dalla risposta, poi:
# 2. Polling risultati (ripeti fino a status != "processing")
curl -s http://localhost:8000/visura/req_F_1709312400000 | jq .
# 3. Prendi un subalterno dai risultati e chiedi gli intestati
curl -s -X POST http://localhost:8000/visura/intestati \
-H "Content-Type: application/json" \
-d '{"provincia":"Roma","comune":"ROMA","foglio":"100","particella":"50","tipo_catasto":"F","subalterno":"3"}' \
| jq .
# 4. Polling intestati
curl -s http://localhost:8000/visura/intestati_F_3_1709312500000 | jq .
Client Python
import requests, time
BASE = "http://localhost:8000"
def visura_completa(provincia, comune, foglio, particella, tipo="F", subalterno=None):
# Fase 1: immobili
r = requests.post(f"{BASE}/visura", json={
"provincia": provincia, "comune": comune,
"foglio": foglio, "particella": particella,
"tipo_catasto": tipo
}).json()
rid = r["request_ids"][0]
# Polling
while True:
res = requests.get(f"{BASE}/visura/{rid}").json()
if res["status"] != "processing":
break
time.sleep(5)
if res["status"] == "error":
raise Exception(res["error"])
immobili = res["data"]["immobili"]
print(f"Trovati {len(immobili)} immobili")
if not subalterno or tipo == "T":
return res["data"]
# Fase 2: intestati per uno specifico subalterno
r2 = requests.post(f"{BASE}/visura/intestati", json={
"provincia": provincia, "comune": comune,
"foglio": foglio, "particella": particella,
"tipo_catasto": tipo, "subalterno": subalterno
}).json()
rid2 = r2["request_id"]
while True:
res2 = requests.get(f"{BASE}/visura/{rid2}").json()
if res2["status"] != "processing":
break
time.sleep(5)
return res2["data"]
# Esempio
dati = visura_completa("Roma", "ROMA", "100", "50", tipo="F", subalterno="3")
print(dati)
Logging e debug
Il servizio produce due livelli di logging:
Log testuale
Scritto su stdout e su file in logs/visura.log. Contiene l'intero flusso operativo: login, navigazione, estrazione dati, errori.
# Avvia con log dettagliati
LOG_LEVEL=DEBUG uvicorn main:app --host 0.0.0.0 --port 8000
Log HTML delle pagine (PageLogger)
Ogni pagina visitata dal browser viene salvata come file HTML su disco. Questo permette di ispezionare esattamente ciò che il browser ha visto in ogni punto del flusso — utile per debug, audit e sviluppo.
Struttura directory:
logs/pages/
└── 2026-03-06_16-28-24/ ← session_id (reset ad ogni avvio del server)
├── login/
│ ├── 01_goto_login.html
│ ├── 02_entra_con_spid.html
│ ├── 03_sielte.html
│ ├── ...
│ └── 15_conferma_lettura.html
├── visura/
│ ├── 01_scelta_servizio.html
│ ├── 02_provincia_applicata.html
│ ├── 03_immobile.html
│ ├── 04_ricerca.html
│ ├── 05_conferma_subalterno.html
│ ├── 06_risultati.html
│ └── 07_intestati_r1.html
├── visura_002/ ← seconda visura nella stessa sessione
│ └── ...
├── logout/
│ ├── 01_before_logout.html
│ └── 02_after_logout.html
└── recovery/
└── ...
Ogni file HTML include in testa dei commenti con metadati:
<!-- URL: https://sister3.agenziaentrate.gov.it/Visure/... -->
<!-- Step: ricerca -->
<!-- Timestamp: 2026-03-06T16:30:45 -->
Privacy: la directory
logs/pages/è nel.gitignoreperché i file HTML contengono dati personali (codice fiscale, intestatari, indirizzi). Non committare mai questi file.
Protezione dati e retention
I dati estratti da SISTER e i log HTML generati da PageLogger possono contenere dati personali e informazioni patrimoniali. Chi esegue il servizio è responsabile della configurazione, conservazione, protezione e cancellazione di questi dati nel proprio ambiente.
Raccomandazioni operative:
- Imposta
LOG_PAGES=0in produzione, salvo sessioni di debug mirate. - Conserva
logs/visura.logelogs/pages/solo per il tempo necessario a diagnosi, audit tecnico o obblighi interni documentati. - Cancella o anonimizza i file HTML prima di condividerli in issue, PR, chat o ticket di supporto.
- Proteggi l'accesso a
logs/, backup e volumi Docker con gli stessi controlli usati per dati personali. - Definisci una procedura di retention esplicita per risultati in memoria, file di log e copie di backup.
Il progetto non applica ancora un mascheramento automatico dei dati nei log HTML: verifica sempre manualmente i file prima di esportarli fuori dal tuo ambiente.
Dettagli tecnici
Gestione della sessione
| Meccanismo | Intervallo | Descrizione |
|---|---|---|
| Light keep-alive | 30 secondi | Mouse move sulla pagina per evitare timeout idle |
| Session refresh | 5 minuti | Naviga a SceltaServizio.do e verifica che la sessione sia ancora attiva |
| Recovery | Su errore | Navigazione diretta → percorso interno → re-login SPID completo |
Coda di elaborazione
- Unica
asyncio.Queuecon worker sequenziale - Pausa di 2 secondi tra una richiesta e l'altra
- Pausa di 5 secondi dopo un errore
- I risultati restano in memoria (
response_store) fino al riavvio del servizio - Il client fa polling su
GET /visura/{request_id}— restituisce"processing"finché il risultato non è pronto
Graceful shutdown
Quando uvicorn riceve SIGINT o SIGTERM:
- Il lifespan
shutdownviene invocato da uvicorn logout()clicca "Esci" sul portale SISTERclose()clicca "Torna al portale", chiude il browser context, chiude Chromium
Il browser viene lanciato con handle_sigint=False, handle_sigterm=False per impedire che Chromium intercetti i segnali prima che il logout sia completato.
Flusso di autenticazione SPID (Sielte ID)
- Naviga alla pagina di login dell'Agenzia delle Entrate
- Clicca "Entra con SPID" → seleziona provider Sielte ID
- Inserisce codice fiscale (con CapsLock attivo) e password
- Clicca "Prosegui" → seleziona invio notifica push
- Clicca "Autorizza" → attende fino a 120 secondi l'approvazione sull'app MySielteID
- Cerca "SISTER" tra i servizi → clicca "Vai al servizio"
- Verifica assenza di sessione bloccata ("Utente già in sessione")
- Naviga: Conferma → Consultazioni e Certificazioni → Visure catastali → Conferma Lettura
Flusso della visura
- Naviga a
SceltaServizio.do— seleziona provincia — clicca Applica - Clicca "Immobile" — seleziona tipo catasto (
T/F), comune, compila foglio e particella - Clicca "Ricerca" — gestisce eventuale "conferma assenza subalterno"
- Se "NESSUNA CORRISPONDENZA TROVATA" → ritorna risultato vuoto con
.error - Estrae la tabella immobili (
table.listaIsp4) - Per ogni immobile (radio button): seleziona → clicca "Intestati" → estrae tabella intestatari → torna indietro
- Gli immobili con
Partita = "Soppressa"vengono inclusi ma senza estrazione intestati
Sviluppo e contribuzione
Setup ambiente di sviluppo
git clone https://github.com/zornade/visura-api.git
cd visura-api
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e ".[dev]" # pytest, black, ruff
playwright install chromium
cp .env.example .env
# Configura le credenziali
Struttura del codice
main.py contiene:
- Modelli Pydantic di input (
VisuraInput,VisuraIntestatiInput,SezioniExtractionRequest) - Dataclass interne (
VisuraRequest,VisuraResponse,VisuraIntestatiRequest) - Eccezioni custom (
VisuraError,AuthenticationError,BrowserError,ValidationError) BrowserManager— gestione browser, login, keep-alive, session recoveryVisuraService— coda, worker, store risultati- Lifespan FastAPI (startup/shutdown)
- Tutti gli endpoint REST
utils.py contiene:
PageLogger— salva HTML di ogni pagina visitata, organizzato per sessione/flusso/steplogin(page, username, password)— flusso SPID completo (15 step, ciascuno loggato)logout(page)— cerca e clicca "Esci" con fallback su più selettori CSSrun_visura(page, ...)— visura completa: selezione provincia → estrazione intestatirun_visura_immobile(page, ...)— visura mirata per un singolo fabbricato con subalternoextract_all_sezioni(page, ...)— iterazione su tutte le province/comuni per estrarre sezionifind_best_option_match(page, selector, text)— fuzzy matching a 5 livelli su dropdown<select>parse_table(html)— parsing tabelle HTML con BeautifulSoup → lista di dizionari
Aggiungere un provider SPID
Il login è implementato nella funzione login() di utils.py. Per supportare un altro provider:
- Modifica il selettore del provider (attualmente
a[href*="sielte"]) - Adatta il form di inserimento credenziali (ogni provider ha layout diversi)
- Gestisci il metodo di approvazione (push notification, OTP, etc.)
Convenzioni per il logging HTML
Quando aggiungi nuovi flussi o step, usa PageLogger:
logger = PageLogger("nome_flusso") # Crea logger per questo flusso
await logger.log(page, "nome_step") # Salva HTML della pagina corrente
I file vengono numerati automaticamente (01_nome_step.html, 02_...). Flussi ripetuti nella stessa sessione ricevono un suffisso incrementale (visura, visura_002, visura_003, ...).
Formattazione e linting
black . # formattazione automatica
ruff check . # controllo linting
Test
python -m pytest -v
Docker
docker-compose up --build # build e avvio
docker-compose logs -f # segui i log
docker-compose down # stop e rimozione container
Linee guida
Leggi CONTRIBUTING.md per il dettaglio completo. In breve:
- Crea un branch dal
maincon un nome descrittivo (fix/...,feat/...) - Ogni modifica significativa deve includere i log
PageLoggernei punti critici - Mai committare file da
logs/— contengono dati personali - Rimuovi le credenziali dai log prima di condividerli in una issue
Risoluzione dei problemi
| Problema | Causa probabile | Soluzione |
|---|---|---|
| Il login non parte | Credenziali mancanti | Verifica ADE_USERNAME e ADE_PASSWORD nel file .env |
| Timeout su "Autorizza" | Push non approvata in tempo | Approva la notifica MySielteID entro 120 secondi |
| "Utente già in sessione" | Sessione precedente non chiusa | Attendi qualche minuto o chiudi manualmente dal portale |
| Sessione scaduta durante visura | Inattività prolungata | Il servizio tenta il recovery automatico; se fallisce, ri-esegue il login |
| "NESSUNA CORRISPONDENZA TROVATA" | Dati catastali inesistenti | Verifica foglio, particella, tipo catasto e comune |
| Risposte lente | Coda piena | Controlla queue_size con GET /health |
| Chromium non si avvia in Docker | Dipendenze di sistema mancanti | Usa il Dockerfile fornito che include tutte le librerie necessarie |
| Log HTML vuoti o mancanti | Errore durante il salvataggio | Controlla i permessi sulla directory logs/pages/ |
Per debug approfondito, ispeziona i file HTML in logs/pages/ — mostrano esattamente cosa vedeva il browser in ogni step.
Autore
Sviluppato da zornade.
Copyright © 2026 zornade.
Licenza
Distribuito sotto licenza GNU Affero General Public License v3.0 — only (SPDX-License-Identifier: AGPL-3.0-only).
Vedi anche il file NOTICE per il testo completo dell'avviso di copyright e per le obbligazioni AGPL §13 imposte agli operatori di servizi di rete.
Cronologia della licenza
| Data | Commit | Licenza |
|---|---|---|
| 2026-03-04 | 128082c | GPL-3.0-only (release iniziale) |
| 2026-03-12 | c8126e8 | AGPL-3.0-only (licenza attuale) |
Chi ha forkato il repository prima del commit
c8126e8conserva un grant perpetuo GPL-3.0 su quella snapshot. Chi ha forkato o tirato modifiche dopoc8126e8è vincolato ad AGPL-3.0-only.
⚠️ Stai forkando? Leggi prima questa sezione
AGPL-3.0 è una strong copyleft network license. In particolare la clausola §13 ("Remote Network Interaction") impone obblighi che molti sviluppatori sottovalutano. Se intendi forkare visura-api e usarlo in un servizio di rete, sei tenuto a:
- Mantenere AGPL-3.0 in tutte le distribuzioni del fork. Non puoi rilicenziare ad Apache, MIT, BSD, GPL-2, GPL-3 o altre licenze.
- Preservare il file
LICENSE, il fileNOTICE, gli header SPDX e i credit all'autore originale in ogni copia distribuita o ridistribuita. - Pubblicare le tue modifiche al codice base sotto AGPL-3.0, includendo l'intera storia git delle modifiche.
- Se esponi il fork (o un suo derivato) come servizio di rete — SaaS, B2B API, dashboard, microservizio, intranet — devi offrire a tutti gli utenti del servizio l'accesso pubblico al Corresponding Source completo dell'opera combinata, comprese:
- le tue modifiche al codice base,
- tutte le componenti private linkate o combinate col servizio (autenticazione, SPID/CIE adapter, frontend, theme, orchestratori, workflow engines, moduli di scoring, integrazioni Stripe/Clerk/CRM, schema DB e migrations Alembic, Dockerfile, Helm chart, IaC),
- le installation information necessarie a ricostruire un deploy comparabile.
- Pubblicare un avviso visibile ("prominent offer") nell'UI o nella documentazione API del servizio, con il link al Corresponding Source.
La mancata conformità ad AGPL §13 termina automaticamente i tuoi diritti sul software (AGPL §8) ed espone a rivendicazioni legali.
Checklist forker (rapida)
- Il
LICENSEdel mio fork è ancoraAGPL-3.0-only? - Il file
NOTICEè presente e include il copyright originale? - Gli header SPDX nei file sorgente sono preservati?
- Il
READMEdel mio fork attribuisce esplicitamente il progetto upstream? - Tutte le dipendenze private che linkero/combinerò sono pronte a essere pubblicate come Corresponding Source, in caso di deploy in rete?
- Ho preparato la "prominent offer" del Corresponding Source nell'UI/docs del mio servizio?
- Se non posso/non voglio rispettare uno dei punti sopra, ho contattato
hello@zornade.comper una licenza commerciale?
Licenza commerciale (dual licensing)
Se la licenza AGPL-3.0 non si adatta al tuo caso d'uso (es. SaaS proprietario, prodotto closed-source, integrazione in piattaforma enterprise senza obbligo di pubblicare i moduli combinati), è disponibile una licenza commerciale separata acquistabile da zornade. Vedi COMMERCIAL-LICENSE.md per condizioni e pricing indicativo.
Contatto: hello@zornade.com · zornade.com/licensing
Enforcement
In caso di violazioni AGPL, contattare hello@zornade.com con oggetto [AGPL] <nome del fork o servizio>. Il maintainer applica le pratiche di enforcement raccomandate da Software Freedom Conservancy e FSF: contatto privato prima di azioni pubbliche, finestra di rimedio di 30 giorni, escalation solo se necessaria.
Ultimo aggiornamento: maggio 2026