23 min read

Your Age Gate Has Never Been Tested on a Child

Regulators grade age assurance on effectiveness, and effectiveness is an empirical claim. But the population your gate exists to exclude is the one population you cannot lawfully recruit into a test set — and Ofcom itself declined to set a numerical accuracy threshold because the testing methodology is not yet mature. Your sandbox returns whatever answer you asked it for. Here is the part of an age gate you can actually test, the part you must buy as evidence, and how to ship a control you can never fully verify.

Editorial illustration on a deep slate-navy background: a verification gate flanked by a bright test harness that loops back on itself in a closed circuit, while the population the gate is meant to stop stands outside the harness entirely, drawn as faint unreachable tokens behind a dashed boundary. Abstract, no faces, no people, no readable text.

On 4 September 2026, Ofcom fined the provider of a pornography site £700,000 for failing to run age checks between July and November 2025, plus a further £30,000 for ignoring a legally binding information request (Ofcom). Routine enforcement by now. What is worth reading twice is the sentence describing what the site did afterwards.

The provider, Ofcom wrote, “has since introduced a form of age assurance capable of being highly effective.”

Capable of being. Not is. The regulator, describing a deployment it had just spent months investigating — one the provider has since geoblocked from the UK anyway — would commit only to the claim that the method belongs to a category that can work. Whether the thing actually running on that site correctly separates a 17-year-old from an 18-year-old is a question the sentence carefully does not answer, because nobody — not the operator, not the vendor, not Ofcom — has measured it.

That gap between capable of being and is is where every age assurance programme currently lives. And it is not a documentation gap or a diligence gap. It is a testing gap, and it has a structural cause: the population your age gate exists to exclude is the one population you are not permitted to assemble into a test set.

The regulator has already admitted this

Ofcom’s four criteria for highly effective age assurance are technical accuracy, robustness, reliability and fairness. Read the definitions of the first two closely, because the industry quotes them constantly and almost never notices what they say about where the evidence comes from.

Technical accuracy is the degree to which a method correctly determines age under test lab conditions. Robustness is the degree to which it does so in actual deployment contexts. Those are two different experiments, run by two different parties, on two different populations. One is a laboratory evaluation of a model. The other is a field measurement of your integration. No single party holds both.

Then there is the concession, buried in Ofcom’s January 2025 guidance announcement and rarely quoted since. Ofcom decided not to set numerical thresholds for highly effective age assurance — no 99% accuracy bar, no published false-acceptance ceiling. The stated reason was that numerical thresholds might complement the four criteria “in the future, pending further developments in testing methodologies, industry standards, and independent research” (Ofcom).

Sit with that. The regulator cannot tell you what number to hit, because the discipline does not yet know how to measure the number. And in the meantime it fines you for effectiveness anyway. You are being graded on an empirical property of your system using a methodology that the grading authority has publicly said is not mature.

This is not an argument that enforcement is unfair. Children are getting through age gates; that is the whole problem, and “the metrology is immature” has never been a defence for shipping a safety control that does not work. It is an argument that your engineering process needs to be honest about which parts of the control it can verify and which parts it is taking on trust — because right now most teams cannot tell you which is which.

The circularity at the centre of the problem

Here is the test you would write if the law let you.

Recruit a cohort of a few thousand people aged 13 to 25, stratified by skin tone, gender presentation, disability and device quality. Run every one of them through your production age gate. Compare the gate’s decision against their true date of birth. Compute false acceptance at your threshold, broken down by demographic. Ship when the numbers are good, and re-run quarterly against drift.

Every step involving the under-18s in that cohort is a regulated act, and several are the exact acts your compliance programme exists to prevent.

Collecting a 14-year-old’s facial image is processing a child’s personal data. Where you are relying on consent as your lawful basis, that engages GDPR Article 8 and its requirement for authorisation by the holder of parental responsibility, below an age each member state sets somewhere between 13 and 16. For the under-13s in the cohort, COPPA requires verifiable parental consent — and note that COPPA stops at 13, so the 13-to-17 band that matters most to your gate is governed by a patchwork of state law rather than by the one federal statute everyone cites. If the image is processed biometrically, you are arguably in Article 9 special-category territory too, though that reading is genuinely contested: Article 9 covers biometric data processed for the purpose of uniquely identifying a person, and the ICO’s position is that age estimation used only to categorise does not meet it. The dispute is live, and it resolves against you on the document path, where a face match does identify uniquely. In Illinois, meanwhile, a scan of face geometry is a BIPA collection event with a per-person statutory damages figure attached, contested interpretation or not.

