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)