Changelog
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 clients —
setupAdapter()/teardownAdapter()helper insrc/content/clients/base.tsretains 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 sharehasAnyClientEnabled(settings)and recompute onchrome.storage.onChanged. - Options page opens in a full tab —
options_ui.open_in_tab: true(re-attached after WXT manifest generation via thebuild:manifestGeneratedhook) so the 9-section settings UI is no longer cramped into thechrome://extensionsmodal. - 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 hardening —
applyBillingCorsnow echoes back onlyhttps://mailshade.organdchrome-extension://<id>(instead of*), in-memory sliding-window rate limiter (10 req/min/IP) gates/billing/checkout,/billing/activate,/billing/portal,console.errorlogs onlyerr.message(no Polar response bodies), andnormalizeCheckoutIdvalidates againstUUID_RE. - Extension messaging —
billing:checkout/billing:portalreject messages from non-chrome-extension://senders;billing:activateaccepts only from thehttps://mailshade.org/upgrade-successcontent script bridge;validateLicenseguarded byif (!result.key) return { ok: false, error: 'missing-key' }to prevent SW crash on edge case. - Hawk license-key scrubber —
maskStringnow also masks Polar license keys in theXXXX-XXXX-XXXX-XXXXformat →[REDACTED:license]while leaving plain UUIDs untouched. - CI hardening —
ci.ymlsetspermissions: contents: readand pinspnpm/action-setup@v4toversion: 9.15.0; Dependabot config added forgithub-actions+npmecosystems;scripts/preflight-launch.shreads version frompackage.jsoninstead of the hard-coded0.1.0zip name;scripts/deploy.shrsync loop uses an argv array instead ofeval. - i18n: 13 new keys × 10 locales —
onboarding_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 IDd3e667ee-…); skip the registration step inphase3-launch-playbook.mdstep 11.
Dead code removed (since v0.1.0)
- Duplicate
ClientAdapterinterface insrc/core/types.ts(the live copy lives insrc/content/injector.ts). upsertSenderStat+DAY_MSfromsrc/core/db.ts— exported but never called in production; the real write path isreportEngine.ts:recordEventinside 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.shshipsapps/webhook/toauth.mailshade.org(Docker image build → load on VPS → docker compose), nginx configs formailshade.org+auth.mailshade.orgwith 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.jsonDNR rules, IndexedDB-backed tracker store (Dexie) withBlockedRequestschema, 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.tswith 35 tracker patterns + click-interceptor link-warning modal across all 5 hosts; 4-step onboarding flow with per-clientchrome.permissions.request;apps/webhook/Polar HMAC webhook receiver with file-locked founder counter (cap 1000 @ $19) at/polar/webhookand/polar/founder-quota; Polar/v1license 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.mjsenforces 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) viascripts/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 byscripts/check-store-listing.mjs; livescripts/post-deploy-smoke.shsmoke 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:--extis a no-op under flat config).pnpm lintrestored to exit 0. tests/e2e/contextmenu-resolver.spec.tsde-flaked: freshlaunchPersistentContextper test + iterativechrome.tabs.query+ probe loop replacing the single-tab race.--repeat-each=3and 3 consecutive full-suite runs all 14/14 green.linkInterceptor.onOpenDirectlynow emitschrome.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.versionfrompackage.json(verifiedpnpm build→.output/chrome-mv3/manifest.json.version equals package.json .version). vite.esbuild.charset = 'ascii'inwxt.config.tsto keep the Dexie""range sentinel out of the bundle (Chromium'sIsStringUTF8rejects non-character code points even when bytes are valid UTF-8).
CI
.github/workflows/ci.ymlruns 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.