Ga naar inhoud

Risks and technical debt

The design and gbo-demo v0.9.0 have known risks and technical debt. The risk list carries forward the gap analysis of the archived reference architecture v0.6.2, with updates from the demo findings. Every entry names its follow-up: a decision, a hardening action, a demo issue, or an open question. Each entry links the findings (F), open questions (Q), requirements (REQ), and decisions (ADR) that it rests on.

Overview

Each risk belongs to one group:

ID Risk Group
R1 The ASI exists at demo grade only Routes
R2 The SDG-OOTS bridge is design-level only Routes
R3 No Gov-to-Gov regime and no positive evidence for it Routes
R4 The consent proof is a bearer token Security
R5 The EUDI adapter accepts unauthenticated issuance callbacks Security
R6 No rate limiting or quota Security
R7 Data-chain responses are not sealed Security
R8 Contract acceptance sees the parties but not the service Security
R9 Demo-grade shared services Production readiness
R10 The consent register (toestemmingsregister) is a dependency of every DvTP decision Production readiness
R11 The demo runs one PDP for all providers Production readiness
R12 Policy distribution has no integrity chain and no visible fallback Production readiness
R13 The PDP mapper is a patch on OpenFTV Production readiness
R14 New sources need a restart of the issuance server Production readiness
R15 The legal ceiling of the service catalog (dienstencatalogus) has no technical counterpart Governance
R16 Rego instead of ODRL Governance
R17 GraphQL is not a recognized data service (datadienst) type of the Federatief Datastelsel (FDS) Governance
R18 Integrator mandates have no validity and no governance Governance
R19 LDV gaps: citizen view, activity register, ASIP Governance
R20 Onboarding cost for small bronhouders Governance
R21 The absence of a consent proof selects the PID regime Security
R22 BSN in the decision logs of the EUDI and ASI routes Privacy
R23 The ADL record is incomplete behind the FSC Inway Audit
R24 The consent proof freezes at consent DvTP
R25 Source isolation is incomplete Security
R26 The QTSP can read the BSN in the ASI token Privacy
R27 No national register for QTSP admission ASI
R28 No link between the QTSP login and the source login ASI
R29 The NL PID has no place of birth ASI
R30 Fuzzy matching without returning a value ASI
R31 The backend revokes a revoked consent again DvTP
R32 Only the network protects the register listener Security
R33 A suspended consumer keeps access DvTP
R34 Any connected peer can read any consent status Security
R35 The consumer sets the consent validity DvTP
R36 Attestations are valid 365 days without a change feed EUDI
R37 BSNk recognition of the consent facility (toestemmingsvoorziening) is not settled DvTP

Routes

