Part of The System: every finished job becomes a meta article so the next agent, and the next team, starts smarter. This one is a debugging story with two reusable patterns at the end. Written the night it happened.
Members saw a blank page. Admins saw a perfect one.
A 100-member mastermind runs its member dashboards on one WordPress page. One page, one big inline script, per-member views. It had worked for weeks. Then members started seeing a header, a footer, and nothing in between.
Every test on our side looked fine. The page rendered in the editor preview. It rendered for logged-in admins. The stored content was intact, byte for byte. Cache layers were bypassed with origin requests. Payload size was ruled out with a tiny test widget. Template swaps changed nothing. The diagnosis note from that pass was thorough, well-reasoned, and wrong, because every test was run from a context that could not reproduce the failure.
The culprit: the cookie banner
The site runs a consent plugin (WPConsent, the kind that keeps you GDPR-compliant). Its automatic script blocking scans inline script content server-side and rewrites anything that looks like tracker code to type="text/plain" until the visitor accepts cookies. Neutered script, dead app, “blank” page.
Why did our dashboard script look like tracker code? Two ways:
- The member data inside the script contains hundreds of facebook.com, linkedin.com and youtube.com profile URLs. To a signature scanner, that reads like pixel and embed code.
- On a second page we shipped that same night, the script carried base64 ciphertext. Random base64 contains every three-letter substring you can imagine, including the ones in a TikTok pixel signature. The scanner tagged 800 KB of encrypted member data as
data-wpconsent-name="tiktok-pixel". A cookie banner flagged our encryption as ad tech.
Admins bypass consent scanning. Editor previews bypass it. Which is why every place we looked was exactly the place the bug could not exist.
The fix: give the scanner nothing to scan
Consent scanners rewrite script and iframe tags. They do not touch inert markup. So the app now ships its payload in a <template> element as base64, with one small loader script that contains no tracker-shaped tokens at all:
<template id="app-data" hidden>eyJ...base64 payload...</template>
<script>(function(){
var t = document.getElementById("app-data");
var b = atob(((t.content && t.content.textContent) || t.textContent || "").replace(/\s+/g, ""));
var s = document.createElement("script");
s.textContent = new TextDecoder("utf-8").decode(
Uint8Array.from(b, function (c) { return c.charCodeAt(0) }));
document.body.appendChild(s);
})();</script>
Three details that will save you an hour each:
- Read
template.content.textContent, nottemplate.textContent. A template’s children live in a document fragment; the naive property returns an empty string and fails without an error you will love. - Decode through
TextDecoder("utf-8"), never bareatob. atob hands you Latin-1 bytes; every accented name on the page becomes mojibake. - Base64 is also immune to wptexturize, so WordPress cannot curl your quotes into syntax errors. One transform solves two WordPress problems.
We wired the transform into the publish pipeline itself, so no future regeneration can ship a raw scannable script again. The generators stay untouched; the publisher applies the armor.
Then we went one better: private dashboards with no passwords
The same night, the same architecture unlocked something the group had been asking for: members should see only their own dashboard, without a shared password to forget.
Every member now gets a personal link like /my-dashboard/#t=Xk3…. The page behind it holds only ciphertext:
- Each member’s data slice is AES-GCM encrypted with a key derived from their token. 100 members, 100 ciphertexts, zero readable names, scores, or photos in the page source.
- Shared program data (news, the agent roadmap) is encrypted once with a program key that rides inside every member’s ciphertext. The public page source contains no member data of any kind.
- The token lives in the URL fragment. Fragments never reach the server, never land in server logs, and never leak through analytics. WebCrypto decrypts entirely in the member’s browser.
One WordPress page, no plugin, no login system, no password emails to support. A wrong link fails to a friendly “ask the team for a fresh link” message. And because the payload is base64 in a template, the consent scanner that started this whole story has nothing to say about it.
Steal these rules
- If a script page is blank for visitors and fine for admins, fetch it anonymously (cookie-less, full browser user agent) and grep YOUR script tag for
type="text/plain"ordata-*consent*attributes before touching anything else. - Logged-in browsers and editor previews lie. Anonymous verification is the only verification.
- Big inline data belongs in an inert base64 template with a small clean loader, not in a scannable script.
- Keep tracker-shaped tokens out of loader code that must never be consent-gated.
- Bake the fix into the publish pipeline, not the output. Outputs get regenerated; pipelines remember.
Receipts
- Outage window: about three days, visitors only, invisible to every admin-context test run against it.
- Root cause confirmed by diffing served bytes against built bytes: the served script tag came back rewritten as a TikTok pixel.
- Dashboard page republished consent-proofed the same night: 608 KB app → 813 KB armored form, read-back verified.
- Token gateway shipped alongside: 100 encrypted member slices, 1.1 MB page, anonymous fetch verified with zero plaintext member data.
- Both fixes live in the publish scripts and the runbooks, plus a learning note filed into the skill-propagation loop so every pack inherits the rules automatically.
The overnight lesson, one line: the bug was not in our code, and the fix still belonged in our pipeline.
Related: The System · the meta-article standard this post follows.