Then the circularity. To obtain verifiable parental consent at scale, you must first verify that the consenting adult is an adult — which requires an age check. You need a working age check to build the test set that proves your age check works.

Even for teams willing to fund the ethics board, the consent infrastructure and the parental verification layer, the resulting asset has a property that makes it nearly worthless as a regression suite: it expires. A test set of 17-year-olds is a test set of 18-year-olds within a year. The precise cohort your gate is most likely to get wrong — the 16-to-19 band around the threshold — is the cohort that migrates out of the label fastest. You cannot maintain a stable golden dataset for the boundary case. There is no CI job you can point at it.

And the standard product-engineering fallback is unavailable too. You cannot A/B test an age gate on the population that matters, because the control arm is “admit the children” and that is not an experiment, it is the offence. Every other funnel in your product can be measured by randomised holdout. This one cannot.

The research says the hole is real, and it points one way

It would be convenient if this were only a paperwork problem — if models trained on adult-only data happened to work fine on minors and the missing test set were a formality. The evidence says otherwise, and it says something worse than “less accurate.”

A May 2026 benchmark paper formalised exactly this constraint. Petrucci, Ribeiro and Avila built a generalised zero-shot protocol for facial age estimation that excludes children’s data from training entirely: samples aged 18–59 for training, validation and testing, samples under 18 held out for zero-shot evaluation only, and 60+ as an unseen validation set. They then ran nine state-of-the-art age estimation methods through it (arXiv:2605.29230).

All nine failed to generalise to the unseen age groups, with performance degrading by 46.4% on average relative to the supervised baseline and by up to 52.8% at worst. Read that as error growing by roughly half again rather than as accuracy halving — the figure is the change in error between the supervised and zero-shot protocols — and it is still a very large regression on the one age band the entire regulatory regime is about.

The direction of failure matters more than the magnitude. The models did not degrade into noise. They “systematically anchor predictions for unseen ages to nearby seen classes” — seen-class bias, a known property of zero-shot learning. In plain terms: a model that has never lawfully seen a 15-year-old does not return an uncertain answer when shown one. It returns the nearest thing it was trained on, and the nearest thing it was trained on is an adult.

That is a structural bias toward false acceptance, concentrated precisely at the threshold, produced by the ethical constraint itself. The more scrupulous the training-data policy, the stronger the pull toward “adult” at the boundary. This is not an argument for training on children’s faces. It is an argument that the buffer you set above your legal threshold is doing far more work than most teams think, and that the vendor benchmark you were shown probably did not measure it under this constraint. We have written separately about reading NIST FATE numbers like an operator and about choosing a challenge-age buffer; this finding is the reason both of those decisions deserve more conservatism than the headline error rate suggests.

Your sandbox is a mock, and a mock is not a test

Ask an engineering team whether their age gate is tested and you will usually be shown a green CI suite built against the vendor’s sandbox. Every serious identity vendor ships one: test credentials, canned document images, deterministic outcomes, a documented way to force an approve, a decline, or a document-authenticity failure.

These environments are genuinely useful and every integration should exercise them. But be precise about what they establish. A sandbox verifies that when the provider returns a decision, your system does the right thing with it. It cannot verify that the provider returns the right decision, because the sandbox decision was selected by you, in the request, on purpose. The fixture that “fails” fails because you asked for the failing fixture.

So the suite is green, and the only proposition it has proved is that your webhook handler parses JSON. The gate itself — the classifier, the threshold, the boundary behaviour on a real human face — has never been exercised by anything except a stub that agrees with you.

This is not a criticism of sandboxes; it is a criticism of what teams conclude from them. The failure mode is specific and common: a passing integration suite creates the felt sense of a tested control, which removes the pressure to build the evidence that would actually survive an information request. The team that knows its gate is untested keeps a risk register entry. The team with the green dashboard does not.

The four tiers you can actually test, and the one you cannot

The useful move is to stop treating “the age gate” as one testable object. It is a stack of five layers with wildly different testability. Four of them are ordinary software you are fully capable of verifying, and — this is the part worth internalising — most real production incidents live in those four, not in the model.

