GovStack-bouwsteen: Implementeer modules voor digitale overheid
Hoe digitale overheidsdiensten te implementeren met de modulaire aanpak van GovStack: bouwsteen voor identiteit, betalingen, toestemming, digitale gegevens en berichtenuitwisseling. Regeringsspecificaties 2.0 (2025-2027), door meer dan twintig landen overgenomen als paradigma voor schaalbare en interoperabele persoonlijke beschermingsmiddelen.
GovStack: het modulaire raamwerk voor de digitale overheid
GovStack is een internationaal initiatief dat in 2020 werd gelanceerd door Estland, Duitsland, ITU (Internationale Telecommunicatie-unie) e DIAL (Digitale Impact Alliantie) met het doel van het delen van de tools, kennis en best practices die nodig zijn om diensten te bouwen digitaal publiek op grote schaal, zonder telkens opnieuw te hoeven beginnen.
Het fundamentele idee is eenvoudig maar krachtig: in plaats van het ontwikkelen van monolithische systemen per land, elk De digitale dienst van de overheid wordt opgesplitst in BouwBlok (functionele modules) die ze bieden een specifieke mogelijkheid (identiteit, betalingen, berichtenuitwisseling, registers, enz.) en dat ze kunnen vrij worden gecombineerd om elke dienst te bouwen. Bouwstenen zijn dat wel interoperabel, herbruikbaar en implementatie-onafhankelijk: GovStack definieert de specificaties, niet de software.
In 2025, met de lancering van Overheidsspecificaties 2.0 (de strategie 2025-2027), heeft GovStack de raamwerk door nieuwe bouwstenen te integreren, interoperabiliteitsspecificaties bij te werken en te definiëren een gelaagd volwassenheidsmodel om landen met veel digitale capaciteit te ondersteunen anders. Meer dan 20 landen maken actief gebruik van de GovStack-aanpak.
Wat je gaat leren
- De 9 fundamentele bouwstenen van GovStack en hun technische specificaties
- Hoe bestaande Italiaanse PA-services (SPID, CIE, pagoPA) in kaart te brengen op GovStack Building Blocks
- GovStack-referentiearchitectuur: gelaagd model en integratiebus
- GovSpecs 2.0: wat is er nieuw in de strategie 2025-2027
- Praktische implementatie van een Building Block Identity met OpenID Connect
- Building Block Payments: integratie met nationale betalingssystemen
- Toestemmingsbouwsteen: AVG-conform toestemmingsbeheer
- Hoe u kunt evalueren of GovStack geschikt is voor uw context
De 9 fundamentele bouwstenen
GovStack definieert 9 kernbouwstenen (gepubliceerd in de nieuwe 2025-specificatie) die de cross-functionaliteiten die nodig zijn voor elke digitale overheidsdienst:
| BouwBlok | Functie | Protocollen / Standaarden | Italiaans voorbeeld |
|---|---|---|---|
| Identiteit | Authenticatie en identiteitsbeheer | OIDC, SAML, W3C DEED | SPID, CIE, EUDI-portemonnee |
| Betalingen | Het verwerken van betalingen en overboekingen | ISO 20022, REST-API | betaalPA |
| Toestemming | Verzamelen en beheren van toestemmingen | AVG-, DPIA- en OAuth-bereiken | CMP met AVG-by-Design |
| Digitale registers | Beheer van gezaghebbende registers (registratiekantoor, kadaster) | REST-API, FHIR, CKAN | ANPR, kadaster, beroepsregister |
| Berichten | Veilige communicatie tussen overheid en burgers | SMTP, Push, WebSocket, MQTT | IO App, PEC, pagoPA-meldingen |
| Informatiebemiddeling | Veilige gegevensuitwisseling tussen systemen | X-Road, REST, GraphQL | PDND, AgID-interoperabiliteit |
| Registratie | Registratie van personen/entiteiten voor diensten | REST-API, OAuth 2.0 | INPS-portaal, SUAP, gemeentelijke portalen |
| Planner | Beheer van afspraken en reserveringen | iCalendar, REST-API | Gezondheid CUP, digitaal loket |
| Werkstroom | Orkestratie van processen en procedures | BPMN, SAGA-patroon, REST | PA praktijkmanagementsystemen |
GovStack-referentiearchitectuur
GovStack stelt een gelaagde architectuur voor waarin de bouwstenen in lagen worden geplaatst onderscheiden met duidelijke verantwoordelijkheden:
- Laag 0 - Infrastructuur: cloud, netwerk, beveiliging. BB’s zijn niet afhankelijk van een specifieke cloud; ze kunnen draaien op AWS, Azure, GCP of on-premise infrastructuur.
- Laag 1 - Kernbouwstenen: de 9 fundamentele BB’s. Het zijn autonome diensten die blootleggen Gestandaardiseerde RESTful API's. Elke BB heeft een openbare specificatie (OpenAPI + JSON-schema's).
- Laag 2 - Gedeelde services: transversale diensten zoals gecentraliseerde logging, service mesh, API-gateway, servicedetectie. Deze services ondersteunen alle BB's.
- Laag 3 - Toepassingen: daadwerkelijke digitale dienstverlening (schoolinschrijving, certificaataanvraag, betaling van zegelrecht) die de onderliggende BB's orkestreren.
# 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: wat is er nieuw in de strategie 2025-2027
Overheidsspecificaties 2.0, aangekondigd door GovStack in 2025, introduceert belangrijke updates ten opzichte van naar de vorige versie van de specificaties:
- Volwassenheidsmodel op 4 niveaus: vanaf "Initiële fase" (alleen training en architectuur hoog niveau) tot "geavanceerde integratie" (meerdere BB's geïntegreerd in nationale interoperabiliteitskaders). Hierdoor kunnen landen GovStack adopteren volgens hun eigen prioriteiten en mogelijkheden.
- Ondersteuning voor verifieerbare referenties: Building Block Identity bevat nu specificaties voor W3C verifieerbare inloggegevens, in lijn met eIDAS 2.0 en het Europese EUDI Wallet-programma.
- NAAR BB: een nieuwe bouwsteen voor de integratie van AI-componenten (taalmodellen, voorspellen, classificeren) in overheidsdiensten, met aandacht voor transparantie en verklaarbaarheid.
- OpenAPI 3.1-specificaties: Alle BB API's zijn nu gedocumenteerd met OpenAPI 3.1, met Voltooi JSON-schema's en testbare voorbeelden via SwaggerUI.
- Conformiteitstesten: Een geautomatiseerd raamwerk voor het verifiëren van een implementatie van BB voldoet aan de GovStack-specificaties.
Implementatie van Building Block Identity met OIDC
De Building Block Identity is het meest kritisch en het meest complex om te implementeren. GovStack specificeert dat het de volgende API's moet vrijgeven:
# 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)
Building Block Messaging: IO-app en overheidsmeldingen
Building Block Messaging beheert de communicatie tussen overheid en burgers. In Italië is de dienst meer in lijn met de GovStack Messaging-specificatie is Ik App (io.italia.it), de nationale app voor communicatie met de PA beheerd door 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)
GovStack-kaart van het Italiaanse ecosysteem
Italië beschikt al over een geavanceerd PPE-ecosysteem dat op natuurlijke wijze aansluit bij de GovStack Building Blocks. Voor een Italiaanse ontwikkelaar betekent het adopteren van GovStack niet dat je helemaal opnieuw moet beginnen: het betekent bestaande integraties reorganiseren volgens een gestandaardiseerd modulair model.
| BB GovStack | Italiaanse implementatie | Volwassenheidsniveau | Te vullen gat |
|---|---|---|---|
| Identiteit | SPID, CIE, eIDAS | Hoog (niveau 3-4) | Voltooi OIDC-unificatie, EUDI Wallet |
| Betalingen | betaalPA | Hoog (niveau 4) | Volledige GPD REST API |
| Berichten | IO-app, PEC | Hoog (niveau 3) | Penetratie van IO-apps (in sommige gebieden nog steeds laag) |
| Digitale registers | ANPR, kadaster, PDND | Gemiddeld (niveau 2-3) | Interoperabiliteit tussen registers is nog steeds gedeeltelijk |
| Informatiebemiddeling | PDND | Gemiddeld (niveau 2) | De adoptie van PDND groeit nog steeds |
| Toestemming | Diverse (niet gestandaardiseerd) | Laag (niveau 1) | We hebben een gecentraliseerd consensusplatform nodig |
| Planner | Regionale gezondheidszorg CUP | Laag-Midden (niveau 1-2) | Regionale fragmentatie; geen nationale standaard |
| Werkstroom | PA oefensystemen (heterogeen) | Laag (niveau 1) | Grote heterogeniteit; standaardisatie is nodig |
Wanneer moet u GovStack adopteren?
GovStack is met name geschikt wanneer:
- Je bouwt een nieuwe PA digitale dienst helemaal opnieuw beginnen en leveranciersafhankelijkheid willen vermijden
- U moet bestaande systemen integreren en een gedeeld referentiearchitectuurmodel willen
- Je opereert in een context meerdere landen (internationale samenwerking, grensoverschrijdende dienstverlening)
- U wilt bijdragen aan het open source-ecosysteem van GovStack en profiteren van de implementaties van andere landen
GovStack is minder geschikt wanneer je zeer specifieke diensten hebt voor de Italiaanse context, wanneer systems bestaande (SPID, pagoPA) voldoen al aan uw vereisten zonder de overhead van extra abstractie, of wanneer u over beperkte middelen beschikt om de complexiteit van een gedistribueerde federatie te beheren.
Conclusie: de GovTech-serie in perspectief
Dit artikel sluit de GovTech-serie af die gewijd is aan de digitalisering van het openbaar bestuur. We verkenden het volledige ecosysteem: van publieke digitale infrastructuur (DPI) tot identiteit Europese Unie (eIDAS 2.0, EUDI Wallet), van de concrete implementatie van SPID en CIE tot gegevensbescherming (GDPR-by-Design), van toegankelijkheid (WCAG 2.1 AA) tot open data (DCAT-AP_IT, CKAN) tot internationale modulaire raamwerken (GovStack).
De rode draad is altijd dezelfde: publieke digitale diensten moeten dat zijn inclusief, veilig, interoperabel en herbruikbaar. GovStack, met zijn gestandaardiseerde Building Blocks, levert een gemeenschappelijk vocabulaire en een gedeeld architectonisch model om deze doelen op mondiale schaal te bereiken.
De volledige GovTech-serie
- #00: Digitale publieke infrastructuur - Architectuur en bouwsteen
- #01: eIDAS 2.0 en EUDI Wallet - Handleiding voor ontwikkelaars
- #02: OpenID Connect voor overheidsidentiteit
- #03: Open Data API-ontwerp - Publiceer en consumeer openbare gegevens
- #04: GDPR-by-Design - Architectuurpatronen voor openbare diensten
- #05: Toegankelijke gebruikersinterface voor de PA - WCAG 2.1 AA-implementatie
- #06: API-integratie voor de overheid - SPID, CIE en IT Digital Services
- #07: GovStack-bouwsteen - (dit artikel)