R1: The ASI exists at demo grade only

  • Risk: The Authentic Source Interface is built (ADR-0037), but only as a local demo stack. From January 1, 2027, the interface of CIR 2026/1735 Annex IV and the seal obligation apply (F-80).
  • Impact: Production needs these parts, which the demo does not have:
    • a qualified seal certificate and its holder (Q-17, Q-28, Q-32)
    • the national register of relying parties and access certificates (R27)
    • real identification means: the DigiD route is a mock, and the wallet verifier also trusts the development issuer CA (Q-36)
    • a decision on the role of GBO (Q-37) and on catalog registration (Q-31, Q-67)
    • a simulation deployment (#594)
  • Follow-up: Three items track this risk:
  • Related: F-80, F-84, F-87, REQ-112, SL-28.

R2: The SDG-OOTS bridge is design-level only

  • Risk: The AS4 bridge for SDG-OOTS has no implementation.
  • Impact: Until the bridge is built, the EU obligation is not met.
  • Follow-up: ADR-0020, ADR-0024. It is open who builds the adapter (Q-79).
  • Related: SDG-OOTS scenario.

R3: No Gov-to-Gov regime and no positive evidence for it

  • Risk: The PDP knows two regimes: consent and PID. No legal-basis regime, rule set, or legal basis (grondslag) data exists. A request without a consent proof falls in the PID regime (ADR-0031).
  • Impact: The two-path access-basis model of ADR-0004 is only half proven. A Gov-to-Gov regime cannot reuse "absence of a token" as its evidence, because the PID regime already uses it (R21).
  • Follow-up: Define the positive evidence for a legal basis. Then add a regime and a rule set.
  • Related: REQ-74, Gov-to-Gov scenario.

Security hardening

R4: The consent proof is a bearer token

  • Risk: The actor binding stops another consumer from using a proof. It does not stop replay by the same consumer within the validity period.
  • Impact: A stolen proof gives data access for its remaining life.
  • Follow-up: Make the validity period shorter. Bind the proof to the FSC identity with proof of possession (ADR-0032).
  • Related: REQ-68, Bearer token.

R5: The EUDI adapter accepts unauthenticated issuance callbacks

  • Risk: The issuance server verifies the PID disclosure. The gap is behind it: the adapter route POST /attestations/{source}/{type} has no caller authentication (source_release_runtime.go#L69-L81, plain ListenAndServe at main.go#L700). The developer portal shows the gap: its playground posts a typed BSN straight to the adapter through /eudi-api/ (EudiQrPanel.tsx#L36-L51, nginx.conf#L36-L42).
  • Impact: Any caller that reaches the adapter can post an arbitrary BSN. The adapter calls the source over FSC as the designated issuer. As a result, the PDP allows the request, and the adapter returns the claims without any wallet. Two more gaps add to this risk:
    • nothing binds the BSN in the source query to the verified disclosure
    • the PID trust list of the issuance server also holds non-PID anchors, including the GBO development issuer CA.
  • Follow-up: Authenticate the callback (mTLS or a signed request from the issuance server), or let the adapter verify the SD-JWT itself. Bind the disclosure to the request. Restrict the PID trust list to PID issuers (REQ-45, #133). The QTSP callback already uses a secret path and CA-pinned TLS.
  • Related: F-70, Q-18, ADR-0018, SL-25 (dropped), the callback gap.

R6: No rate limiting or quota

  • Risk: Authorization concern ⑤ exists only as structural checks: parse, depth, size, and coverage. Rate limiting and quota are not built.
  • Impact: Only the FSC contracts limit enumeration and abuse.
  • Follow-up: Define limits per grant. Enforce them at the Inway or the PDP (ADR-0004).
  • Related: REQ-17.

R7: Data-chain responses are not sealed

  • Risk: DvTP and EUDI source responses carry no qualified electronic seal. ASI verification results carry a seal: a detached JWS (ES256, x5c) over the exact body. The ASI certificate is a development certificate and is not qualified (REQ-83).
  • Impact: Non-repudiation of a data-chain response rests on the FSC transaction log and the ADL. No seal supports it.
  • Follow-up: Decide whether every source response gets one seal, whatever the channel (Q-75). The ASI seal format waits for Q-32 (ADR-0041). The BSN in the decision logs is a separate risk (R22).
  • Related: F-80, E4.

R8: Contract acceptance sees the parties but not the service

  • Risk: The autosign input carries peer IDs and grant types only.
  • Impact: At contract time, the policy cannot enforce admission per service.
  • Follow-up: An upstream FSC change. Until that change, request-time rules compensate (ADR-0015).
  • Related: F-56 (closed), REQ-62.

R21: The absence of a consent proof selects the PID regime

  • Risk: A request without a consent proof is in the PID regime (engine.rego#L168-L181). The only control is the list of designated issuers per EUDI rule. A consumer on that list can skip consent: it leaves out the consent proof.
  • Impact: The PID regime rests on an identifier whose origin no one in the chain checks. A test keeps the issuer list disjoint from the DvTP consumers, but the test knows only the local consumers. For the simulation consumer, only an inspection shows that it is disjoint.
  • Follow-up: Make positive PID evidence at the point where the wallet presentation is verified. Bind this evidence to the subject of the query (REQ-45, #133, #381). Until then, test the disjointness per environment.
  • Related: F-70, REQ-74, Q-18, Q-22, ADR-0031.

R25: Source isolation is incomplete

  • Risk: The rule "Decrypt only after the allow" depends on one condition: only the FSC Inway reaches the source gateway (REQ-81). In Docker Compose, the gateways publish no port and are only on source-gateways and sources. However, the source APIs (graphql-server, brp-graphql-server, lvg-graphql-server) are also on backend and fsc-infra (docker-compose.yml#L631-L636), together with the consumer backend (#L1080-L1082).
  • Impact: A party on those networks can reach a source API without the Inway, the PDP, and the gateway. The Helm chart has no NetworkPolicy. The decrypted value is not bound to one consent (no nonce).
  • Follow-up: Take the source APIs off the shared networks. Outside Compose, add a NetworkPolicy or an equal control. Add the nonce as a second safeguard (BSNk preconditions).
  • Related: F-79, REQ-21, ADR-0033, network isolation.

R32: Only the network protects the register listener

  • Risk: The register listener for create, list, read, and revoke has no caller authentication. The consent portal (toestemmingsportaal) backend is its only intended caller (REQ-39). In Compose, the register is on backend (docker-compose.yml#L737-L740).
  • Impact: Any party that reaches the register directly can create or revoke consents for every citizen.
  • Follow-up: Decide between authentication of the backend and network isolation (Q-58). Isolation alone works only under one condition: the register and the backend run at one participant (Q-49).
  • Related: REQ-76 (the status listener goes over FSC).

R34: Any connected peer can read any consent status

  • Risk: The status service over FSC answers every peer that has a contract for it. It does not check that the caller is the PDP of a source in the consent.
  • Impact: A connected peer with a consent_id learns its status.
  • Follow-up: Restrict the status answer to the PDPs of the sources in the consent (REQ-76, #382).
  • Related: ADR-0032.

Privacy and audit

R22: BSN in the decision logs of the EUDI and ASI routes

  • Risk: In the EUDI and ASI routes, the query names the citizen with the plain BSN. The PDP input, the ADL, and the console decision log hold it. Nothing masks the BSN, and the mapper stopped pseudonymizing it.
  • Impact: REQ-60 is not met for these routes. Masking alone also does not meet the requirement.
  • Follow-up: Keep identifiers out of the PDP input (ADR-0043, proposed). The decision is made in #368.
  • Related: F-54, SL-29 (reversed by PR #370), the BSN exception.

R23: The ADL record is incomplete behind the FSC Inway

  • Risk: The ADL is the authoritative decision record (ADR-0036). Three parts are missing:
    • Behind the FSC Inway, the record has its own random trace and no transaction ID. The Inway passes neither to the PDP (#369).
    • The record does not name the policy version (REQ-59).
    • The record holds only the allow and the reason. It does not hold the consent status answer (F-72).
  • Impact: An auditor finds the record only through the stored request header. The auditor cannot replay the decision against a known policy.
  • Follow-up: Fix the trace headers upstream in open-fsc (ADR-0035). Record the bundle revision in the ADL.
  • Related: F-41, REQ-71, Q-25.

R26: The QTSP can read the BSN in the ASI token

  • Risk: ETSI requires the identification data in the access token. In the demo, a token claim holds the BSN in readable form (tokens.go#L29).
  • Impact: The QTSP holds the token. It can read a BSN that it is not allowed to process (P-04).
  • Follow-up: Encrypt the identification data for the ASIP (#595). Send the gap to ETSI (E7).
  • Related: F-86, REQ-86, Q-36.

ASI production gaps

R27: No national register for QTSP admission

  • Risk: The authorization server admits a QTSP on the entitlement QEAA_Provider in the national register of relying parties. That register and its API do not exist. It is not fixed which identifier links the QTSP certificate to its entry. The demo uses a stub register file.
  • Impact: QTSPs cannot be admitted in production. If the register is unreachable during a re-check, the demo keeps the client active (fail-open). Issued tokens stay valid up to 10 minutes after a withdrawal.
  • Follow-up: Q-34 (outage behavior), Q-35 (identifier), Q-41 (tokens after withdrawal).
  • Related: F-85, REQ-88, REQ-89, E10.

R28: No link between the QTSP login and the source login

  • Risk: The QTSP identifies the citizen, and the ASI authorization server identifies the citizen again. Nothing links the two. The QR route can be completed on another phone.
  • Impact: A QTSP can attest the data of person B to person A.
  • Follow-up: Add a check under CIR 2025/1569 art. 9(5). In this check, the QTSP sends its identification data for verification. As an alternative, the QTSP gives this data to the authorization server as a hint (Q-40). Send the gap to ETSI (E6).
  • Related: F-93, REQ-90, REQ-102.

R29: The NL PID has no place of birth

  • Risk: CIR 2024/2977 makes the place of birth a mandatory PID attribute. The NL PID type metadata has no place of birth. As a result, the authorization server asks for four of the five mandatory attributes.
  • Impact: If the Netherlands reads "identification data" as the mandatory PID attributes, a token from the NL Wallet cannot meet that reading. DigiD gives a BSN only (F-88).
  • Follow-up: Ask the NL Wallet team. Fix the minimum identification data in the PSA (Q-36).
  • Related: F-94, REQ-86.

R30: Fuzzy matching without returning a value

  • Risk: The ASIP gives MatchWithVariation and never returns the stored value. Annex IV of CIR 2026/1735 makes the value optional but keeps the fuzzy-matching requirement. The conformance matrix marks that requirement failing.
  • Impact: In a literal reading of the annex, the ASIP does not conform.
  • Follow-up: Decide whether the ASIP returns the value on a match (Q-29). Send the gap to ETSI (E2).
  • Related: F-92, REQ-85, REQ-113.

R24: The consent proof freezes at consent

  • Risk: BSNk transforms only while the citizen is logged in (BSNK-04). As a result, the proof fixes the parties, the key versions, the BSN authorization, and the proof form for its whole validity period. The proof has no version.
  • Impact: A new source needs a new consent. A re-keyed source must keep its old keys (AV-09). A new proof form makes all outstanding proofs fail with CONSENT_CONTEXT_INVALID.
  • Follow-up: Give the proof a version. Extend the service catalog with OIN, key version, and BSN authorization per party (REQ-82, Q-77).
  • Related: F-78, ADR-0032, the proof freezes at consent.

R31: The backend revokes a revoked consent again

  • Risk: A direct revoke of an expired or already revoked consent returns HTTP 200, sets REVOKED again, and writes a new LDV record (main.go#L282-L299).
  • Impact: The status history and the logbook get false entries.
  • Follow-up: Refuse the revoke with CONSENT_NOT_REVOCABLE (REQ-109, #600).
  • Related: F-97.

R33: A suspended consumer keeps access

  • Risk: The request policy does not read the admission status of a direct consumer. A suspended consumer with an existing contract can still send queries under a valid consent.
  • Impact: Suspension takes effect only at the revocation of the contract.
  • Follow-up: REQ-110. Q-53 covers the time until the suspension takes effect.
  • Related: ADR-0015.

R35: The consumer sets the consent validity

  • Risk: The consumer sends the validity period. The default is one year, with no upper limit. The citizen cannot change it.
  • Impact: A proof can live longer than the purpose needs. This makes R4 and R24 worse.
  • Follow-up: Decide who sets the validity period (Q-13, Q-76).
  • Related: F-11, REQ-19.

R37: BSNk recognition of the consent facility is not settled

  • Risk: Only a recognized party can activate and transform (BSNK-01). It is open whether the consent facility can be that party, and in which role. The demo assumes route D: the backend derives and activates, and the register stores PI and PP.
  • Impact: Route D works only under one condition: the backend and the register are one BSNk participant. The rotation of the reference key (Q-56) and the protection of stored PI and PP (Q-57) are open.
  • Follow-up: Q-26, Q-49, and ADR-0040 (proposed).
  • Related: F-75, REQ-69.

EUDI issuance

R36: Attestations are valid 365 days without a change feed

  • Risk: Every attestation type gets a fixed validity of 365 days. GBO only queries sources and has no change feed.
  • Impact: If the source data changes, an issuer must revoke an attestation that is valid for 24 hours or more. GBO cannot do that.
  • Follow-up: Per type, choose short-lived attestations or a change feed with revocation (REQ-77).
  • Related: F-82, AV-12, AV-13, ADR-0028.

Production readiness

R9: Demo-grade shared services

  • Risk: The shared services are demo grade: the consent register runs in memory by default, DigiD is a mock, and the PKI is self-generated. The BSNk mock follows the BSNk interface (as JSON), but it encrypts nothing. The three demo sources share one OIN and one decryption component.
  • Impact: None of these services carries production trust or availability. The demo does not demonstrate unlinkability per source.
  • Follow-up: ADR-0023, ADR-0005, a replicated register store (SL-12), and a DigiD connection.
  • Related: Q-54, demo mock and production BSNk.

R10: The consent register is a dependency of every DvTP decision

  • Risk: For every request, the policy asks for the consent status online, without a cache.
  • Impact: A register outage stops all consent-based traffic (fail closed).
  • Follow-up: Set the availability, latency, and propagation targets (REQ-75, Q-47). Replicate the register.
  • Related: F-61, ADR-0032.

R11: The demo runs one PDP for all providers

  • Risk: The target model has a PAP and a PDP at each bronhouder (ADR-0021). The demo runs one shared PDP.
  • Impact: The demo does not show policy separation between bronhouders.
  • Follow-up: Run a PDP per provider in the simulation deployment.
  • Related: Q-21 (answered).

R12: Policy distribution has no integrity chain and no visible fallback

  • Risk: By default, the PDP loads policies from a bind mount. Bundle distribution through the OpenFTV Manager is opt-in. Bundles are not signed. Signing is not planned while bundles stay inside one organization (#426). The policy store is cumulative, and one broken module stops every decision.
  • Impact: There is no integrity chain from author to runtime. There is no visible fallback to the last valid policy set.
  • Follow-up: Guidelines RL-01 and RL-05. If a bundle crosses an organization boundary, signing becomes a requirement.
  • Related: F-09, F-51, F-52, Q-21.

R13: The PDP mapper is a patch on OpenFTV

  • Risk: The PDP is built from OpenFTV tag v0.1.0, with the FTV GraphQL mapper patched in.
  • Impact: Upstream changes can break the build. The patch steps fail loudly, but the maintenance cost stays.
  • Follow-up: Contribute the mapper extension point to OpenFTV upstream (ADR-0013).
  • Related: REQ-42.

R14: New sources need a restart of the issuance server

  • Risk: The issuance server reads its configuration at startup.
  • Impact: Source activation needs a controlled restart.
  • Follow-up: Accepted in demo v0.9.0 (ADR-0016). If the issuance server supports reload, revisit this decision.
  • Related: REQ-64.

Governance and standards

R15: The legal ceiling of the service catalog has no technical counterpart

  • Risk: Rule coverage replaced the catalog artifact. The legally anchored ceiling and its governance (the Bureau DvTP process) are not defined.
  • Impact: The legal ceiling of what a consumer can ask for is not inspectable on its own.
  • Follow-up: A legal anchoring decision (ADR-0008, ADR-0009). The data-set description is one input (REQ-121).
  • Related: F-74.

R16: Rego instead of ODRL

  • Risk: The PSA lists ODRL for S06. The design uses Rego, and Cedar inside the OpenFTV Manager.
  • Impact: The comply-or-explain duty is open.
  • Follow-up: Bring the deviation to the standards bodies, in line with D-13. For more information, see Policy lifecycle and Q-78.

R17: GraphQL is not a recognized FDS data service type

  • Risk: The data access pillar rests on a deviation that no standard covers.
  • Follow-up: ADR-0025.

R18: Integrator mandates have no validity and no governance

  • Risk: Three points of the integrator route are open. A mandate has no validity period, and the integrator is not in the proof (Q-46). The upstream act.sub deviates from RFC 8693. The route itself is built (opt-in) and has three parts (ADR-0034):
    • an FSC delegated grant
    • a mandate per integrator, provider, and rule in the admission register
    • a consent that binds the represented party.
  • Impact: A mandate stays valid until someone removes it. It is not set who admits integrators and on which terms (AV-06).
  • Follow-up: Three items are open: mandate validity, the governance of admission (Q-74), and a change log for admissions (REQ-123).
  • Related: F-73 (closed), REQ-106, Q-12.

R19: LDV gaps: citizen view, activity register, ASIP

  • Risk: LDV is built: each controller logs its own processings in its own logbook (ADR-0026). These gaps remain:
    • the citizen view of processings per consent is not built (P-13)
    • the register of processing activities is a stand-in (Q-08)
    • the local dpl.gbo.* extension is not agreed (Q-72)
    • the ASIP writes no LDV records
    • no pointer leads from the ADL to the consent logbook (Q-25)
    • no retention, access model, or tamper evidence exists for the ADL the logbooks, and the FSC transaction logs (REQ-32).
  • Impact: A citizen cannot see QTSP verifications. A chain read stops at a party that does not log under LDV (F-68).
  • Follow-up: The open points of ADR-0026, and the consumer obligation (AV-02).
  • Related: REQ-72, F-64, SL-50.

R20: Onboarding cost for small bronhouders

  • Risk: The cost to connect a source stays high for small organizations.
  • Impact: There is an adoption risk for municipalities and small sources.
  • Follow-up: ADR-0027.

Accepted technical debt

The design accepts the following technical debt on purpose:

  • Monetary amounts in attestations are integers, because the pinned wallet stack cannot represent non-integral values (ADR-0017).
  • Attestations have no nested claims (REQ-78).
  • The unsecured source transport exists for local development. The mount policy excludes it from deployments. The code does not exclude it.
  • The demo works around OpenFTV upstream quirks and documents them: the policy language flag, the missing policy-data APIs, and the broken policy DELETE (the demo retires a policy instead).

Traceability