Tier 0 — Threshold arithmetic, which you own completely

This is deterministic, side-effect-free, and belongs at 100% branch coverage. It is also where a genuinely embarrassing share of age bugs live, because “is this person 18” looks trivial and is not.

The cases that break real systems:

  • “Eighteen years ago today” is not a subtraction. Naive implementations do now - dob >= 18*365 or 18*365.25 days. Across eighteen years the leap-day drift is four to five days wide, which means a real window in which your gate and a regulator’s calculation disagree about whether a specific user was an adult.
  • 29 February birthdays. A user born 29 February 2008 turns 18 on a date that does not exist in 2026. Your calendar library has an opinion — 28 February or 1 March — and different libraries in different services in your own stack may not share it.
  • Timezone at the boundary. A user in UTC+13 turns 18 up to thirteen hours before your UTC server agrees. Whose midnight governs? Pick one, write it down, test both sides of it. A user who is 18 in Auckland and 17 on your database is a false decline; invert the offset and the identical bug admits someone your gate should have stopped.
  • Date versus timestamp. A DOB stored as a timestamp acquires a time component from somewhere, and a midnight-UTC timestamp rendered in a negative-offset locale is the previous day. This silently shifts a birthday by one day for a subset of users forever.
  • Non-Gregorian input. If you accept a date of birth from a document using a Solar Hijri, Hijri or Buddhist calendar, the conversion is a correctness requirement, not a localisation nicety.

Every one of these is a pure function. Property-based testing is the right tool: generate a date of birth and an evaluation instant, assert the decision matches a reference implementation computed independently, and let the generator find the leap-year and boundary cases you did not think of. This is a day of work and it is the highest-value testing in the entire programme, because these bugs are silent, systematic, and apply to your whole user base rather than to a tail.

Tier 1 — The decision state machine

An age decision is not an event, it is a record with a lifecycle, and the lifecycle is fully testable because it involves no minors — only state transitions.

Verify: that a decision expires when your policy says it does and that expiry triggers re-verification rather than silent trust; that a decision is scoped to the identity that earned it and does not survive an account merge, a device handoff or a session transfer; that a step-up from a Check to a full document Verification correctly supersedes the weaker record rather than sitting alongside it; that revocation propagates; that an appeal moves an account through review and reinstatement without leaving orphaned state; and that the audit record written at decision time is complete enough to reconstruct the decision months later, including method, threshold, confidence and policy version.

That last one is what an information request actually asks for. The £30,000 component of the penalty above was for failing to produce information, not for failing to check ages — a distinction we examined in more detail when age assurance became an evidence problem.

Tier 2 — The integration contract

Consumer-driven contract tests against the vendor’s API schema, run in CI, so a provider’s response change breaks your build rather than your gate. Then the failure paths, which are the ones nobody writes:

  • Webhook signature verification, including rejection of a validly signed message replayed twice, and idempotent handling of the duplicate delivery every provider eventually sends.
  • Out-of-order delivery: the result webhook arriving before the session-created acknowledgement.
  • Fault injection at the boundary — 503, timeout, malformed body, TLS failure, SDK failing to load at all — with an assertion on what your gate does in each case. That assertion is a compliance decision, and it deserves to be written as a test rather than discovered during an outage. We have argued the fail-open versus fail-closed case at length; the testing point is narrower and simpler. If your degraded mode has no test, you do not have a degraded mode. You have an unreviewed catch block.

Tier 3 — Adversarial behaviour, testable entirely with adults

This is the tier teams skip because it feels like it needs minors. It does not. Every circumvention technique that matters can be exercised by consenting adult testers, because the attacker’s method is age-independent even though the attacker’s motive is not.

Run a red team against your own gate with adult staff: virtual camera and injection attacks against the capture path (the certification gap here is real); replay of a previously successful capture; presentation attacks with printed and screen-displayed images; borrowed-credential flows where one adult completes the check and a second person uses the session; shared-device and family-tablet scenarios; and retry-budget exhaustion, where an attacker with unlimited attempts eventually finds a frame the estimator scores high enough.

That last one deserves emphasis because it is testable and rarely tested. If your gate allows unbounded retries, its effective false-acceptance rate is not the model’s published rate — it is the probability that at least one of N attempts succeeds, which converges on certainty as N grows. You can measure this yourself, today, with one adult and a script. It is the closest thing to a direct empirical test of your gate’s weakness that you are legally permitted to run.

