Privacy11 min lezen

Het Nexun Infrastructuurrapport: Hoe Zeven Geautomatiseerde Checks En Een Ondertekende JSON Elke Week Onze No-Logs Claim Bewijzen

Een diepe duik in het wekelijkse Nexun Infrastructuurrapport: wat elk van de zeven geautomatiseerde checks bewijst, waarom we "enge" kolommen juist openbaren, de ondertekende JSON-payload en hoe je de SHA-256 zelf verifieert.

Gepubliceerd 2026-04-14Nexun Team
Het Nexun Infrastructuurrapport: Hoe Zeven Geautomatiseerde Checks En Een Ondertekende JSON Elke Week Onze No-Logs Claim Bewijzen

De meeste VPN-"transparency reports" zijn jaarlijkse PDFs geschreven door dezelfde marketingafdeling die ook de verkooppagina maakt. Die van ons is anders. Elke week draait er op onze productie-infrastructuur een geplande taak die zeven geautomatiseerde controles uitvoert op het live systeem, het resultaat als ondertekende JSON-payload in een database-rij schrijft en publiceert op nexun.io/transparency/infrastructure. Het resultaat is een cryptografisch verifieerbare attestatie van hoe de Nexun-backend er nu werkelijk uitziet — en je kunt twee willekeurige weken naast elkaar leggen om te zien dat er niets onder de motorkap is veranderd.

Bekijk het live Infrastructuurrapport
Wekelijkse SHA-256 attestatie met 7 geautomatiseerde checks

Wat de rapport-kaart in een oogopslag laat zien

Bovenaan /transparency/infrastructure staat de rapport-kaart. Vier dingen: een Report ID zoals 2026-W16-093318 (ISO-jaar, ISO-week, tijdstempel), een "gegenereerd op"-datum tot op de seconde nauwkeurig, een overall-statuspil ("Alle controles geslaagd" of "Te beoordelen"), en een 64-tekens SHA-256 content-hash. Die hash wordt berekend over de canonieke JSON-payload van het rapport. Verander een enkele byte in de payload en de hash wordt een compleet andere string. Zo weet je dat de pagina die je leest niet is gemanipuleerd.

De zeven checks, uitgelegd

Onder de header zie je een samenvattings-blok met zeven regels, elk gemarkeerd als pass of fail. Elke regel wordt gegenereerd door een andere geautomatiseerde probe die op de live machine draait — geen statisch config-bestand. Dit bewijst elke regel:

  • Geen logbestanden op schijf — de probe scant /var/log/wireguard, /var/log/nexun en /var/log/openvpn. Als een van die directories bestaat of bestanden bevat, faalt de check. Een passend resultaat vertelt je dat er nergens op schijf een plek is waar connectie-logs kunnen landen, zelfs als iets zou proberen te schrijven.
  • API-oppervlak geinventariseerd — FastAPI doet introspectie op de eigen routing-tabel en lijst elke gemounte prefix met HTTP-methodes op (bijvoorbeeld /admin heeft 33 endpoints, /beta heeft 62, /transparency heeft er 5). Het gaat niet om de getallen — het gaat erom dat de volledige vorm van de API publiek is. Er zijn geen verborgen routes.
  • Data-inventaris gedocumenteerd — de probe leest het live PostgreSQL-schema voor vpn_users, vpn_subscriptions, vpn_support_tickets en billing_events en classificeert elke kolom (identifier, contact, payment, operational, timestamp, credential, unknown). Als er in productie een nieuwe kolom wordt toegevoegd die niet in de inventaris staat, faalt de check.
  • Gebruikers-tabellen hebben geen IP-kolommen — de probe zoekt in elke gebruikers-tabel naar kolommen genaamd client_ip, ip_address, ip, remote_ip, created_ip, last_ip of source_ip. Een enkele vondst zou de check laten falen. Het passende resultaat betekent dat de database letterlijk geen kolom heeft waar een IP-adres in kan staan.
  • Database query logging uitgeschakeld — PostgreSQL-instellingen worden live gelezen: log_duration=off, log_statement=none, log_connections=off, log_disconnections=off, log_min_duration_statement=-1. Als iemand ooit een van die vlaggen aan zou zetten "om iets te debuggen", vangt het rapport dat binnen 7 dagen op.
  • Geen analytics SDKs in dependencies — de probe parseert de exact geinstalleerde Python-packages op de draaiende API (22 packages) en zoekt tegen een deny-list: sentry-sdk, datadog, newrelic, mixpanel, segment-analytics-python, posthog, rollbar, bugsnag, google-analytics, amplitude, heap, fullstory. Een passend resultaat betekent dat geen van de gebruikelijke stille data-lekkende libraries in de omgeving zit.
  • Geen audit- of log-extensies geinstalleerd — de probe queryt pg_extension tegen een deny-list: pgaudit, pg_stat_statements, pg_qualstats, auto_explain. Dat zijn Postgres-extensies die query-historie stil kunnen vastleggen. Alleen pgcrypto en plpgsql zijn geinstalleerd — beide noodzakelijk, geen van beide logt iets.

Schema-toelichtingen — waarom we de "enge" kolommen juist laten zien

