Skip to main content
← /security overview
TRACKLAYER · CONSENT + PII FIREWALL

PII never leaves your pipeline without consent.
Period.

Every event hits a policy gate before it goes to Meta, Google, or any other platform. Default policy redacts and hashes; the policy DSL lets you write per-region, per-platform, per-event rules your DPO can audit on a Friday afternoon without a panic attack.
default-policy.dsl
when consent.ad_storage = denied then redact(em, ph, ln, fn, zp)
when platform = any then hash(user_data.*)
when event_name ~/^health_/ then block
custom-policy.dsl · Pro+
when region = EU and consent.ad_storage = denied then block
when platform = meta then hash(user_data.phone)
when event_name ~/^pregnancy_|^fertility_/ then block
// THE PROBLEM

Why is your CAPI sending hashed-but-not-consented PII to Meta?

Most CAPI tools — Stape, server-side GTM, custom Worker setups — hash em and ph before forwarding. That's the easy part. The hard part nobody implements is gating the send on actual consent state. A SHA-256 hash of an unconsented email is still personal data under GDPR Art. 4(1) and still triggers Schrems II concerns when it lands in a US ad platform.

The CNIL fined a French retailer €600,000in 2024 for exactly this pattern: a CMP that captured opt-out, an ad pixel that ignored it, and server-side hashing the DPO believed was “anonymous.” The Irish DPC has open inquiries into eight large e-commerce brands on the same hashed-CAPI angle. The EDPB's 2024 guidance on tracker-based marketing is explicit: hashing is not anonymization.

Your DPO can't prove compliance from a Stape dashboard. There's no per-event log of what was sent, what was blocked, and which consent string was active at send time. When the regulator asks for audit evidence covering Q1, you need a CSV export you can hand to legal — not a screenshot of a GTM container.

§ 01

applyFirewall runs before every send.

One function, five steps, deterministic outcome. Called from the consumer Worker the moment an event is dequeued — before any platform.sendEvent() can fire.
§ 01

Event arrives

Consumer Worker dequeues a normalized event from the events queue. Event already carries consent_state (ad_storage, ad_user_data, analytics_storage, ad_personalization).

§ 02

Firewall reads policy + consent

applyFirewall loads the merchant's active policies (default + custom), parses the consent_state, and resolves region from event.geo.country.

§ 03

Evaluates rules in priority order

Custom rules (Pro+) evaluate first; default rules apply last as fallback. First matching block-rule short-circuits. modify-rules accumulate.

§ 04

Returns action + sanitized event

{ action: 'send' | 'block' | 'modified', sanitized_event, log_entry }. The Worker only calls platform.sendEvent() when action !== 'block'.

§ 05

Logs persist 30 days rolling

log_entry written to firewall_logs (event_id, platform, action, rule_matched, consent_state, sanitized_fields, region, ts). Exportable as CSV for the legal team.

packages/core/src/firewall/consent-firewall.ts
export interface FirewallResult {
  action: 'send' | 'block' | 'modified';
  sanitized_event: NormalizedEvent;
  log_entry: FirewallLogEntry;
}

export function applyFirewall(
  event: NormalizedEvent,
  platform: PlatformId,
  policies: ConsentPolicy[],
  consent_state: ConsentState,
): FirewallResult {
  const rules = compilePolicies(policies);
  for (const rule of rules) {
    if (rule.matches(event, platform, consent_state)) {
      if (rule.action === 'block') {
        return blockResult(event, platform, rule);
      }
      if (rule.action === 'modify') {
        event = rule.apply(event);
      }
    }
  }
  return sendResult(event, platform);
}

// Sample evaluation
const r = applyFirewall(
  { event_name: 'Purchase', user_data: { em: 'a@b.co' }, geo: { country: 'DE' } },
  'meta',
  defaultPolicies,
  { ad_storage: 'denied', ad_user_data: 'denied' },
);
// → { action: 'block',
//     log_entry: { rule_matched: 'region=EU and consent.ad_storage=denied' } }
§ 02

Default at Starter, custom DSL at Pro.

Two surfaces over one engine. The default policy ships safe-by-default; the DSL editor unlocks merchant-specific rules without touching code.
BASIC REDACT · STARTER+

Safe defaults, zero config.

Auto-applied to every merchant on Starter and above. Three rules cover ~90% of GDPR/CCPA exposure for a typical DTC store. You don't see the editor; you see firewall_logs proving it's working.

  • Redact PII (em, ph, ln, fn, zp) when consent.ad_storage = denied
  • Hash all user_data fields with SHA-256 before any platform send
  • Block sensitive event_names matching ^health_, ^pregnancy_, ^fertility_
POLICY DSL · PRO+

Write rules per-region, per-platform, per-event.

Full grammar: when ⟨condition⟩ then ⟨action⟩. Conditions on consent state, region, platform, event name (regex), and user_data fields. Actions: block, redact(...), hash(...).

  • Per-region overrides (EU / UK / US-CA / US / APAC)
  • Platform-scoped hashing (e.g. always hash phone for Meta only)
  • Regex event-name blocking for sensitive verticals
  • Live syntax check + dry-run against last 1k events
§ 03

What it looks like in TrackLayer.

