Blocul GovStack: implementați modulele guvernamentale digitale
Cum să implementați serviciile guvernamentale digitale cu abordarea modulară a GovStack: bloc pentru identitate, plăți, consimțământ, înregistrări digitale și mesagerie. GovSpecs 2.0 (2025-2027), adoptat de peste 20 de țări, ca paradigmă pentru EIP scalabil și interoperabil.
GovStack: cadrul modular pentru guvernarea digitală
GovStack este o inițiativă internațională lansată în 2020 de Estonia, Germania, ITU (Uniunea Internațională de Telecomunicații) e DIAL (Alianța cu impact digital) cu scopul de a împărtăși instrumentele, cunoștințele și cele mai bune practici necesare pentru a construi servicii audiente digitale la scară, fără a fi nevoie să pornești de la zero de fiecare dată.
Ideea fundamentală este simplă, dar puternică: în loc să se dezvolte sisteme monolitice pe țară, fiecare serviciul digital guvernamental este descompus în BuildingBlock (module funcţionale) care oferă o capacitate specifică (identitate, plăți, mesagerie, registre etc.) și asta pot fi combinate liber pentru a construi orice serviciu. Blocurile de construcție sunt interoperabil, reutilizabil și independent de implementare: GovStack definește caietul de sarcini, nu software-ul.
În 2025, odată cu lansarea lui GovSpecs 2.0 (strategia 2025-2027), GovStack a rafinat cadru prin integrarea de noi blocuri de construcție, actualizarea specificațiilor de interoperabilitate și definirea un model de maturitate pe niveluri pentru a sprijini țările cu o mare capacitate digitală diferite. Peste 20 de țări utilizează în mod activ abordarea GovStack.
Ce vei învăța
- Cele 9 blocuri fundamentale ale GovStack și specificațiile tehnice ale acestora
- Cum să mapați serviciile PA italiene existente (SPID, CIE, pagoPA) pe blocurile de construcție GovStack
- Arhitectura de referință GovStack: model stratificat și magistrală de integrare
- GovSpecs 2.0: ce este nou în strategia 2025-2027
- Implementarea practică a unei identități de bloc cu OpenID Connect
- Building Block Payments: integrare cu sistemele naționale de plată
- Blocul de bază al consimțământului: gestionarea consimțământului conform GDPR
- Cum să evaluezi dacă GovStack este potrivit pentru contextul tău
Cele 9 blocuri fundamentale
GovStack definește 9 blocuri de bază (publicate în noua specificație 2025) care acoperă funcționalități transversale necesare oricărui serviciu guvernamental digital:
| BuildingBlock | Funcţie | Protocoale / Standarde | Exemplu italian |
|---|---|---|---|
| Identitate | Autentificarea și managementul identității | OIDC, SAML, W3C DID | Portofel SPID, CIE, EUDI |
| Plăți | Procesarea plăților și transferurilor | ISO 20022, REST API | payPA |
| Consimţământ | Colectarea și gestionarea consimțământurilor | Domenii de aplicare GDPR, DPIA, OAuth | CMP cu GDPR-by-Design |
| Registre digitale | Gestionarea registrelor de autoritati (registru, carte funciara) | REST API, FHIR, CKAN | ANPR, carte funciară, carte profesională |
| Mesaje | Comunicații sigure între guvern și cetățeni | SMTP, Push, WebSocket, MQTT | Notificări aplicație IO, PEC, pagoPA |
| Medierea informațională | Schimb sigur de date între sisteme | X-Road, REST, GraphQL | PDND, Interoperabilitate AgID |
| Înregistrare | Înregistrarea persoanelor/entităților pentru servicii | API REST, OAuth 2.0 | Portal INPS, SUAP, portaluri municipale |
| Programator | Gestionarea programărilor și rezervărilor | iCalendar, API REST | Health CUP, contor digital |
| Fluxul de lucru | Orchestrarea proceselor și procedurilor | BPMN, model SAGA, REST | Sisteme de management al practicii PA |
Arhitectura de referință GovStack
GovStack propune o arhitectură stratificată în care blocurile de construcție sunt plasate în straturi distins cu responsabilități clare:
- Stratul 0 - Infrastructură: cloud, rețea, securitate. BB-urile nu depind de un anumit nor; pot rula pe infrastructură AWS, Azure, GCP sau on-premise.
- Stratul 1 - Blocuri de bază: cele 9 BB-uri fundamentale. Sunt servicii autonome care expun API-uri RESTful standardizate. Fiecare BB are o specificație publică (scheme OpenAPI + JSON).
- Nivelul 2 - Servicii partajate: servicii transversale, cum ar fi înregistrarea centralizată, rețea de servicii, Gateway API, descoperire de servicii. Aceste servicii acceptă toate BB-urile.
- Stratul 3 - Aplicații: servicii digitale reale (înscrierea la școală, cerere de certificat, plata taxei de timbru) care orchestrează BB-urile subiacente.
# Architettura di un servizio PA con Building Block GovStack
# Esempio: Servizio di iscrizione scolastica online
# Il servizio orchestra 5 Building Block:
# 1. Identity BB - autenticazione genitore con SPID/CIE
# 2. Registration BB - raccolta dati del bambino
# 3. Digital Registries BB - verifica iscrizione anagrafica
# 4. Consent BB - consenso al trattamento dati minori
# 5. Messaging BB - conferma iscrizione via IO App/email
# 6. Payments BB - pagamento tassa iscrizione via pagoPA
from dataclasses import dataclass
from typing import Optional
import httpx
@dataclass
class SchoolEnrollmentRequest:
parent_session_token: str # Token da Identity BB (SPID/CIE)
child_fiscal_code: str
school_code: str
year: int
class SchoolEnrollmentService:
"""
Servizio iscrizione scolastica che orchestra i Building Block GovStack.
Pattern: Saga orchestrator (gestione stati distribuiti con compensazioni).
"""
def __init__(
self,
identity_bb_url: str,
registry_bb_url: str,
consent_bb_url: str,
messaging_bb_url: str,
payments_bb_url: str
):
self.identity_url = identity_bb_url
self.registry_url = registry_bb_url
self.consent_url = consent_bb_url
self.messaging_url = messaging_bb_url
self.payments_url = payments_bb_url
async def process_enrollment(self, request: SchoolEnrollmentRequest) -> dict:
"""
Saga: processo iscrizione con compensazioni in caso di errore.
Ogni step è idempotente e reversibile.
"""
saga_log = []
try:
# Step 1: Verifica identità genitore tramite Identity BB
parent_identity = await self._verify_parent_identity(
request.parent_session_token
)
saga_log.append({"step": "identity_verified", "status": "ok"})
# Step 2: Verifica residenza bambino tramite Digital Registries BB
child_registry = await self._verify_child_in_registry(
request.child_fiscal_code,
parent_identity["fiscal_number"]
)
saga_log.append({"step": "registry_verified", "status": "ok"})
# Step 3: Raccolta consenso GDPR tramite Consent BB
consent_id = await self._collect_consent(
parent_identity["fiscal_number"],
purpose="school_enrollment_data_processing"
)
saga_log.append({"step": "consent_collected", "consent_id": consent_id})
# Step 4: Registrazione iscrizione tramite Registration BB
enrollment_id = await self._register_enrollment(
child_fc=request.child_fiscal_code,
school_code=request.school_code,
year=request.year,
parent_fc=parent_identity["fiscal_number"]
)
saga_log.append({"step": "enrollment_registered", "enrollment_id": enrollment_id})
# Step 5: Pagamento tassa (se prevista) tramite Payments BB
payment_url = await self._create_payment(
parent_fc=parent_identity["fiscal_number"],
enrollment_id=enrollment_id,
amount_cents=1500 # 15 euro
)
saga_log.append({"step": "payment_created", "payment_url": payment_url})
# Step 6: Notifica conferma tramite Messaging BB
await self._send_confirmation(
parent_fc=parent_identity["fiscal_number"],
enrollment_id=enrollment_id,
payment_url=payment_url
)
saga_log.append({"step": "notification_sent", "status": "ok"})
return {
"status": "success",
"enrollment_id": enrollment_id,
"payment_url": payment_url,
"saga_log": saga_log
}
except Exception as e:
# Compensazione: rollback degli step completati in ordine inverso
await self._compensate(saga_log, e)
raise
async def _verify_parent_identity(self, session_token: str) -> dict:
"""Chiama il Building Block Identity per validare il token SPID/CIE."""
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.identity_url}/v1/tokens/validate",
json={"token": session_token}
)
response.raise_for_status()
return response.json() # Returns: fiscal_number, name, surname, etc.
async def _verify_child_in_registry(self, child_fc: str, parent_fc: str) -> dict:
"""Chiama il Building Block Digital Registries per verificare l'anagrafe."""
async with httpx.AsyncClient() as client:
response = await client.get(
f"{self.registry_url}/v1/citizens/{child_fc}/family-relations",
params={"parent_fiscal_code": parent_fc}
)
response.raise_for_status()
return response.json()
async def _collect_consent(self, parent_fc: str, purpose: str) -> str:
"""Registra il consenso tramite il Building Block Consent."""
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.consent_url}/v1/consents",
json={
"citizen_pseudonym": self._pseudonymize(parent_fc),
"purpose": purpose,
"legal_basis": "GDPR Art. 6(1)(e)",
"version": "2025-01"
}
)
response.raise_for_status()
return response.json()["consent_id"]
async def _compensate(self, saga_log: list, error: Exception):
"""
Compensazione Saga: rollback in ordine inverso degli step completati.
Garantisce consistenza anche in caso di errori parziali.
"""
for step in reversed(saga_log):
try:
if step["step"] == "consent_collected":
await self._revoke_consent(step["consent_id"])
elif step["step"] == "enrollment_registered":
await self._cancel_enrollment(step["enrollment_id"])
except Exception as compensation_error:
# Logga l'errore di compensazione ma continua
print(f"Compensation error for {step['step']}: {compensation_error}")
def _pseudonymize(self, fiscal_code: str) -> str:
import hashlib, hmac
key = b"secret-vault-key" # In produzione: usa un HSM
return hmac.new(key, fiscal_code.encode(), hashlib.sha256).hexdigest()[:32]
GovSpecs 2.0: Ce este nou în strategia 2025-2027
GovSpecs 2.0, anunțat de GovStack în 2025, introduce actualizări importante comparativ la versiunea anterioară a specificațiilor:
- Model de maturitate cu 4 niveluri: din „Faza inițială” (doar instruire și arhitectură nivel înalt) la „Integrare avansată” (BB-uri multiple integrate în cadrele naționale de interoperabilitate). Acest lucru permite țărilor să adopte GovStack în funcție de propriile priorități și capacități.
- Suport pentru acreditări verificabile: Building Block Identity include acum specificații pentru Acreditări verificabile W3C, aliniate cu eIDAS 2.0 și cu programul european EUDI Wallet.
- LA BB: un nou Building Block pentru integrarea componentelor AI (modele de limbaj, predicție, clasificare) în serviciile guvernamentale, cu atenție la transparență și explicabilitate.
- Specificații OpenAPI 3.1: Toate API-urile BB sunt acum documentate cu OpenAPI 3.1, cu Scheme JSON complete și exemple testabile prin SwaggerUI.
- Testare de conformitate: Un cadru automat pentru verificarea implementării of BB respectă specificațiile GovStack.
Implementarea Building Block Identity cu OIDC
Building Block Identity este cel mai critic și cel mai complex de implementat. GovStack specifică că trebuie să expună următoarele API-uri:
# Building Block Identity - Implementazione minima conforme GovStack
# OpenAPI 3.1 compatible - FastAPI implementation
from fastapi import FastAPI, HTTPException, Header, Depends
from fastapi.security import HTTPBearer
from pydantic import BaseModel
from typing import Optional
import jwt
app = FastAPI(
title="Identity Building Block",
description="GovStack Identity BB - Conforme a GovSpecs 2.0",
version="2.0.0"
)
security = HTTPBearer()
# --- Modelli ---
class TokenValidationRequest(BaseModel):
token: str
expected_acr: Optional[str] = None # Livello autenticazione richiesto
class IdentityResponse(BaseModel):
sub: str # Identificativo presso l'IdP
fiscal_number: Optional[str] = None # Codice Fiscale (SPID/CIE)
given_name: str
family_name: str
birthdate: Optional[str] = None
acr: str # Livello autenticazione effettivo
auth_time: int # Timestamp autenticazione
session_valid_until: int # Scadenza sessione
class SessionCreationRequest(BaseModel):
idp_id: str # Identity Provider scelto dall'utente
redirect_uri: str
acr_values: str = "https://www.spid.gov.it/SpidL2"
scope: list = ["openid", "profile"]
ui_locales: str = "it"
# --- Endpoints del Building Block Identity ---
@app.post("/v1/sessions",
summary="Avvia sessione di autenticazione",
tags=["Sessions"],
response_model=dict)
async def create_authentication_session(request: SessionCreationRequest) -> dict:
"""
GovStack Identity BB - Avvia il flusso di autenticazione.
Restituisce l'URL di redirect verso l'IdP.
"""
# Genera state e nonce per sicurezza
import secrets
session_id = secrets.token_urlsafe(32)
state = secrets.token_urlsafe(32)
nonce = secrets.token_urlsafe(32)
# Recupera metadata dell'IdP dalla federazione OIDC
idp_metadata = await get_idp_metadata(request.idp_id)
# Costruisce URL autorizzazione (PKCE + Request Object)
auth_url = build_oidc_auth_url(
idp_auth_endpoint=idp_metadata["authorization_endpoint"],
client_id=CLIENT_ID,
redirect_uri=request.redirect_uri,
scope=request.scope,
state=state,
nonce=nonce,
acr_values=request.acr_values,
private_key=SIGNING_KEY
)
# Salva sessione (Redis o DB)
await session_store.save(session_id, {
"state": state, "nonce": nonce, "idp_id": request.idp_id
})
return {"session_id": session_id, "authorization_url": auth_url}
@app.post("/v1/tokens/validate",
summary="Valida un token di sessione",
tags=["Tokens"],
response_model=IdentityResponse)
async def validate_token(request: TokenValidationRequest) -> IdentityResponse:
"""
GovStack Identity BB - Valida un token e restituisce l'identità.
I servizi chiamanti usano questo endpoint per verificare l'autenticazione.
"""
try:
# Decodifica e valida il token (firma, scadenza, audience)
claims = jwt.decode(
request.token,
JWKS,
algorithms=["RS256"],
audience=CLIENT_ID
)
# Verifica livello autenticazione se richiesto
if request.expected_acr:
actual_acr = claims.get("acr", "")
if not _meets_acr_requirement(actual_acr, request.expected_acr):
raise HTTPException(
status_code=403,
detail=f"Insufficient authentication level. Required: {request.expected_acr}"
)
return IdentityResponse(
sub=claims["sub"],
fiscal_number=claims.get("fiscal_number"),
given_name=claims["given_name"],
family_name=claims["family_name"],
birthdate=claims.get("birthdate"),
acr=claims.get("acr", ""),
auth_time=claims.get("auth_time", 0),
session_valid_until=claims.get("exp", 0)
)
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError as e:
raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}")
@app.delete("/v1/sessions/{session_id}",
summary="Termina sessione (logout)",
tags=["Sessions"])
async def end_session(session_id: str) -> dict:
"""
GovStack Identity BB - Logout con propagazione verso l'IdP.
Implementa OIDC Back-Channel Logout per notificare tutti i RP attivi.
"""
session = await session_store.get(session_id)
if not session:
raise HTTPException(status_code=404, detail="Session not found")
# Notifica logout all'IdP (OIDC Back-Channel Logout)
idp_metadata = await get_idp_metadata(session["idp_id"])
if "end_session_endpoint" in idp_metadata:
await propagate_logout(idp_metadata["end_session_endpoint"], session)
await session_store.delete(session_id)
return {"status": "session_terminated"}
def _meets_acr_requirement(actual: str, required: str) -> bool:
"""Verifica che il livello di autenticazione effettivo soddisfi il requisito."""
ACR_LEVELS = {
"https://www.spid.gov.it/SpidL1": 1,
"https://www.spid.gov.it/SpidL2": 2,
"https://www.spid.gov.it/SpidL3": 3,
"https://www.cie.gov.it/cie/aa": 2,
}
return ACR_LEVELS.get(actual, 0) >= ACR_LEVELS.get(required, 0)
Mesagerie bloc: aplicație IO și notificări guvernamentale
Building Block Messaging gestionează comunicațiile dintre guvern și cetățeni. În Italia, serviciul mai aliniat cu specificația GovStack Messaging este I App (io.italia.it), aplicația națională de comunicare cu PA administrată de PagoPA S.p.A.
# Building Block Messaging - Integrazione con IO App
# API REST di IO App per invio messaggi ai cittadini
import httpx
from pydantic import BaseModel
from typing import Optional
class IOMessage(BaseModel):
fiscal_code: str # Codice Fiscale del destinatario
time_to_live: int = 3600 # Secondi di validità notifica push
content: dict # Contenuto del messaggio
default_addresses: Optional[dict] = None # Fallback email
class IOAppClient:
"""
Client per le API di IO App.
Le API IO App sono disponibili su https://developer.io.italia.it
"""
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.io.italia.it/api/v1"
async def send_message(self, message: IOMessage) -> dict:
"""
Invia un messaggio a un cittadino tramite IO App.
Il cittadino deve aver attivato il proprio profilo IO.
"""
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.base_url}/messages",
headers={
"Ocp-Apim-Subscription-Key": self.api_key,
"Content-Type": "application/json"
},
json={
"fiscal_code": message.fiscal_code,
"time_to_live": message.time_to_live,
"content": message.content,
"default_addresses": message.default_addresses
}
)
response.raise_for_status()
return response.json()
async def check_profile(self, fiscal_code: str) -> bool:
"""
Verifica se un cittadino ha attivato il profilo IO e accetta messaggi dalla PA.
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"{self.base_url}/profiles/{fiscal_code}",
headers={"Ocp-Apim-Subscription-Key": self.api_key}
)
if response.status_code == 404:
return False
response.raise_for_status()
profile = response.json()
return profile.get("sender_allowed", False)
# Utilizzo: notifica iscrizione scolastica
async def notify_enrollment(fiscal_code: str, enrollment_id: str, payment_url: str):
io_client = IOAppClient(api_key="your-io-api-key")
# Verifica se il cittadino usa IO App
has_io = await io_client.check_profile(fiscal_code)
if has_io:
# Messaggio strutturato IO App con CTA pagamento
message = IOMessage(
fiscal_code=fiscal_code,
content={
"subject": f"Iscrizione Scolastica {enrollment_id} - Conferma",
"markdown": f"""
## La tua iscrizione è stata registrata
L'iscrizione con codice **{enrollment_id}** è stata registrata con successo.
Per completare la procedura, effettua il pagamento della tassa di iscrizione tramite il
link qui sotto.
**Importo**: 15,00 €
[Paga ora]({payment_url})
Per assistenza: [Ufficio Scolastico](https://istruzione.comune.esempio.it)
""",
"payment_data": {
"amount": 1500,
"notice_number": enrollment_id,
"payee": {
"fiscal_code": "COMUNE_FC",
"name": "Comune di Esempio"
}
}
},
default_addresses={"email": None} # No fallback email
)
await io_client.send_message(message)
else:
# Fallback: email tradizionale (da implementare)
await send_email_notification(fiscal_code, enrollment_id, payment_url)
Harta GovStack a ecosistemului italian
Italia are un ecosistem PPE deja avansat, care se mapează în mod natural pe blocurile de construcție GovStack. Pentru un dezvoltator italian, adoptarea GovStack nu înseamnă a începe de la zero: înseamnă reorganiza integrările existente după un model modular standardizat.
| BB GovStack | Implementarea italiană | Nivelul de maturitate | Gol de umplet |
|---|---|---|---|
| Identitate | SPID, CIE, eIDAS | Înalt (nivel 3-4) | Unificare completă OIDC, portofel EUDI |
| Plăți | payPA | Înalt (nivel 4) | Completați API-ul GPD REST |
| Mesaje | Aplicația IO, PEC | Înalt (nivel 3) | Penetrarea aplicației IO (încă scăzută în unele zone) |
| Registre digitale | ANPR, carte funciara, PDND | Medie (nivel 2-3) | Interoperabilitatea între registre este încă parțială |
| Medierea informațională | PDND | Medie (nivelul 2) | Adopția PDND este în continuare în creștere |
| Consimţământ | Diverse (nestandardizate) | Scăzut (nivel 1) | Avem nevoie de o platformă de consens centralizat |
| Programator | CUP regională de sănătate | Scăzut-Mediu (nivel 1-2) | Fragmentarea regională; nici un standard național |
| Fluxul de lucru | Sisteme de practică PA (eterogene) | Scăzut (nivel 1) | Mare eterogenitate; este nevoie de standardizare |
Când să adoptăm GovStack
GovStack este potrivit în special atunci când:
- Tu construiești o noul serviciu digital PA de la zero și doriți să evitați dependențele de furnizor
- Trebuie să integrați sistemele existente și doriți un model arhitectural de referință comun
- Operați într-un context multi-țară (cooperare internațională, servicii transfrontaliere)
- Vrei să contribui la ecosistemul open source GovStack și să beneficiezi de implementările altor țări
GovStack este mai puțin potrivit atunci când aveți servicii foarte specifice pentru contextul italian, când sisteme cele existente (SPID, pagoPA) vă satisfac deja cerințele fără suprasolicitarea abstracției suplimentare, sau atunci când aveți resurse limitate pentru a gestiona complexitatea unei federații distribuite.
Concluzie: Seria GovTech în perspectivă
Acest articol închide seria GovTech dedicată digitalizării administrației publice. Am explorat întregul ecosistem: de la infrastructura digitală publică (DPI) până la identitate Uniunea Europeană (eIDAS 2.0, EUDI Wallet), de la implementarea concretă a SPID și CIE până la protecția datelor (GDPR-by-Design), de la accesibilitate (WCAG 2.1 AA) la date deschise (DCAT-AP_IT, CKAN) până la cadre modulare internaționale (GovStack).
Filul comun este întotdeauna același: serviciile digitale publice trebuie să fie inclusiv, sigur, interoperabil și reutilizabil. GovStack, cu blocurile sale standardizate, oferă rezultate un vocabular comun și un model arhitectural comun pentru atingerea acestor obiective la scară globală.
Întreaga serie GovTech
- #00: Infrastructură publică digitală - Arhitectură și bloc de construcție
- #01: eIDAS 2.0 și EUDI Wallet - Ghid pentru dezvoltatori
- #02: OpenID Connect pentru identitate guvernamentală
- #03: Open Data API Design - Publicați și consumați date publice
- #04: GDPR-by-Design - Modele arhitecturale pentru servicii publice
- #05: UI accesibilă pentru implementarea PA - WCAG 2.1 AA
- #06: Integrare API guvernamentală - SPID, CIE și IT Digital Services
- #07: GovStack Building Block - (acest articol)







