Skip to content

[privacy] Trust-gate: blocklist copy + og→Umami exclude-search + PII purge (T1–T3, report §9) #320

Description

@ignromanov

Origin: Viability report ops/analysis/2026-06-10-voidpay-viability/report.md §9 (T1–T3). Owner: Kai (CTO). Co-review: Shade (CISO).
Decoupled from: spec 092-distribution-experiment (Phase 0 lifted out per Ignat 2026-06-10 — trust-fixes have privacy/security nature + own owner, не живут внутри distribution-спека).
Constitution: II (Privacy-first — never PII / financial data on servers).

Why a track, not a spec

Three independent privacy-hygiene fixes, all <1 day combined. For a product whose brand = privacy, these are "один внешний аудит до конфуза" (report §9). T4 (publish design-spec) lives on the void-layer track — see void-layer/codec issue, not here.

▌ Distribution gate

092 distribution start is gated on T1 + T2 deployed to prod. T3 is do-now-but-de-gateable (residual, not externally exposed). When T1+T2 are merged & live → this issue unblocks 092.


T1 — blocklist copy: present-claim → reserved-right [gating]

Problem: privacy/terms describe a blocklist as existing; zero implementation in code. Active misrepresentation to the privacy audience.
Decision (Kai): do NOT build the blocklist now (no abuse signal — 3 payments all-time; building = days, over-engineering pre-PMF). Rewrite tense present→reserved-right. Keeps legal cover, stops the false claim. Real implementation → separate backlog issue, triggered by first real abuse.
Sites (4):

  • src/app/privacy/content.ts:149 — "we maintain a public blocklist" → "we may maintain / reserve the right to maintain"
  • src/app/privacy/content.ts:153,165 — soften present-tense description of structure/publicity to conditional
  • src/app/terms/content.ts:192 — "we reserve the right to add … to our public blocklist" (already reserved-right — verify wording, likely OK)
  • src/app/terms/content.ts:243 — verify reserved-right framing holds
    Owner: Spark (final wording → copy-editing Sweep-2 voice-gate) → Atlas applies. Co-review Shade: does reserved-right language retain legal abuse-cover without a present claim.
    Effort: ~2–3h.

T2 — og-params leak into Umami url_query [gating]

Problem: ?og=INV_amounts_names written to Umami url_query (93 events, last 2026-06-06), against "collects no personal or financial data". UmamiScript.tsx:28 has data-exclude-hash="true" but NOT data-exclude-search.
Fix: add data-exclude-search="true" to src/features/analytics/ui/UmamiScript.tsx Script tag. Update the comment block (line 13–18) to document both guards.
⚠ Scope boundary: this closes the Umami sink only. The Vercel access-logs sink for the same og-params is a separate, heavier fix tracked in #211 (P2, not gating). Closing T2 does NOT close #211 — do not mark og-leak fully resolved.
⚠ Phase-1c coupling (for Spark/092): blanket data-exclude-search strips ALL query strings from Umami. 092 Phase 1c attribution must therefore use custom-event data (track('invoice-create', { ch })), NOT a URL ?ch= query — the query would be stripped here. Privacy-positive forcing function; design 1c accordingly.
Owner: Atlas (Kai brief — pure code, my lane, no voice-gate). Snapshot test on UmamiScript.
Effort: minutes.

T3 — purge 37 residual PII rows in Umami [do-now, de-gateable]

Problem: 37 rows with full invoice-hash PII, window 2026-02-05→03-28. Leak already stopped (data-exclude-hash since day-0). Residual data in private analytics DB (vp-metrics), not externally exposed.
Fix (mandatory order, no shortcuts):

  1. SELECT count(*) on exact predicate (url_query/url_path LIKE pattern + date window) — eyeball count vs 37
  2. snapshot/backup (CREATE TABLE _bak AS SELECT … or Supabase PITR)
  3. DELETE
    Risk: only risk = wrong WHERE deletes good rows. Shade reviews the predicate before DELETE.
    Owner: Ignat runs SQL via supabase db query --linked (ref: memory reference_supabase_umami). Co-review Shade (predicate).
    Effort: minutes + backup discipline.
    De-gateable: not externally visible → does not block 092 start by itself; bundled here because it's cheap and closes the loop.

Sequencing

  1. T2 — 1 line, deploy first, stops live leak. Fix 1c coupling note → Spark.
  2. T1 — parallel; Spark wording → Shade legal-check → Atlas applies.
  3. T3 — parallel; SELECT-verify → snapshot → DELETE (Shade predicate review).
  4. Gate-check: T1+T2 merged & live → comment here → 092 unblocked.

Acceptance

Co-review

  • shade-ciso: T1 legal-cover wording, T3 SQL predicate
  • spark-cmo: T1 final copy (voice-gate), T2 Phase-1c attribution design

Metadata

Metadata

Assignees

No one assigned

    Labels

    advisor:kaiAssigned to Kai (CTO)p1Must-have post-deploysecurity:hardeningCSP, headers, rate limiting

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions