Appearance
Marketing vs. Portal Analytics — Auth-Wall Isolation Control
Audience: SOC 2 / HIPAA / EDE Phase 3 auditors, AskFlorence engineering + compliance, performance-marketing partners (Creative AdBundance and any future agency).
Scope: The operational control that lets us run third-party performance-marketing tools (ad pixels, heatmaps, A/B testing, audience builders, email/CRM) on pre-enrollment surfaces without putting PHI or post-enrollment member data in the same blast radius as those tools.
Last updated: 2026-05-11
1. The control
AskFlorence operates two distinct surfaces with a hard architectural boundary between them:
| Surface | Domain | Data class | Third-party scripts |
|---|---|---|---|
| Apex marketing | askflorence.health and www.askflorence.health | Public + PII at most (email, ZIP, age, income range, NPN, name) | Permitted after a one-shot per-tool compliance review |
| Member portal | app.askflorence.health (Phase 5 portal subdomain, working name) | PHI in scope (SSN, DOB, income docs, enrollment records, agent-assisted interactions) | Architecturally forbidden via CSP; enforced at the browser layer, not by runtime checks |
The boundary is the subdomain split. Cookies set on askflorence.health are scoped Domain=askflorence.health (apex only, NOT .askflorence.health), so the browser drops them when the user crosses into app.askflorence.health. Pixels physically cannot follow a user across the cut. This is what we mean by "enforced by physics, not by policy" — there is no runtime check to forget, no per-route allowlist to maintain, no WAF rule that can be misconfigured. The browser does it.
The control extends the production / staging isolation logic that EDE Phase 3 §3 requires from "environment separation" to "PHI vs. non-PHI surface separation," using the same Atlas project + AWS account + DNS / CDN topology that already underpins prod / staging isolation.
Why this control exists
Performance marketing for an ACA marketplace platform requires conversion pixels, retargeting, audience-build tooling, and creative testing. Those tools assume a relatively permissive operating environment that is incompatible with HIPAA-grade and EDE Phase 3-grade controls on PHI surfaces. Forcing a single domain to host both would require per-route allowlists and CSP exceptions that drift and break silently. Splitting at the subdomain level makes the boundary impossible to forget.
Framework mappings
- HIPAA Security Rule §164.308(a)(4) — Information Access Management. The control implements least-functional-access for third-party processors by scoping their reachable cookie + execution surface to the non-PHI subdomain.
- HIPAA Security Rule §164.312(a)(1) — Access Control technical safeguard. The portal subdomain enforces authentication before any PHI-bearing page renders, and CSP enforces no third-party script execution on that subdomain.
- CMS EDE Appendix A §3 — Environment separation. The control is the documented extension of the prod / staging isolation logic from environments to surfaces.
- SOC 2 CC6.1 — Logical access. The subdomain CSP allowlist is the operational artifact mapping to logical access scoping for third-party processors.
- SOC 2 CC6.6 / CC6.7 — Restriction of access to systems and information. The control restricts third-party processor access to apex-only.
2. Cookie + CSP scoping rules
Apex (askflorence.health)
- All first-party cookies set by our application must be scoped
Domain=askflorence.health(not.askflorence.health). The wildcard form would propagate toapp.askflorence.health. - Third-party cookies (ad-tech, heatmap, A/B test) inherit the third party's defaults. Most ad-tech vendors set apex-scoped cookies by default. Reviewer confirms at intake.
- CSP is permissive within an explicit allowlist of approved third-party domains, populated from the tool registry below.
Portal (app.askflorence.health)
Content-Security-Policy: script-src 'self'; img-src 'self' data:; connect-src 'self' <first-party-analytics-host>; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'as the floor.<first-party-analytics-host>= our self-hosted OpenPanel + GlitchTip (ADR 0009). No third-party origins permitted inscript-src,connect-src(except the first-party self-hosted analytics),frame-src, orform-action.- CSP
report-urienabled in production; any blocked-script violation generates a report to our backend. Reports are reviewed in the quarterly access review (seedocs/infrastructure/access-reviews/). - The portal serves no inline scripts;
'unsafe-inline'is not in the policy. - No analytics SDKs, no heatmap SDKs, no error-tracking SDKs with un-BAA'd vendors. The first-party self-hosted stack (OpenPanel + GlitchTip) is the only analytics/error surface.
Compatibility with the cross-surface attribution pattern
Click IDs (fbclid, gclid, li_fat_id, ttclid) and UTM params captured on apex are persisted on the waitlist record and read again at enrollment-submit time inside the portal. The portal does NOT load a third-party pixel to read them; instead, our backend reads the click IDs server-side from the enrollment record and fires the conversion server-side to Meta CAPI, Google Enhanced Conversions, LinkedIn CAPI, and TikTok Events API with SHA-256 hashed PII. Hashing happens before bytes leave our infrastructure.
This pattern is the standard documented healthcare-and-finance approach for all four ad platforms and does not require the portal to relax its CSP.
3. Tool-approval SOP
Proposal template
Each tool a partner wants to wire onto apex is submitted as a single record:
Tool name: <e.g., Meta Pixel + Conversions API>
Vendor: <vendor legal name>
Vendor website: <link>
Surface scope proposed: apex | portal | both
Data flow summary: <what events, what fields, where they go>
PII transmitted: <yes / no, list of fields, hashed?>
Cookie domain scope: <apex | .apex (wildcard, likely flag for review)>
Loads on portal: <yes / no — if yes, flag for review; default no>
BAA available: <yes / no / not applicable>
BAA signed if yes: <date or N/A>
Proposed by: <partner name, contact email>
Purpose: <one-line business reason>Review checklist (compliance reviewer side)
- Is the surface scope apex-only? If proposed
bothorportal, default is denial unless the vendor has a signed BAA and the data flow has no PHI. - Does the cookie scope respect the apex boundary? Wildcard scope (
.askflorence.health) is flagged; resolve with vendor to a scoped variant before approval. - Does the data flow include PHI? If yes, deny unless BAA is signed AND surface is portal-only AND the vendor is on the data-classification minimum-vendor list.
- Does the tool load on portal? If yes, deny unless first-party self-hosted (OpenPanel + GlitchTip).
- For session-replay / heatmap tools: is input recording disabled by default? If recording is enabled, require input-masking configuration that excludes the calculator income field. Document the configuration screenshot or settings export in the registry row.
- Is the vendor on the existing vendor register? If not, add a row to the vendor register at the same time as the analytics registry row, with appropriate subprocessor classification.
- Is there an Article 28 GDPR DPA available if the vendor receives EU traffic? If we ever market in EU, this becomes mandatory. Today we only market in the US; flag for forward awareness only.
Approval / denial language
- Approved: add a row to the registry below with approval date + reviewer initials. Reply to partner:
Approved. Wire as described. Registry row: <link>. Re-review in 12 months unless scope changes. - Approved with condition: add the row with the condition explicitly noted (e.g., "Approved with condition: input masking on for
input[type='number']on/"). Reply with the exact condition copy/pasted so the partner can confirm before going live. - Denied: reply with the specific reason from the checklist, the change required, and an offer to re-review on resubmission. Do not file a registry row for denied tools; track in the partner's correspondence only.
Turnaround target
24-48 hours for a tool submission. We do not batch; each tool is reviewed independently and returned independently. The clock starts when the proposal record is complete (all fields filled).
Re-review cadence
- Annual: every registry row gets a touchpoint review on its anniversary date. Confirm vendor, scope, BAA status, configuration unchanged.
- On scope change: if a partner wants to expand a tool from PageView-only to Lead+Purchase tracking, or from apex-only to wherever, re-run the full review.
- On vendor M&A or material policy change: trigger an off-cycle review if the vendor is acquired, changes data residency, or materially changes its data-processing terms.
4. Living tool registry
The registry is populated as partners propose tools. It is the source of truth for what is authorized to run on apex.
| Tool | Vendor | Surface | Proposed by | Data flow | PII transmitted | Cookie scope | BAA | Approval date | Reviewer | Last reviewed | Notes |
|---|---|---|---|---|---|---|---|---|---|---|---|
| (empty) | (empty) | (empty) | (empty) | (empty) | (empty) | (empty) | (empty) | (empty) | (empty) | (empty) | Registry will populate as Creative AdBundance submits the first tool list. Empty is correct at 2026-05-11. |
When the first tools land, replace the empty placeholder row with real entries. Keep entries even after a tool is retired (move to a "Retired" sub-section with retirement date) for the audit trail.
5. Tripwires + detection
What the control is designed to prevent, and how we know if any of it slipped:
| Tripwire | How it happens | Detection |
|---|---|---|
Third-party script renders on app.askflorence.health | Engineer pastes an <script> into a layout file; PR review misses it | CSP violation report to backend report-uri; periodic audit script grep'ing built portal HTML for non-'self' script origins |
Cookie scoped Domain=.askflorence.health (wildcard) by mistake | Vendor SDK default differs from documented behavior; engineer adds wrong scope | Quarterly cookie inventory audit; first-party cookie check in browser-test suite |
| Session-replay tool records calculator income input | Vendor default has recording on; intake didn't catch it | Spot-check session-replay recordings during quarterly access review; vendor settings export attached to registry row |
| Tool added to apex without registry review | Partner wires a tool ahead of review; engineer ships it | PR template asks "new third-party script?" with link to registry; CI check fails build if <script src= references a domain not in the allowlist (future enhancement) |
| PHI accidentally hashed and sent to ad-tech endpoint | Backend conversion-forwarding code receives a PHI field by mistake; sends it hashed but it shouldn't be sent at all | Code review on backend conversion forwarder; allowlist of fields permitted in the hashed-PII payload (email, phone — that's it) |
6. Reference patterns in the repo
These already exist and are referenced by the control:
instrumentation-client.ts+src/lib/posthog-server.ts— hostname-based opt-out pattern; the same pattern will short-circuit any first-party analytics capture on the portal subdomain.src/app/api/waitlist/route.ts(consent sub-document pattern, ~line 460-469 as of 2026-05-11) — the per-record consent capture pattern that extends to enrollment-time consent capture in the portal.src/lib/analytics.ts(server-side event spine) — the tool-portable callsite where the OpenPanel ingest plugs in when ENG-347 lands. (The legacynext.config.ts/ingest/*PostHog reverse-proxy was removed with the PostHog rip — #75 sub-A, PRs #184/#186.)
7. Cross-references
| Topic | Where it lives |
|---|---|
| Privacy policy + Terms of Service (consumer-facing) | /privacy, /terms (v2026.04, published) |
| Partner-facing version of this control (Creative AdBundance) | docs/briefs/creative-adbundance-analytics-brief.md |
| EDE Phase 3 control mapping (§3 prod/staging + this control as the surface-level extension) | docs/security-compliance/ede-control-mapping.md |
| Vendor / Subprocessor register (where each approved tool also lands a row) | docs/security-compliance/vendor-register.md |
| Privacy Impact Assessment (current PHI-adjacent flows) | docs/security-compliance/privacy-impact-assessment.md |
| Data classification taxonomy | docs/infrastructure/data-classification.md |
| Member portal compliance design (auth, audit, consent capture inside the portal) | docs/agent-platform/compliance.md |
| ENG-187 — Member portal concept and requirements (where the subdomain + CSP + click-ID propagation + server-side CAPI work is scoped) | Linear |
8. Open items
- First tool registry entries — will populate when Creative AdBundance returns its proposed Stage 1 tool list (target mid-June 2026).
- Cookie consent UI — not yet shipped. Inventory of currently-set cookies + a consent banner that gates third-party pixel loading is a prerequisite for any tool that drops third-party cookies on apex. New GitHub issue to be filed once CA tool list confirms the need.
- Portal subdomain provisioning —
app.askflorence.healthDNS + ACM cert + ALB/CloudFront routing + CSP report-uri are part of ENG-187 scope, not a standalone task. - Click-ID capture + server-side CAPI pipeline — also part of ENG-187 scope. Spawns its own issue only if engineering design says it warrants independent tracking.
- CI check on script-src allowlist — future enhancement. PR template question is the current control; automated CI grep is the next step.