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:
- 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
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}.
- 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
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) |
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.
- 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.
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 | 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 |
- 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
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/healthgit 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 8000All'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
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 |
GET /health
{
"status": "healthy",
"authenticated": true,
"queue_size": 0
}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.
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
}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"
}
}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) |
POST /shutdown
Esegue un shutdown controllato: logout dal portale SISTER e chiusura del browser.
# 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 .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)Il servizio produce due livelli di logging:
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 8000Ogni 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.
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.
| 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 |
- 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
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.
- 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
- 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
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 credenzialimain.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
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.)
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 correnteI file vengono numerati automaticamente (01_nome_step.html, 02_...). Flussi ripetuti nella stessa sessione ricevono un suffisso incrementale (visura, visura_002, visura_003, ...).
black . # formattazione automatica
ruff check . # controllo lintingpython -m pytest -vdocker-compose up --build # build e avvio
docker-compose logs -f # segui i log
docker-compose down # stop e rimozione containerLeggi 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
| 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.
Sviluppato da zornade.
Copyright © 2026 zornade.
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.
| 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.
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.
- 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?
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
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