Changelog

Auto-generated from the versioned corresponding-source archive on every deploy. Format follows Keep a Changelog; Mailshade adheres to Semantic Versioning.

v1.0.0 — 2026-06-01

First public launch.

Features (since v0.1.0)

  • Stable content-script teardown across five content bundles and six supported clientssetupAdapter() / teardownAdapter() helper in src/content/clients/base.ts retains MutationObserver + interval handles and disconnects them as one lifecycle, eliminating handle accumulation on SPA navigations (especially Superhuman's grid observer).
  • Setup-incomplete UX hints — toolbar action shows an amber ! badge + "Setup incomplete" title, popup renders a "Set up Mailshade →" CTA, and Options → Mail clients shows an inline warning banner whenever no email client is enabled. All three surfaces share hasAnyClientEnabled(settings) and recompute on chrome.storage.onChanged.
  • Options page opens in a full taboptions_ui.open_in_tab: true (re-attached after WXT manifest generation via the build:manifestGenerated hook) so the 9-section settings UI is no longer cramped into the chrome://extensions modal.
  • Onboarding rebrand: "Open Gmail" → "Open inbox" — last onboarding step now opens whichever inbox the user just granted permission for (Gmail / Outlook / Office 365 / Superhuman / Yahoo / ProtonMail).

Fixes (since v0.1.0)

  • Webhook hardeningapplyBillingCors now echoes back only https://mailshade.org and chrome-extension://<id> (instead of *), in-memory sliding-window rate limiter (10 req/min/IP) gates /billing/checkout, /billing/activate, /billing/portal, console.error logs only err.message (no Polar response bodies), and normalizeCheckoutId validates against UUID_RE.
  • Extension messagingbilling:checkout/billing:portal reject messages from non-chrome-extension:// senders; billing:activate accepts only from the https://mailshade.org/upgrade-success content script bridge; validateLicense guarded by if (!result.key) return { ok: false, error: 'missing-key' } to prevent SW crash on edge case.
  • Hawk license-key scrubbermaskString now also masks Polar license keys in the XXXX-XXXX-XXXX-XXXX format → [REDACTED:license] while leaving plain UUIDs untouched.
  • CI hardeningci.yml sets permissions: contents: read and pins pnpm/action-setup@v4 to version: 9.15.0; Dependabot config added for github-actions + npm ecosystems; scripts/preflight-launch.sh reads version from package.json instead of the hard-coded 0.1.0 zip name; scripts/deploy.sh rsync loop uses an argv array instead of eval.
  • i18n: 13 new keys × 10 localesonboarding_step_waiting_permission, onboarding_clients_permission_waiting, onboarding_open_inbox_cta (rebrand), badge_setup_incomplete_title, clients_setup_incomplete_hint, popup_setup_incomplete_cta, etc.

Operator decisions (since v0.1.0)

  • Discord de-scoped from submit gate. The replacement DoD is "≥ 5 install confirmations through any community channel (CWS reviews / GitHub Discussions / Discord) within 14 days of going live" — phase3-w1 no longer blocks on Discord server creation or member count.
  • CWS developer account ($5) confirmed already paid under brandandface@gmail.com (Developer Group ID d3e667ee-…); skip the registration step in phase3-launch-playbook.md step 11.

Dead code removed (since v0.1.0)

  • Duplicate ClientAdapter interface in src/core/types.ts (the live copy lives in src/content/injector.ts).
  • upsertSenderStat + DAY_MS from src/core/db.ts — exported but never called in production; the real write path is reportEngine.ts:recordEvent inside a Dexie transaction.

v0.1.0 — 2026-05-22

Initial pre-launch snapshot. Phase 0 + Phase 1 deliverables landed across phases phase0-w1, phase1-w1, phase1-w2, phase1-w3, phase1-w4. The version tag itself will be created by scripts/release.sh on first release.

Features

  • Landing site, infra, legal pages, and Hawk error masker (phase0-w1) — scripts/deploy.sh ships apps/webhook/ to auth.mailshade.org (Docker image build → load on VPS → docker compose), nginx configs for mailshade.org + auth.mailshade.org with certbot-managed TLS, privacy / terms / impressum pages, and a Hawk transport wrapper that strips PII (emails, hostnames, message subjects) before any error report leaves the extension.
  • WXT scaffold + Declarative Net Request + tracker database (phase1-w1) — Manifest V3 extension built with WXT (Chrome / Edge / Firefox channels), rules/pixels.json + rules/links.json DNR rules, IndexedDB-backed tracker store (Dexie) with BlockedRequest schema, popup + report React surfaces.
  • Six supported mail clients + popup + report dashboard (phase1-w2) — Content scripts for Gmail / Outlook Web / Office 365 / Superhuman / Yahoo / ProtonMail with sender extraction + pixel attribution, popup showing "today / 7d / 30d" counts, full report page with Recharts time-series + per-sender breakdown + CSV export.
  • Link unwrapper, onboarding, Polar webhook, licensing, options page (phase1-w3) — linkUnwrapper.ts with 35 tracker patterns + click-interceptor link-warning modal across all 5 hosts; 4-step onboarding flow with per-client chrome.permissions.request; apps/webhook/ Polar HMAC webhook receiver with file-locked founder counter (cap 1000 @ $19) at /polar/webhook and /polar/founder-quota; Polar /v1 license validate client + 7-day grace cache + feature gate (isPro / reportHistoryDays / topSendersLimit / sender + domain whitelists); 9-section options page (General / Clients / Blocking / Senders[Pro] / Domains[Pro] / Reports / Privacy / Upgrade / About).
  • i18n (10 locales), production landing polish, Playwright e2e, store assets, deploy gate (phase1-w4) — check-i18n.mjs enforces parity across en/ru/de/es/fr/it/ja/ko/pt-BR/zh-CN; site/ landing rewritten for production with Lighthouse CI gate (Perf ≥ 0.90, A11y ≥ 0.95) via scripts/check-lighthouse.mjs --url; Playwright e2e suite (14 specs — popup, report, link-warning, contextmenu-resolver, options-whitelist, onboarding, …); store-listing artefacts (5 PNG @ 1280×800 + marketing/store-listing.md + marketing/privacy-practices.md) gated by scripts/check-store-listing.mjs; live scripts/post-deploy-smoke.sh smoke runner with 11 PASS / 0 SKIP steps including live-URL Lighthouse check.

Fixes

  • ESLint v9 flat config now covers scripts/**/*.{mjs,js} with Node globals (root cause: --ext is a no-op under flat config). pnpm lint restored to exit 0.
  • tests/e2e/contextmenu-resolver.spec.ts de-flaked: fresh launchPersistentContext per test + iterative chrome.tabs.query + probe loop replacing the single-tab race. --repeat-each=3 and 3 consecutive full-suite runs all 14/14 green.
  • linkInterceptor.onOpenDirectly now emits chrome.runtime.sendMessage({type:'toast:show', variant:'link-opened-directly', payload:{originalHost, unwrappedHost}}) after navigating; producer side wired so the toast asserted in the Playwright spec has a real source.

Build

  • WXT auto-syncs manifest.version from package.json (verified pnpm build.output/chrome-mv3/manifest.json .version equals package.json .version).
  • vite.esbuild.charset = 'ascii' in wxt.config.ts to keep the Dexie "￿" range sentinel out of the bundle (Chromium's IsStringUTF8 rejects non-character code points even when bytes are valid UTF-8).

CI

  • .github/workflows/ci.yml runs lint + typecheck + unit + e2e + build + store-assets gate + landing Lighthouse on every PR. Tag-triggered release pipeline (this version) lives in .github/workflows/release.yml.