Ghostify is a Manifest V3 browser extension that applies local privacy controls
to Instagram, Facebook, Messenger, and Facebook Messenger proxy frames. The
manifest matches the broader www.fbsbx.com host, and runtime guards narrow
Messenger-specific behavior to MAW proxy pages. Ghostify runs inside the user's
browser, stores preferences locally, and ships its privacy patterns with the
extension package.
Ghostify has four main runtime pieces:
src/background.js: the Manifest V3 background entry point. Chromium runs it as a service worker; Firefox runs the same bundle as a non-persistent background script. It owns dynamicdeclarativeNetRequestrules for endpoint shapes that can be blocked safely at the extension layer.src/content.js: the isolated-world bridge. It loads bundled config fromdist/config/patterns.json, readschrome.storage.local, and posts config and setting updates into the page.src/ghost.js: the main-world privacy engine. It receives settings/config, installs fetch, XHR, WebSocket, and focus/visibility hooks, then starts platform-specific protections.src/messenger_patch.js: the main-world Messenger/Facebook module patcher. It wraps Messenger and Facebook bridge/module surfaces that cannot be handled reliably by static URL rules alone.
The extension popup is currently a static asset under dist/. Its JavaScript
reads the manifest version, writes ghostifySettings to chrome.storage.local,
and depends on open pages receiving those setting changes through src/content.js.
All maintained browser packages read the same display-only public status feed.
The privileged request uses no credentials, custom headers, query parameters,
or body and sends no extension settings, tab URLs, messages, or social-media activity. The latest dated history record
controls the popup: a working record is green, while a report, confirmed issue,
or review record is yellow. The popup shows that record's month and day without
changing its color merely because the record is old.
The daily verification workflow refreshes one status-data PR for maintainer review. Automation never merges those PRs or treats automated checks as live Meta proof. The public feed changes only after the maintainer completes the smoke checklist and merges the proposal. Scheduled automation does not propose green while the latest merged state is yellow. Manual workflow dispatch supports report, work-in-progress, confirmed-issue, and full-verification proposals.
dist/manifest.json is the Chromium entry point loaded by Chrome and Edge.
browser-targets/firefox/manifest.overlay.json replaces only the fields that
must differ for Firefox. scripts/prepare-firefox-extension.js combines the
overlay with the Chromium manifest in an ignored staging directory; generated
Firefox manifests are never maintained by hand.
Important manifest properties:
manifest_version: 3- Permissions:
storageanddeclarativeNetRequest - Host permissions for Instagram, Messenger, Facebook, and
www.fbsbx.com. Thewww.fbsbx.commatch is broader than the MAW proxy path, so runtime checks gate Messenger-specific behavior to/maw_proxy_page. - A host permission for
ghostify-extension.vercel.app, used only by the popup to fetch the display-only/status.jsonfeed in Chrome, Edge, and Firefox. src/content.jsbundle injected asdist/js/content.jsin theISOLATEDworld atdocument_startsrc/ghost.jsbundle injected asdist/js/ghost.jsin theMAINworld atdocument_startsrc/messenger_patch.jsbundle injected asdist/js/messenger_patch.jsin theMAINworld for Messenger/Facebook surfacesall_frames: trueandmatch_origin_as_fallback: trueso relevant frames receive the protections early enough
The split between isolated-world and main-world scripts is intentional. Extension APIs are available to the isolated content script, while page-native APIs and page module loaders must be wrapped from the main world.
build.js uses esbuild to bundle four source entry points:
src/ghost.jstodist/js/ghost.jssrc/content.jstodist/js/content.jssrc/background.jstodist/background.jssrc/messenger_patch.jstodist/js/messenger_patch.js
The build output format is iife, the target is chrome89, and minification
is currently disabled. npm test runs npm run build before executing the
regression harness.
Not every dist/ file is generated by build.js. The popup HTML/CSS/JS,
icons, manifest, and dist/config/patterns.json are maintained as extension
package assets. When source modules change, rebuild and commit the generated
bundle files. When popup or static assets change, edit the corresponding
dist/ asset directly unless a source counterpart is introduced later.
Store packaging is deliberately split:
npm run package:extensionornpm run package:chromiumcreates the package used for Chrome Web Store and Microsoft Edge Add-ons.npm run package:firefoxcreates an AMO upload ZIP and a separate reviewer source ZIP.npm run prepare:firefoxcreatestmp/firefox-extension/for local Firefox loading andweb-extlinting.
The Firefox overlay declares the permanent Gecko add-on ID, Firefox 140 as the minimum supported release, a background script instead of a service worker, and Mozilla's no-data-collection declaration. Shared runtime, popup controls, icons, and bundled privacy configuration remain byte-identical unless a documented browser-specific behavior requires otherwise.
Default settings are represented by feature toggles:
igTypingigSeenigStorymsgTypingmsgSeenmsgStory
The popup stores user preferences in chrome.storage.local under
ghostifySettings. The isolated content script reads those settings and sends
them to the main-world scripts with window.postMessage. It also listens for
storage changes and pushes updates to already-open pages.
Privacy pattern configuration is bundled in dist/config/patterns.json. The
content script fetches that file through chrome.runtime.getURL, validates its
version and shape, and posts it to the main-world engine. If the packaged file
cannot be read or is invalid, the content script uses its matching committed
fallback. Runtime configuration is not persisted separately in browser
storage. src/ghost.js then calls updatePatterns in src/utils/network.js.
Version values must stay synchronized across:
package.jsonpackage-lock.jsondist/manifest.jsondist/config/patterns.jsonsrc/content.jsfallback configCHANGELOG.mdrelease heading and release notesREADME.mdwhen it contains fixed version or release wording
Ghostify uses multiple layers because Meta web apps mix normal navigation, GraphQL operations, worker traffic, bridge commands, and real-time frames.
src/background.js creates dynamic declarativeNetRequest rules for the
settings that can be represented safely at the request-rule layer. These
currently cover Instagram story-seen writes when igStory is enabled and
legacy Facebook/Messenger typing endpoints when msgTyping is enabled. Other
privacy toggles are handled by the main-world hooks and platform patches. These
rules are not declared as static rule_resources in the manifest.
dist/config/patterns.json provides bundled pattern lists. src/utils/network.js
sanitizes and merges those lists into runtime matchers. The matcher layer decides
whether payloads should be allowed, blocked, or sanitized.
src/core/interceptors/ installs hooks for:
fetchXMLHttpRequestWebSocket- focus and visibility behavior
The network layer avoids blocking normal sends and read-only loads where
possible. For mixed batches, it can sanitize privacy tasks while preserving
message-send or hydration tasks. For blocked fetch/XHR requests, response helpers
in src/utils/responses.js provide safe synthetic responses so the page does
not fail harder than necessary.
src/messenger_patch.js handles surfaces that do not reduce cleanly to URL or
payload matching. It wraps page-level constructs such as workers, message ports,
Facebook's module loader, typing exports, and read-receipt exports. Some read
receipt operations are blocked outright; some local read-state modules are
sanitized so UI loading can continue without sending Seen-style writes.
Platform-specific code lives under src/platforms/.
facebook.jshandles Facebook feed, mini-chat, Messenger surfaces, message request behavior, read-state heuristics, and media-safe focus behavior.messenger.jshandles Messenger-specific surfaces and request/navigation behavior.instagram.jshandles Instagram surfaces where privacy writes and media behavior require site-specific treatment.
These modules exist because the same privacy feature can have different payload shapes and UI side effects on each Meta surface.
Debug helpers are intentionally quiet by default. Console diagnostics and
observation output are gated by local storage flags such as ghostifyDebug and
ghostifyMessengerObserve. Some page-visible status/helper objects may still be
installed quietly so diagnostics can be enabled without changing the build.
Normal production use should not produce noisy console output.
The root test command is:
npm testThat command builds the bundled extension scripts and runs the full regression
suite in test/:
messenger-send-stability.test.js— the large Node VM harness with fake browser primitives exercising the builtdist/js/ghost.jsanddist/js/messenger_patch.jsbundles, plus popup asset contracts and GitHub issue-template behaviorfirefox-popup.test.js— popup status/rating-link runtime behavior for Chromium and Firefox targetssettings-defaults.test.js,runtime-config.test.js,module-registry.test.js,module-flags.test.js— shared settings/config/ module contractspackage-extension.test.js,package-firefox.test.js— release ZIP dry-run tests for both storesvalidate-extension-package.test.js— the extension package validatorprepare-status-update.test.js,daily-verification-git.test.js— public status feed generationdependency-audit-policy.test.js— governed audit exceptionsextract-release-notes.test.js— CHANGELOG-driven release notesversion-tag-integrity.test.js— release identity-path protection
These tests protect important regression paths, but they are not a substitute
for manual browser smoke tests on Instagram, Messenger, and Facebook before a
browser-store release. Use the smoke IDs in docs/QA_FIXTURES.md for
high-risk fixes and releases.
Ghostify depends on behavior from third-party web apps that can change without notice. High-risk change areas include:
- GraphQL operation names, doc IDs, and variable shapes
- Messenger Lightspeed module names and export names
- Facebook module-loader behavior around
window.__d - Worker and shared-worker bridge frame shapes
www.fbsbx.com/maw_proxy_pageframe behavior- Message request routes and hydration behavior
- Local read-state behavior in Facebook mini-chat and Messenger popovers
- Media playback and focus/visibility interactions on Facebook and Instagram
When a platform changes, prefer the smallest durable matcher or patch that fixes the affected privacy signal while preserving normal messaging, media playback, and navigation.
- Change bundled runtime logic in
src/. - Run
npm run buildafter source changes and commit the generateddist/bundle changes. - Keep popup/static asset edits in
dist/unless a source pipeline is added. - Update
dist/config/patterns.jsonandsrc/utils/network.jstogether when a new pattern requires new matching logic. - Run
npm testbefore opening a PR. - Run
npm run cibefore PR-ready or release-ready changes. - Treat
docs/BROWSER_DISTRIBUTION.mdas the authoritative store/package map. - Manually test affected Instagram, Messenger, and Facebook flows before a release.
- Do not include local-only files, ZIP packages, logs, or temporary captures in commits.