Het rapport bevat ook een korte toelichtings-sectie. Sommige van onze kolomnamen ogen alarmerend naast een "geen logs"-claim, en in plaats van ze in een achterkamer te verstoppen leggen we ze allemaal op dezelfde pagina uit. Verstoppen zou het hele punt van deze pagina ondermijnen.

  • vpn_support_tickets.log_data — een debug-bundel die een gebruiker vrijwillig meestuurt bij een support-ticket. Gekoppeld aan de include_logs boolean op dezelfde rij. Wordt nooit geschreven voor VPN-verkeer; alleen ingevuld wanneer iemand zelf op "logs meesturen" klikt in het support-formulier.
  • vpn_users.test_password — een plaintext-wachtwoord voor App Store- en Google Play-reviewaccounts. Apple en Google eisen beide een werkende testlogin om app-updates goed te keuren. Een database CHECK-constraint (chk_test_password_only_for_test) maakt het fysiek onmogelijk om deze kolom te vullen op een rij waar is_test_account false is.
  • vpn_users.notes — een vrij tekstveld voor operator-notities (refund-context, support-follow-ups). Nooit zichtbaar voor andere gebruikers; alleen leesbaar voor level-9 admins. Elk AVG-inzageverzoek bevat dit veld in de export.
  • vpn_subscriptions.raw — de exacte webhook-payload die we van Stripe / Apple / Google ontvangen voor elk subscription-event, letterlijk opgeslagen om billing-disputes te kunnen reconcilieren. Bevat geen informatie buiten wat de betalingsprovider zelf al over dezelfde transactie heeft.

De volledige JSON-payload en hoe je het zelf kunt verifieren

Onderaan de rapport-pagina renderen we het exacte JSON-document waarover de SHA-256 is berekend. Iedereen kan er doorheen scrollen: de checks, de findings-arrays, elke tabel en elke kolom in de data-inventaris, de Postgres-instellingen, de geinstalleerde extensies, de dependency-lijst en de summary. Het formaat is stabiel (schema_version: 2) zodat twee rapporten uit verschillende weken direct te diffen zijn. Om de hash zelf te verifieren canoniseer je de JSON (gesorteerde keys, geen extra whitespace) en bereken je een SHA-256 over de bytes. Komt jouw resultaat overeen met de hash in de header-kaart, dan is het rapport intact. Komt het niet overeen, dan is er iets mis en willen wij dat weten.

Wat er tussen weken verandert

Omdat het rapport automatisch uit het live systeem wordt gegenereerd, zal het veranderen wanneer het systeem echt verandert. In een typische week kan total_packages een beetje omhoog of omlaag als we dependencies updaten, of kan endpoint_count onder /beta veranderen als we beta-features toevoegen of uitfaseren. Wat nooit zou moeten veranderen is de set categorieen ("geen IP-kolommen", "geen query-logging", "geen analytics SDKs", "geen audit-extensies") — elke verandering daar zou overall_pass op false zetten en een rode "Te beoordelen"-pil tonen in plaats van een groene. Zie je dat ooit gebeuren, dan zijn wij je een uitleg verschuldigd en het rapport is zo ontworpen dat die uitleg onvermijdelijk is.

Waarom dit eerlijker is dan een jaarlijkse PDF

Een jaarlijkse PDF is een momentopname geschreven door mensen. Hij kan opnieuw worden geredigeerd, anders worden geformuleerd en stilletjes worden geupload. Een wekelijkse ondertekende attestatie die door de machine zelf wordt gegenereerd kan dat niet. De database-rij van elk rapport wordt gesleuteld op ISO-week; de hash staat vast op het moment van creatie; en elke regel van de payload is een directe lees-actie op de live infrastructuur. Zouden we ooit stilletjes een claim moeten afzwakken, dan zouden de checks falen en zou de pagina dat laten zien. Dat is het echte verschil tussen privacy beloven en privacy bewijzen.

Hoe je het rapport gebruikt als gebruiker

Open nexun.io/transparency voor een oogopslag van de huidige week: canary-kleur, infrastructure-statuspil, SHA-256-vingerafdruk. Klik door naar /transparency/infrastructure om elke check te lezen. Bookmark de pagina en open hem een week later — de Report ID (2026-Wxx-...) en de hash moeten beide veranderd zijn, als bewijs dat de attestatie live is. Verandert er iets in de "never-log"-sectie of de check-lijst zonder publieke aankondiging van ons, beschouw dat dan als een reden om vragen te stellen. Daar is dit rapport precies voor bedoeld.

Bekijk het live Infrastructuurrapport
Wekelijkse SHA-256 attestatie met 7 geautomatiseerde checks

Download Nexun VPN

Probeer Nexun VPN gratis -- bescherm je privacy met WireGuard-encryptie en Privacy Logging.

Gratis downloaden

FAQ

Hoe reproduceer ik de SHA-256 hash zelf?

Kopieer de volledige JSON-payload onderaan /transparency/infrastructure, canoniseer hem met gesorteerde keys en UTF-8 (Python: json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()), en bereken sha256 over die bytes. De 64-tekens hex die je krijgt moet gelijk zijn aan de hash in de header-kaart. Zo ja, dan is het rapport dat je las precies het rapport dat wij ondertekenden.

Waarom mag test_password plaintext zijn?

Omdat Apple en Google beide eisen dat hun reviewers tijdens app-review kunnen inloggen, en een gehashte password daar niet voor gebruikt kan worden. We isoleren het risico met een CHECK-constraint: chk_test_password_only_for_test verhindert fysiek dat een plaintext-password wordt opgeslagen op een rij waar is_test_account niet true is. De kolom bestaat dus, maar kan alleen ooit credentials bevatten voor accounts die geen echte gebruikers zijn.

Hoe zou een falend rapport eruit zien?

De header-pil zou rood worden met "Te beoordelen" in plaats van groen, de falende check-regel zou van pass naar fail springen, en de findings-array eronder zou exact het item noemen dat de fail triggerde (een bestand dat verscheen in /var/log, een IP-kolom gevonden in een gebruikers-tabel, een onverwachte Postgres-extensie, etc). De SHA-256 hash van een falend rapport zou nog steeds geldig zijn — hij zou simpelweg een ander, eerlijk verhaal over het systeem die week ondertekenen.

Gerelateerde artikelen