Whether this visitor may be tracked at all, and by which layer.
Two questions that get conflated and must not be:
- first-party analytics — a cookieless, rotating-hash pageview stored in the site's own database. Nothing is written to or read from the device, so ePrivacy Article 5(3) — the cookie rule — is not engaged, and no banner is required for it. GDPR still governs the processing; the lawful basis is legitimate interest under Article 6(1)(f), available precisely because the data is not collected for advertising.
- ad-platform destinations — Meta, TikTok, Snap, Google. These send identifying data to a third party for advertising. That needs consent where consent law applies, and suppressing only the browser pixel while the server-side Conversions API keeps firing is theatre: the server leg carries more identifying data (hashed email, phone, IP) to the same companies.
allow?/2 answers the first. destinations_allowed?/1 answers the second.
They are separate because answering them together is how a site ends up
either over-collecting or throwing away its own analytics for no legal gain.
Where consent is required
The EEA, the UK and Switzerland. Not everywhere: gating a visitor in Egypt or
the Gulf costs real attribution data and buys nothing, because ePrivacy
consent rules do not reach them. required?/1 is the list.
Global Privacy Control
Sec-GPC: 1 is honoured by default and is checked server-side, from the
header. It is a legally binding opt-out under CCPA/CPRA and a growing set of
US state laws — unlike DNT, which never had legal force, was ignored, and was
removed from Safari. DNT is off by default and available as a courtesy.
Checked server-side for two reasons: the client cannot be trusted to check a signal about itself, and an ad-blocker may well have removed the script that would have.
It ships off
consent_gate: [enabled: true] turns the geographic gate on. The default is
off, because switching it on before a banner exists silently stops tracking
for those visitors, and the honest default is current behaviour until the
decision is deliberate.
Summary
Functions
May pixelex record a first-party event for this visitor?
Whether tracking is permitted, as a plain boolean.
May pixelex forward this event to third-party ad platforms?
The countries where a consent decision is legally required.
Should a banner be shown? Only when a decision is required and none has been
made. nil and "denied" both block; opt-in, not opt-out.
Is a consent decision required for this visitor's country?
Types
@type signals() :: %{ optional(:country) => String.t() | nil, optional(:decision) => String.t() | nil, optional(:gpc) => String.t() | nil, optional(:dnt) => String.t() | nil }
A visitor's signals, as the plug layer extracts them.
:country— ISO-3166-1 alpha-2, from geo lookup or a CDN header:decision—"granted"/"denied"/nil, from the host's banner:gpc— theSec-GPCheader value:dnt— theDNTheader value
Functions
@spec allow?(signals()) :: true | {:denied, :gpc | :dnt | :no_consent}
May pixelex record a first-party event for this visitor?
Returns true, or {:denied, reason} so the caller can report why nothing
was recorded. A silent false here is indistinguishable from a bug.
Whether tracking is permitted, as a plain boolean.
For call sites that only branch and have nowhere useful to put the reason.
May pixelex forward this event to third-party ad platforms?
Strictly narrower than allow?/1: a first-party pageview can be lawful where
a Conversions API call is not.
@spec gated_countries() :: [String.t()]
The countries where a consent decision is legally required.
Should a banner be shown? Only when a decision is required and none has been
made. nil and "denied" both block; opt-in, not opt-out.
Is a consent decision required for this visitor's country?