Tier 4 — The model, which is not yours to test

Everything above is your responsibility and within your capability. The classifier is neither. You cannot benchmark a face age estimator without a labelled dataset spanning minors, and you are not in a position to build and maintain one — as the section above set out, the consent machinery alone is circular, and the asset expires annually even if you build it.

So stop trying to test it, and start procuring evidence about it — which is a different activity, with a different artefact, owned by a different function.

The evidence file has two authors, and each assumes the other wrote it

The most common failure we see is not a bad test suite. It is a division of responsibility that nobody ever stated out loud, in which the platform assumes the vendor holds the accuracy evidence and the vendor assumes the platform holds the deployment evidence, and neither has the other’s half when the information request lands.

Map it explicitly. The regulator’s four criteria split cleanly across the two parties.

Technical accuracy is the vendor’s to prove, and yours to demand in writing. Ask for the independent laboratory evaluation, not the marketing summary: NIST FATE AEV submissions with the algorithm identifier that matches what is actually deployed on your account, conformance against ISO/IEC 27566, presentation-attack detection testing to ISO/IEC 30107-3, and error rates broken down at your threshold rather than as a headline mean absolute error. Ask specifically what age range the evaluation covered and how the under-18 samples were obtained and consented — a vendor that cannot answer this either has an ethics problem or a coverage gap, and you need to know which. Ask when the deployed model version was last evaluated, because the answer is often “an earlier one was.” Our vendor security checklist covers the adjacent questions.

Robustness, reliability and fairness in your traffic are yours, and the vendor cannot produce them. No lab result tells you how the method performs on your users, your devices, your lighting, your onboarding copy, your retry policy. That measurement only exists if you instrument it. It is also the half a regulator is most likely to ask about, because it is the half specific to the service under investigation.

Write the split into the contract and into the runbook, name an owner for each half, and review both on the same cadence. The point is not the document. The point is that the question “who holds the evidence that this works” currently has no answer at most companies, and it is a bad question to first consider under a statutory deadline.

What you can legitimately measure in production

You cannot compute a true false-acceptance rate without ground truth you are not allowed to have. You can, however, build a set of proxies that are lawful, cheap, and considerably better than nothing — and that, unlike a lab number, describe your actual deployment.

Appeal and overturn rate is your best empirical signal, and most teams throw it away. Every adult wrongly blocked by your gate who appeals and is reinstated after a stronger check is a confirmed false positive with ground truth attached — supplied voluntarily, by an adult, for a purpose they initiated. Instrument that pipeline properly and you have a real error measurement at your real threshold on your real traffic, at no privacy cost. The rate at which appeals are upheld is the closest thing to a live accuracy metric you will ever get, which is a good reason to make appeals easy rather than to bury them. It is also the metric that turns adult lockout from a support complaint into an engineering input.

Threshold-band distribution tells you how much of your traffic clusters near the decision boundary. If a large share of decisions sit within a point or two of the cut, small model drift will move a large number of users, and your gate is more fragile than a point estimate of accuracy suggests.

Cohort drift over time. The distribution of confidence scores for a fixed segment should be stable. When it moves without a release, either your user population changed or the vendor shipped a model update you were not told about. Both are things you want to detect from your own telemetry rather than from a support queue.

Step-up conversion asymmetry. Among users the estimator declined who then completed a document verification, what fraction turned out to be adults? That is a directly measured false-positive rate on a self-selected sample. The selection bias is real and you should say so in the write-up, but it is a measurement, and it is yours.

Fairness slices on the metrics you already hold. You do not need demographic labels you are not entitled to collect. Segment by device class, camera resolution, operating system version, locale and time of day. Large divergence in decline rates across cheap and expensive handsets is a fairness finding under Ofcom’s fourth criterion, and it is sitting in your existing logs.

None of these require a single additional byte of a minor’s personal data. All of them are the kind of evidence that makes a response to an information request read like an engineering document rather than a press release.

Shipping a control you cannot fully verify

The honest position is that you will deploy an age gate whose central component you have not tested and cannot test. That is a design constraint, not an excuse, and mature engineering practice already has a shape for shipping under exactly this kind of uncertainty.

