Product · Age verification

Prove age, not identity.

A holder proves age_over_18 to your site without revealing date of birth, name, or document. Your site gets a yes/no answer, an audit receipt, and a per-site nullifier — an identifier derived differently at every relying party, by design.

Regulatory driver

The regulatory direction is set

Age-assurance rules in the UK and the EU converge on one engineering requirement: confirm an age attribute without collecting, storing, or linking identity.

United Kingdom — Online Safety Act

The Online Safety Act places age-assurance duties on services in scope. How a service meets them is an engineering choice — and an age check that builds a store of identity documents trades a compliance problem for a data-protection one.

European Union — age verification

EU work on age verification centres on one property: showing that a user is over an age threshold without disclosing other personal data. That property — predicate out, identity withheld — is the design brief this verifier is built around.

Regulatory context only — not legal advice. Whether and how any duty applies to your service is a question for your counsel.

How it works

One predicate crosses the wire. Nothing else.

The credential stays in the wallet. Each relying party receives a proof of a single predicate and a nullifier derived for that relying party alone — so two sites see two different values for the same user.

The same wallet presents to two relying parties; each receives the same age answer but a different nullifier, with no shared identifier between the two sites. One user, one wallet full credential stays here proof · scope: rp-a proof · scope: rp-b Relying party A over_18: true nullifier: 7f3a…c1 Relying party B over_18: true nullifier: e92d…84 ✕ no shared identifier
Same user, two sites: the answer is identical, the nullifier differs per relying party. Nullifier values are illustrative.

No PII to the relying party

The disclosed-attribute list is empty: no date of birth, no name, no document number. The response carries the predicate result, the receipt, and the per-site nullifier — nothing else.

Unlinkable by design

Designed and machine-checked for unlinkability properties: identifier-hiding, audit/verifier-split and nullifier-unlinkability / private-presentation theorems are discharged; verifier-view unlinkability, issuer hiding and selective-disclosure privacy are modeled with machine-verified observational-equivalence lemmas but remain partial overall.

A receipt, not a black box

Each check can emit a receipt stating exactly which property was proven — and which wasn't — so you can show your auditor what the check did and did not learn.

Two honest modes

Two modes, stated honestly

Standards interop and cross-site unlinkability are different properties. PSDP implements both — as two explicitly labelled modes, so you always know which one you are running.

Wallet-backed — the standards path

A real OID4VP check of an SD-JWT VC or ISO mdoc age claim: structure, nonce binding, issuer signature. What it gives you: standards transport, no proprietary token. What it does not give you by itself: cross-site unlinkability.

Austria's vck library (A-SIT / the engine behind the ID-Austria Valera wallet) presented an SD-JWT PID to the PSDP verifier over OID4VP 1.0 Final (direct_post + DCQL); PSDP verified the issuer signature and the Key-Binding JWT and accepted (2026-06-08, reproducible harness).

Unlinkable — the ZK path

Adds the per-relying-party scoped nullifier and the predicate proof on top of the standards check. The relying party still learns only the boolean — and two relying parties hold different nullifier values for the same user.

The public POST /verify/age endpoint mints and verifies a real Groth16/BN254 zero-knowledge proof end to end: the holder proves age_over_18 (and a per-relying-party scoped nullifier) in zero knowledge, revealing no date of birth. It is reference-grade: the proving system uses a single-party deterministic trusted setup — not a multi-party ceremony — so it is not production-secure against a determined forger. A real proof takes about 10–15 seconds — the honest cost of real ZK.

Reference ZK circuits additionally checked with Picus (Veridise, Z3) — “properly constrained” — and Circomspect (Trail of Bits) — “no issues found” — plus 14/14 negative-witness tests (circom reference circuits, April 2026 evidence package).

The standards path is tested against the official OpenID Foundation conformance suite (local run, reproducible): 319 of 332 test modules passed, 0 failed (11 warnings, 2 skipped), across OID4VCI, OID4VP and OpenID Federation test plans. Self-run evidence — not an OpenID Foundation certification. See the evidence →

Integration

Integrate in an afternoon

A static drop-in: one div, one script tag. No CDN, no framework, no build step. Underneath is one REST route — POST /verify/age returns the boolean and the receipt.

Drop-in age gate (one div + one script)
<div data-psdp-age-verify
     data-base-url="https://your-verifier.example"
     data-relying-party-id="your-site.example"
     data-min-age="18"
     data-demo="true"></div>
<script src="/path/to/psdp-age-verify.js"></script>

Set data-demo="false" once your users present a real wallet credential. The widget renders the verify button, calls your verifier, and shows the allow/deny result with the privacy note.

Fail-closed on mock proofs

In production mode (PSDP_PRODUCTION=1) the verifier refuses mock proofs — submissions are rejected with mock_crypto_rejected_in_production — and startup checks enforce production configuration before serving.

What this is

PSDP is a reference implementation; production hardening is in progress.

Indicative pricing

You price against exposure, not against compute

Indicative figures, not a rate card. Pricing is finalised on the pilot call.

~$0.010.03 Indicative: per age-verification check, at volume Final per-check pricing depends on volume and deployment model. Ask about a pilot →

Measured (2026-06-06, developer hardware): a real Groth16/BN254 verification costs ~7–22 ms of compute — on the order of 10–20 cents per million verifications — so verification compute is not a pricing floor.

You are pricing against a regulatory exposure and a repeated KYC check — not against a free login library.

All figures on this page are indicative.

Scope & limits

What this is — and what it is not

Read this before the pilot call. It is the same list we will say out loud.

Next step

Pilot it against your age gate

Bring one page you need to gate; we'll bring the verifier and the receipt. Engineering to engineering.