{"id":"leeftijd-gte-21-v1","built":true,"verificationKeyUrl":"https://zkp-circuit-registry.administratieomgeving.nl/circuits/leeftijd-gte-21-v1/verification-key.json","wasmUrl":"https://zkp-circuit-registry.administratieomgeving.nl/circuits/leeftijd-gte-21-v1/circuit.wasm","verificationKeySchemaProtocol":"groth16","verificationKeySchemaCurve":"bn128","readme":"# Circuit leeftijd-gte-21-v1\n\n**Wat dit circuit bewijst**: dat een persoon op een gegeven peil-datum minimaal een gegeven leeftijds-drempel oud is, zonder dat de geboortedatum aan de verifier wordt prijsgegeven.\n\n**Status**: end-to-end werkend op productie sinds 2026-05-25. Eerste echte build via SSH op de VPS (pre-built circom-binary plus snarkjs-globaal-install plus pot12.ptau-download van Polygon zkEVM mirror). Vier snapshot-proeven slaagden (35j, 10j, precies 21, bijna 21). Vier artefacten (`circuit_js/circuit.wasm`, `circuit_final.zkey`, `verification_key.json`, `hashes.txt`) zijn via Aanpak A gecommit naar git plus via webhook-deploy automatisch in beide containers (zkp-circuit-registry-voorziening voor publieke artefacten, admin-backend voor de proving-key) gelandt. Browser-test door Gijs in de wallet-PWA Rekenregels-pagina sectie \"Circuit-bewijzen\" bevestigd op 12:35 CEST.\n\n**Onderdeel van**: Sub-stap 10a uit [`../../../../docs/uitbreidingen/implementatie-rule-and-principle-inferencing/plan.md`](../../../../docs/uitbreidingen/implementatie-rule-and-principle-inferencing/plan.md). Eerste concrete circuit voor het zkp-predicate-claim-type, conform de techniek-keuze die in [`../../../../docs/uitbreidingen/research-rule-and-principle-inferencing/02-techniek-invulling.md`](../../../../docs/uitbreidingen/research-rule-and-principle-inferencing/02-techniek-invulling.md) sub-vraag 2.6 is vastgesteld.\n\n## Architectuur-keuze 2026-05-23\n\nDit circuit doet bewust **niet** zelf de handtekening-controle van de bron-credential. De admin-backend valideert eerst de ECDSA-handtekening van mock-gemeente-diplo op de PID-mdoc (of een vergelijkbare bron met `birth_date`-claim), en geeft de geparseerde geboortedatum pas dan als geheime invoer aan de prover door. De verifier vertrouwt daarmee impliciet op de admin-backend-validatie van de credential plus op de wiskundige correctheid van dit circuit.\n\nHet alternatief, een variant die de handtekening-controle wel in het circuit doet, zou een circuit van ongeveer 1.5 miljoen constraints opleveren met een prove-tijd van 10 tot 20 seconden plus een trusted-setup-artefact van honderden megabytes. De huidige variant heeft paar honderd constraints met sub-seconde prove-tijd plus een trusted-setup van kilobytes. Voor demo-iteratie 1 is dit voldoende; de uitgebreidere variant komt eventueel later in een productie-iteratie.\n\n## Inputs en outputs\n\n### Privaat (witness, alleen op de prover bekend)\n\n| Signaal | Type | Betekenis |\n|---|---|---|\n| `birth_year` | uint16 | Geboortejaar, bijvoorbeeld 1990 |\n| `birth_month` | uint8 | Geboortemaand, 1 tot en met 12 |\n| `birth_day` | uint8 | Geboortedag, 1 tot en met 31 |\n\n### Publiek (bekend bij prover en verifier)\n\n| Signaal | Type | Betekenis |\n|---|---|---|\n| `today_year` | uint16 | Peil-jaar, bijvoorbeeld 2026 |\n| `today_month` | uint8 | Peil-maand, 1 tot en met 12 |\n| `today_day` | uint8 | Peil-dag, 1 tot en met 31 |\n| `threshold_years` | uint16 | Leeftijds-drempel in vol-jaren, voor dit circuit typisch 21 |\n\n### Output\n\n| Signaal | Type | Betekenis |\n|---|---|---|\n| `voldoet` | bool (0 of 1) | 1 als persoon op peil-datum minimaal `threshold_years` oud is, anders 0 |\n\n## Hoe het circuit de leeftijd berekent\n\nDe gedaante van een correct leeftijds-berekening is **vol-jaren tussen geboortedatum en peil-datum**, met aftrek van een jaar als de verjaardag in het lopende jaar nog niet is bereikt.\n\nDe vier stappen in het circuit zijn:\n\n1. `year_diff = today_year - birth_year`\n2. `verjaardag_geweest = (today_month > birth_month) OR (today_month == birth_month AND today_day >= birth_day)`\n3. `leeftijd = year_diff - 1 + verjaardag_geweest`\n4. `voldoet = leeftijd >= threshold_years`\n\nStap 3 corrigeert voor de jaarwissel: als de verjaardag al is geweest, trek 0 jaren af (dus `year_diff` blijft staan); is hij nog niet geweest, trek 1 jaar af.\n\nDit komt overeen met de pseudo-code in [`../../../../docs/uitbreidingen/research-rule-and-principle-inferencing/03-credential-claim-mapping.md`](../../../../docs/uitbreidingen/research-rule-and-principle-inferencing/03-credential-claim-mapping.md) Vraag 3.3 plus met de `vol-jaren-tussen`-formule die in begrippenkader voor het begrip `leeftijd` is gedefinieerd.\n\n## Bestanden in deze folder\n\n| Bestand | Wat het is | Wel in git |\n|---|---|---|\n| `circuit.circom` | Circom-bron van het circuit | ja |\n| `build.sh` | Compile-script plus trusted-setup-runner | ja |\n| `test-prove.mjs` | Snapshot-test met vier proeven, ES-module-extensie | ja |\n| `README.md` | Dit document | ja |\n| `.gitignore` | Sluit gegenereerde artefacten uit | ja |\n| `circuit_js/circuit.wasm` | Prover-runtime in WebAssembly, gegenereerd door build.sh | **ja, sinds 2026-05-24 (Aanpak A)** |\n| `circuit_final.zkey` | Proving-key met mock-ceremony-bijdrage, gegenereerd door build.sh | **ja, sinds 2026-05-24 (Aanpak A)** |\n| `verification_key.json` | Verification-key voor de verifier, gegenereerd door build.sh | **ja, sinds 2026-05-24 (Aanpak A)** |\n| `hashes.txt` | SHA-256 van de gegenereerde artefacten, gegenereerd door build.sh | **ja, sinds 2026-05-24 (Aanpak A)** |\n| `node_modules/` | Circomlib plus snarkjs als npm-deps | nee |\n| `pot12.ptau` | Powers-of-Tau setup-file (9 MB) | nee, eenmalig downloaden |\n| `circuit.r1cs` | Tussenliggend, gegenereerd door circom-compile | nee, gegenereerd |\n| `circuit.sym` | Symbool-tabel, gegenereerd door circom-compile | nee, gegenereerd |\n| `circuit_0000.zkey` | Tussenliggende zkey, gegenereerd door snarkjs-setup | nee, gegenereerd |\n\n## Build-en-commit-flow voor Aanpak A\n\nVastgesteld 2026-05-24 door Gijs als Aanpak A uit het distributie-vraagstuk: de drie publieke artefacten (`circuit_js/circuit.wasm`, `verification_key.json`, `hashes.txt`) plus de proving-key (`circuit_final.zkey`) worden gecommit naar git zodat zij via webhook-deploy automatisch op de productie-VPS landen. Beide containers (de zkp-circuit-registry-voorziening plus de admin-backend) lezen ze daar dan via hun normale build-flow.\n\nVoor demo-scope met mock-data is dat acceptabel: de proving-key wordt een gedeeld geheim in een publieke repo. Voor productie zou een echte multi-party trusted-setup-ceremony nodig zijn plus een private key-management-pipeline, zie [`../../../../docs/uitbreidingen/research-rule-and-principle-inferencing/02-techniek-invulling.md`](../../../../docs/uitbreidingen/research-rule-and-principle-inferencing/02-techniek-invulling.md) Sub-vraag 2.6 voor de open upgrade-paden naar PLONK (universele setup) of Halo2 (transparante setup).\n\n**Werkstroom per build (eenmalig of bij circuit-revisie)**:\n\n1. Volg de \"Lokaal compileren en testen\"-sectie hieronder om eenmalig de afhankelijkheden te installeren plus `bash build.sh` te draaien. Resultaat: vier nieuwe of bijgewerkte artefacten in deze folder.\n2. Run `node test-prove.mjs` ter snapshot-validatie. Vier proeven moeten slagen.\n3. `git add eco/voorzieningen/stelselvoorziening-bewijs-circuits-register/circuits/leeftijd-gte-21-v1/circuit_js/circuit.wasm circuit_final.zkey verification_key.json hashes.txt`.\n4. `git commit` met een korte boodschap als \"build(leeftijd-gte-21-v1): nieuwe artefacten na circuit-revisie\" plus de eerste regel van `hashes.txt` in de body voor traceerbaarheid.\n5. `git push` via GitHub Desktop. De webhook-deploy detecteert de pad-wijziging onder `eco/voorzieningen/stelselvoorziening-bewijs-circuits-register/circuits/leeftijd-gte-21-v1/` en triggert TWEE rebuilds: (a) de zkp-circuit-registry-voorziening neemt de nieuwe artefacten in haar container op, en (b) de admin-backend image-build kopieert de nieuwe `circuit_final.zkey` naar `/app/zkp-keys/leeftijd-gte-21-v1.zkey` voor zijn prover-module.\n\n**Belangrijk**: de mock-ceremony in `build.sh` genereert een random-entropy-bijdrage per run, dus elke nieuwe build levert een andere `circuit_final.zkey` op. De verification-key blijft hetzelfde zolang `circuit.circom` plus `pot12.ptau` ongewijzigd zijn. Voor cross-check kun je de hashes in `hashes.txt` vergelijken met een eerdere build: de `r1cs`-hash plus de `verification_key`-hash zijn deterministisch, de `zkey`-hash niet.\n\n## Lokaal compileren en testen\n\n**Eenmalige setup** (Node.js v18 of nieuwer; Rust-toolchain niet vereist want we gebruiken de pre-built circom-binary uit iden3-releases):\n\n```bash\n# Circom 2.1.6 als pre-built Linux-binary plaatsen in /usr/local/bin\nsudo curl -L -o /usr/local/bin/circom https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64\nsudo chmod 755 /usr/local/bin/circom\n\n# Snarkjs globaal installeren plus circomlib lokaal in de circuit-folder\nsudo npm install -g snarkjs@^0.7\n# Op een sudo-install kan npm restrictieve permissies zetten op\n# /usr/local/lib/node_modules/snarkjs, waardoor `snarkjs` op de PATH niet\n# uitvoerbaar is voor andere users. Eenmalig fixen met:\nsudo chmod 755 /usr/local/lib/node_modules\nsudo chmod -R go+rX /usr/local/lib/node_modules/snarkjs\n\ncd eco/voorzieningen/stelselvoorziening-bewijs-circuits-register/circuits/leeftijd-gte-21-v1/\nnpm init -y\nnpm install circomlib@^2.0.5\n\n# Powers-of-Tau setup-file downloaden (4.7 MB) van de Polygon zkEVM mirror.\n# De originele Hermez S3-URL is sinds late 2025 niet meer publiek\n# toegankelijk; Polygon's mirror is een betrouwbaar replica.\ncurl -L -o pot12.ptau https://storage.googleapis.com/zkevm/ptau/powersOfTau28_hez_final_12.ptau\n```\n\n**Build plus test**:\n\n```bash\nbash build.sh\nnode test-prove.mjs\n```\n\nVerwachte uitvoer van `node test-prove.mjs`: vier proeven slagen achtereenvolgens, met \"Alle proeven geslaagd\" aan het einde.\n\n## Volgende stappen\n\n- **Sub-stap 10b**: zkp-circuit-registry-voorziening die deze artefacten host op een publiek bereikbare URL. **Afgerond 2026-05-23.**\n- **Sub-stap 10c**: admin-backend zkp-prover-module die snarkjs aanroept met de geboortedatum als witness plus de huidige datum plus 21 als publieke inputs, en de proof-blob retourneert. **Afgerond 2026-05-23.**\n- **Sub-stap 10d**: wallet-EUDIPLO issuance-config voor een nieuw credential-type `Leeftijdsbewijs` met de proof-blob als claim. **Afgerond 2026-05-24.**\n- **Sub-stap 10e**: vct-registry-entry plus begrippenkader-koppeling plus admin-backend service-laag plus HTTP-endpoint. **Afgerond 2026-05-24.**\n- **Sub-stap 10f**: Rekenregels-frontend nieuwe sectie \"Circuit-bewijzen\" met een genereer-knop. **Afgerond 2026-05-24.**\n- **Sub-stap 10g (open)**: verifier-helper in de gedeelde mock-org-balie-build zodat een mock-org een binnengekomen Leeftijdsbewijs cryptografisch kan checken.\n- **Sub-stap 10h**: deploy-wiring voor Aanpak A. **Afgerond 2026-05-24, deze README-update plus de admin-backend Dockerfile-aanpassing plus de deploy-monorepo-trigger plus de docker-compose-context-fix.**\n- **Sub-stap 10i (vastgesteld 2026-05-25)**: huidige hello-world-modal waarin de burger haar geboortedatum handmatig in drie selects invult, wordt vervangen door een PID-VC-gekoppelde variant waarin admin-backend de geboortedatum leest uit een echte leeftijds-VC (mock-PID-mdoc of een toekomstige aparte leeftijds-VC) in de wallet. Demo-narratief: \"we hebben een echte leeftijds-VC en zetten die in onze omgeving om naar een ZKP\". Snelle ophaal van die onderliggende VC kan altijd nog via de catalogus-route. Concrete sub-taken in de plan-doc Sub-stap 10i.\n\nEindstand: na lokaal builden plus committen van de vier artefact-bestanden landt alles end-to-end op de VPS via één webhook-deploy. Browser-test door Gijs in de Rekenregels-pagina sectie \"Circuit-bewijzen\" bevestigd 2026-05-25. Vervolg-iteratie 10i koppelt de modal aan een echte VC voor geloofwaardigheid.\n\nVolledige plan-doc met de stap-volgorde en acceptatie-criteria per sub-stap in [`../../../../docs/uitbreidingen/implementatie-rule-and-principle-inferencing/plan.md`](../../../../docs/uitbreidingen/implementatie-rule-and-principle-inferencing/plan.md) Fase 10.\n","hashes":"  r1cs:             a3d23b851e206e4b6794fbca59e5ffb880489f324f92febb8ddbdf46036952ba\n  wasm:             8ff0d46df9e0a0cd17b699976daa48ca98627c8d486972a26cb3fd534fffd95c\n  verification_key: f7d11895bf17df4329b40b548b030d48a66ddbea56fc13aace0d5bd4e47cd6b3\n  zkey:             86a20b713828b6d1b7eab037037df228877cf57eeca8a84270d318ac47655c75\n"}