Run it in shadow first. Deploy the check so it evaluates every session and writes a decision, but enforces nothing. Watch the distribution for a fortnight. You learn your decline rate, your latency, your capture-failure rate and your threshold-band shape before a single real user is blocked, and the cost of being wrong is a log line.

Canary by segment, not by percentage. A random 5% rollout spreads a systematic error thinly across everyone, which is the hardest failure to see. Enabling enforcement fully for one locale or one device class surfaces population-specific breakage immediately, which is the failure mode you are actually worried about.

Pre-register the metric. Before the rollout, write down what number would cause you to stop, and where it will be read from. Deciding after the fact whether a 9% decline rate is acceptable is not a decision, it is a rationalisation. This is also the artefact that demonstrates a considered approach if anyone later asks how the threshold was chosen.

Keep it reversible, and rehearse the reversal. A kill switch that reverts to the previous policy without a deploy, tested in staging, owned by someone on call. Age gates fail at the worst possible moment — a model update, a traffic spike, a provider incident — and the interval between noticing and reverting is the interval in which either children get through or adults are locked out.

Treat enforcement against your existing user base as a separate, harder programme. Applying a probabilistic classifier retroactively to accounts that already exist is not a rollout; it is a destructive batch job graded by a model with a known error rate, and it needs its own plan and its own reversibility guarantees. We have written that one up in full: back-book remediation and the missing undo button.

How Xident is built for this

Most of what makes an age gate testable is architectural, decided long before anyone writes a test.

We separate the two billable operations deliberately — a Check (browser-based age check, liveness, returning-user lookup, OAuth) and a Verification (document and ID with OCR and face match) — so that step-up is an explicit, logged transition between methods rather than a hidden branch inside one opaque call. That separation is what makes Tier 1 testable at all: the state machine has named states, and the transition between a probabilistic method and a documentary one is a thing your tests can assert on.

Every decision is stored as a decision record rather than a retained artefact: the outcome, the method, the threshold, the confidence, the policy version, and the timestamp — not the passport image. That is the right privacy posture, and it is also what makes the production metrics above computable. You cannot run cohort drift analysis on data you deleted, and you should not be keeping the images. The record is the thing you export when someone asks you to evidence a measure you had in use eleven months ago.

The free sandbox exists so this work can happen before procurement, not after. It grants 1,000 Checks and 100 document Verifications, once — these are one-time allowances, not a monthly recurring quota. The 100 Verifications are there specifically so an engineering team can exercise the real document path end to end, including the failure branches, before committing to a paid plan. That allowance is deliberately sized for a test plan rather than for a trial: enough to drive the Tier 0 through Tier 3 work above against a real integration, on a real document flow, before anyone signs anything.

And we publish the accuracy evidence we can publish, and are precise about which half it is. Lab evidence for the methods; your deployment evidence stays yours, because it is the only place it can be generated.

The uncomfortable summary

An age gate is a safety control whose central component cannot be tested by the party deploying it, graded on an empirical standard whose regulator has said the measurement methodology is not yet mature, aimed at a population that cannot lawfully be recruited into a test set, using models whose error on exactly that population climbs sharply when they are trained under the ethical constraint — and that fail toward “adult” rather than toward uncertainty when they do.

None of that makes the obligation go away. It does mean that “we tested it” is a claim most teams cannot currently support, and that the green sandbox suite is not the support they think it is.

What you can do is draw the line honestly. Test the perimeter to a standard you would defend in a code review: the arithmetic, the state machine, the contract, the failure paths, the adversary. Buy the model evidence, in writing, matched to the version actually deployed on your account. Measure your own deployment, using the proxies that cost no one their privacy. Ship it in shadow, canary it by segment, and keep the revert one switch away.

That is not the same as knowing your gate works. It is the strongest position available, and it is a great deal better than the alternative, which is a passing test suite that only ever asked itself questions it had already answered.


Xident provides age verification and age estimation infrastructure built around decision records rather than retained documents, with a free sandbox that includes a one-time allowance of 1,000 Checks and 100 document Verifications so you can exercise the full path — including the failure branches — before you pay for anything. Talk to us about what evidence your programme is missing.

Share this article

Ready to implement age verification?

Get started in minutes with our simple SDK. Free trial includes 100 verifications.

Book a 20-minute demo