Transparency Report
At a Glance
rules.muga.app, disable it any time in Settings. Optional Follow shortener redirects: off by default; when enabled, the extension itself fetches the shortener host directly (bit.ly, t.co, tinyurl.com, …) with credentials omitted to read its redirect, no MUGA server is involved. No cookies, no identifiers, no POST body. Auditable open-source code.
rules.muga.app weekly, on by default (an Ed25519-signed public payload carrying no user data; disable it any time in Settings). Optional Follow shortener redirects make direct HTTPS GETs to the shortener host (bit.ly, t.co, …) when enabled, no MUGA server is contacted.
esbuild, used only to bundle the cleaning library into the content script; output is committed to the repo). Development tooling such as the linter and the web-ext packager does not ship in the extension and does not transform what runs.
What MUGA Does With Your Data
URLs. Cleaned locally inside your browser by the content script's bundled cleaning library. The cleaning runs inline in the page's content script. It does not round-trip through the service worker or any external server. URLs are never stored beyond the current session. Session history (recently cleaned URLs, per-tab badge counts, debug logs) lives in chrome.storage.session and is automatically cleared when the browser restarts.
Behavioural preferences. Stored in chrome.storage.sync: language, blacklist, whitelist, custom tracking parameters, and the default values for the affiliate toggle and remote-rule-updates toggle. Encrypted and managed by your browser, synced to your Google or Firefox account, not to any server we control or can access.
Consent and per-device decisions. Stored in chrome.storage.local (this device only): your acceptance of the Terms (onboardingDone, consentVersion, consentDate), and per-device overrides for remoteRulesEnabled when a synced preference arrives enabled and you decline to inherit it. The reasoning is captured in ADR-0001: consent is an act between you and the device you are using; inheriting it across devices silently would deny each device's user the chance to read and decide.
Stats. Local counters only: URLs cleaned, parameters removed, referrals spotted. Stored in chrome.storage.local. No personally identifiable information. No timestamps tied to individual URLs.
Domain stats. Domain names and parameter counts only. No full URLs, no paths, no timestamps. Stored locally, capped at 50 domains to prevent unbounded growth. Never transmitted.
Affiliate tags. MUGA does not add any affiliate tag of its own. By default, it respects the original referral: if a creator you follow earned an affiliate tag on a link, it stays in place, locally, with no API call involved. If you enable "Remove all affiliate tags from other sources", third-party affiliate tags are stripped from the URL, leaving none behind; this is an optional extra, off by default, and MUGA never adds its own tag in their place.
Architecture: Where the Work Happens
MUGA is split into two small components that talk to each other only when strictly necessary:
The content script. Runs in every page (subject to your blacklist/whitelist). It owns the user-visible work: URL cleaning on click, on copy, and on page load; <a ping> attribute removal; redirect-wrapper unwrapping; and (on Firefox) AMP-to-canonical redirect. The cleaning library ships inside the content script as a pre-built bundle (src/content/cleaner-bundle.js), generated from the ES module sources under src/lib/ by tools/bundle-content.mjs. The bundle is committed to the repository so reviewers can verify it matches the unbundled source byte-for-byte.
The service worker. Runs in the background. It owns three things: (1) cross-cutting state that the content script doesn't need to compute itself, namely incrementing the badge counter and stats on a fire-and-forget message (the BADGE_AND_STATS side-channel), (2) cross-page coordination such as the toolbar action surface, the context-menu entries, and the optional weekly remote-rules fetch, and (3) the consent state machine, covering onboarding, soft and hard re-onboard, and the migration from legacy sync-stored consent to local. The service worker does not see or process URLs in transit; cleaning is finished before the service worker hears about it.
Why this split matters for transparency. Most extensions route URLs through their service worker so the worker can decide what to do. MUGA does the opposite: the cleaning runs in the page where you clicked, the service worker hears only the post-cleaning summary it needs to update the badge. There is no central pipeline that observes your URLs.
Per-Device Consent
MUGA records your acceptance of its Terms and Privacy Policy per device, not per browser account. If you install MUGA on a second device, you'll be asked to read and accept the documents on that device too, even if you already accepted them somewhere else. Behavioural preferences (language, toggles) still follow your account through sync, but the consent to act on them is local. The full reasoning is in ADR-0001.
Re-onboarding. If we materially change the Terms, MUGA surfaces a re-acceptance flow on each device the next time the service worker wakes up; affected features are gated until you re-accept. If we only add clauses (without changing existing ones), MUGA shows you the new clauses and lets you accept or decline; declining keeps you under the previously accepted Terms.
Migration on upgrade. If you previously installed MUGA before per-device consent shipped and your acceptance was stored across devices via sync, MUGA migrates that acceptance to local storage on the first run after upgrade. The migration is one-way (sync → local) and idempotent. You keep your acceptance state without re-onboarding. Implemented in src/lib/sync-migration.js.
What We Deliberately Don't Do
- No telemetry or usage analytics. We have no idea how often you use MUGA or which features you use.
- No crash reporting. If the extension throws an error, it logs to your browser console. Nowhere else.
- No remote configuration or feature flags. Every toggle is a local preference. There is no server that can change your extension's behavior remotely.
- No server-side rule delivery beyond a weekly signed check. The 450+ tracking parameter list and domain rules ship bundled with the extension at install time; that baseline never depends on the network. On top of it, Remote rule updates is on by default: the extension fetches a signed parameter list at most once per 7 days from
rules.muga.appvia HTTPS GET withcredentials: "omit"andcache: "no-store": no browsing data is sent, no cookies, no account, no identifiers in the request. Like any request to any website, it does reveal your IP address and the time of the request to whoever serves the file; MUGA neither logs nor stores this. You can disable it any time in Settings, which returns the extension to zero outbound requests. This default was flipped in the release documented in the CHANGELOG (#888), once the rule-signing infrastructure and defense-in-depth verification were ratified as production-ready. - No server-side redirect for affiliate links. Creators come first. We evaluated 10+ affiliate programs that require routing your click through an external tracking server. We rejected every one of them.
- No A/B testing infrastructure. Every user runs the same code.
- No user accounts or sign-in. We have no concept of a MUGA user identity.
Permissions Explained
Every permission MUGA requests, across its Chrome (manifest.json) and Firefox (manifest.v2.json) manifests, has a specific, minimal purpose. Here is what each one is and why it exists.
How to Verify
Every claim on this page is verifiable. You don't have to take our word for it.
- Read the source code. github.com/yocreoquesi/muga. Every line that runs in your browser is published there.
- Build from source. Clone the repo and run
npm ci && npm run build. The output is the same extension distributed on the Chrome Web Store and Firefox AMO. - Verify the content-script bundle matches its source. The cleaning library is bundled into the content script via esbuild (
tools/bundle-content.mjs, ~30 lines) because MV3 content scripts cannot use ES module imports portably across Chrome and Firefox MV2. The unbundled source lives undersrc/lib/and the committed bundle output issrc/content/cleaner-bundle.js. Runnpm run build:contentand diff the regenerated output against the committed file. It must match exactly. The bundle is the only build-time transformation in the project; everything else is plain vanilla JavaScript (no TypeScript, no Babel, no JSX). - Diff the published extension. Unpack the installed extension (Chrome:
chrome://extensionsin developer mode; Firefox: extract the XPI) and diff the files against the published source. They should match exactly. - Check network requests yourself. Open your browser's DevTools Network tab while using MUGA. On a default install you will see at most one HTTPS GET to
rules.muga.appper 7 days (Remote rule updates is on by default). Disable Remote rule updates in Settings and you will see zero outbound requests initiated by the extension. - Read the coding standards.
AGENTS.mdandCONTRIBUTING.mdin the repository document the automated review rules that enforce these constraints on every pull request. - Read the architectural decision records.
docs/adr/captures the reasoning behind decisions like per-device consent. If you want to know why something is built the way it is, the ADRs are where the trade-offs are written down.