/settings/firewall — one page, three panels. Editor, log, export.
A · POLICY EDITOR
1 when region = "EU" and
2 consent.ad_storage = "denied"
3 then block
4
5 when platform = "meta" then
6 hash(user_data.phone)
✓ syntax ok · dry-run: 412/1000 events affected
B · FIREWALL LOG · LAST 5
14:02:11metasend
default · hash before send
em→sha256, ph→sha256
14:02:14googleblock
region=EU and consent.ad_storage=denied
14:02:17tiktokmodified
default · redact PII when consent denied
removed: email, phone
14:02:20metablock
event_name~/^pregnancy_/
14:02:23pinterestsend
default · hash before send
em→sha256
C · AUDIT EXPORT
FROM
2026-01-01
TO
2026-03-31
47,318 rows · 4.2 MB
§ 04

CSV export your DPO will actually use.

One endpoint, one CSV, the columns regulators ask for. No screen-scraping a dashboard at 5pm on the day before the audit.
firewall_audit_q1_2026.csv
timestamp,event_id,platform,action,policy_rule_matched,consent_state,sanitized_fields,region
2026-01-04T14:02:11Z,evt_8aJ2,meta,send,default·hash_before_send,"granted/granted/granted/granted",em→sha256;ph→sha256,DE
2026-01-04T14:02:14Z,evt_8aJ3,google,block,"region=EU and consent.ad_storage=denied","denied/denied/granted/denied",,DE
2026-01-04T14:02:17Z,evt_8aJ4,tiktok,modified,default·redact_when_denied,"denied/denied/granted/denied","removed: em,ph",FR
2026-01-04T14:02:20Z,evt_8aJ5,meta,block,"event_name~/^pregnancy_/","granted/granted/granted/granted",,NL
2026-01-04T14:02:23Z,evt_8aJ6,pinterest,send,default·hash_before_send,"granted/denied/granted/granted",em→sha256,IE
ENDPOINT
GET /v1/firewall/audit-export
?from=2026-01-01&to=2026-03-31

Returns up to 90 days per call (firewall_logs retention). API-key authed; rate-limited at 4 calls/min so a junior engineer can't accidentally DoS your own audit. Streams a gzipped CSV for batches over 100k rows. Same payload is reachable from the dashboard download button.

// HOW IT COMPARES

Stape and Elevar still don't gate on consent.

Cross-checked against public docs, April 2026. We'll update this row by row when they ship features.
SOURCES · PUBLIC DOCS · 04·2026
CAPABILITYTRACKLAYERStapeElevar
Consent gating before send (block on denied)~
Policy DSL (when/then rules)
Audit log retention (per-event, exportable)30d rolling7d UI only
CSV export for DPO (full schema)
Per-region policy overrides
Sensitive event_name blocking (regex)
Default safe-policy on signup~
TRACKLAYER
Consent gating before send (block on denied)
Policy DSL (when/then rules)
Audit log retention (per-event, exportable)
30d rolling
CSV export for DPO (full schema)
Per-region policy overrides
Sensitive event_name blocking (regex)
Default safe-policy on signup
Stape
Consent gating before send (block on denied)
Policy DSL (when/then rules)
Audit log retention (per-event, exportable)
CSV export for DPO (full schema)
Per-region policy overrides
Sensitive event_name blocking (regex)
Default safe-policy on signup
Elevar
Consent gating before send (block on denied)
~
Policy DSL (when/then rules)
Audit log retention (per-event, exportable)
7d UI only
CSV export for DPO (full schema)
Per-region policy overrides
Sensitive event_name blocking (regex)
Default safe-policy on signup
~
§ 05

Three teams, three Friday afternoons saved.

§ A

EU DTC store passing GDPR audit

Berlin-based skincare brand, €4M ARR. CNIL-style audit triggered by a customer complaint. Exported 90 days of firewall_logs as CSV in 30 seconds, handed to legal counsel. Showed every consent-denied event was blocked from Meta CAPI. Audit closed in 11 days with zero remediation.

§ B

Agency managing 12 stores, 6 jurisdictions

Stockholm performance agency with merchants in DE, FR, ES, UK, US, AU. One policy template, six per-region overrides. The agency rolled out the same DSL to all 12 stores in a morning — UK gets ICO-style strict consent, US gets CCPA do-not-sell, AU gets the lighter Privacy Act profile. One audit log per merchant, isolated.

§ C

SaaS pre-empting CCPA enforcement

LA-based subscription brand reading the new CPPA enforcement memo. Added one rule: when region=US-CA and consent.ad_personalization=denied then block. Took two minutes. Caught 8% of California events that the previous tag-manager setup was leaking. Three months of clean logs before the first regulatory advisory in the vertical.

// AVAILABLE ON

Starter onwards.

STARTER
$79/mo

Default firewall: redact PII when consent denied, hash before send, block sensitive event names. Auto-applied. firewall_logs retained 30 days rolling. CSV export included.

Start trial
PRO
$599/mo

Everything in Starter plus the policy DSL editor. Custom per-region, per-platform, per-event rules. Live syntax check + dry-run against the last 1k events. Multi-policy support.

See Pro plan
ENTERPRISE
Custom

Custom regional policy templates, dedicated DPA support, extended firewall_logs retention (up to 365 days), private compile-once-deploy-everywhere policy bundles for agency portfolios.

Talk to sales
NEXT

Compliance as code, not as a panicked Friday afternoon.

Default policy ships with every Starter trial. The DSL is a config-only flip on Pro. Your DPO gets the audit log either way.
// RELATED

Other security & compliance features.

We use essential cookies to keep the site secure and functional. Analytics and third-party tags run only with your consent. See our Cookie Policy.

We use essential cookies to keep the site secure and functional. Analytics and third-party tags run only with your consent. See our Cookie Policy.