Skip to content

Latest commit

 

History

History
274 lines (218 loc) · 12.3 KB

File metadata and controls

274 lines (218 loc) · 12.3 KB

Ghostify Architecture

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.

Runtime Pieces

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 dynamic declarativeNetRequest rules for endpoint shapes that can be blocked safely at the extension layer.
  • src/content.js: the isolated-world bridge. It loads bundled config from dist/config/patterns.json, reads chrome.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.

Manifest And Injection Model

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: storage and declarativeNetRequest
  • Host permissions for Instagram, Messenger, Facebook, and www.fbsbx.com. The www.fbsbx.com match 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.json feed in Chrome, Edge, and Firefox.
  • src/content.js bundle injected as dist/js/content.js in the ISOLATED world at document_start
  • src/ghost.js bundle injected as dist/js/ghost.js in the MAIN world at document_start
  • src/messenger_patch.js bundle injected as dist/js/messenger_patch.js in the MAIN world for Messenger/Facebook surfaces
  • all_frames: true and match_origin_as_fallback: true so 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 Flow

build.js uses esbuild to bundle four source entry points:

  • src/ghost.js to dist/js/ghost.js
  • src/content.js to dist/js/content.js
  • src/background.js to dist/background.js
  • src/messenger_patch.js to dist/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:extension or npm run package:chromium creates the package used for Chrome Web Store and Microsoft Edge Add-ons.
  • npm run package:firefox creates an AMO upload ZIP and a separate reviewer source ZIP.
  • npm run prepare:firefox creates tmp/firefox-extension/ for local Firefox loading and web-ext linting.

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.

Settings And Config Flow

Default settings are represented by feature toggles:

  • igTyping
  • igSeen
  • igStory
  • msgTyping
  • msgSeen
  • msgStory

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.json
  • package-lock.json
  • dist/manifest.json
  • dist/config/patterns.json
  • src/content.js fallback config
  • CHANGELOG.md release heading and release notes
  • README.md when it contains fixed version or release wording

Blocking And Sanitization Strategy

Ghostify uses multiple layers because Meta web apps mix normal navigation, GraphQL operations, worker traffic, bridge commands, and real-time frames.

Dynamic DNR Rules

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.

Runtime Pattern Matching

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.

Main-World Network Hooks

src/core/interceptors/ installs hooks for:

  • fetch
  • XMLHttpRequest
  • WebSocket
  • 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.

Messenger And Facebook Module Patching

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 Modules

Platform-specific code lives under src/platforms/.

  • facebook.js handles Facebook feed, mini-chat, Messenger surfaces, message request behavior, read-state heuristics, and media-safe focus behavior.
  • messenger.js handles Messenger-specific surfaces and request/navigation behavior.
  • instagram.js handles 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.

Debugging Hooks

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.

Tests

The root test command is:

npm test

That 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 built dist/js/ghost.js and dist/js/messenger_patch.js bundles, plus popup asset contracts and GitHub issue-template behavior
  • firefox-popup.test.js — popup status/rating-link runtime behavior for Chromium and Firefox targets
  • settings-defaults.test.js, runtime-config.test.js, module-registry.test.js, module-flags.test.js — shared settings/config/ module contracts
  • package-extension.test.js, package-firefox.test.js — release ZIP dry-run tests for both stores
  • validate-extension-package.test.js — the extension package validator
  • prepare-status-update.test.js, daily-verification-git.test.js — public status feed generation
  • dependency-audit-policy.test.js — governed audit exceptions
  • extract-release-notes.test.js — CHANGELOG-driven release notes
  • version-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.

Platform-Change Risks

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_page frame 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.

Contributor Guidelines

  • Change bundled runtime logic in src/.
  • Run npm run build after source changes and commit the generated dist/ bundle changes.
  • Keep popup/static asset edits in dist/ unless a source pipeline is added.
  • Update dist/config/patterns.json and src/utils/network.js together when a new pattern requires new matching logic.
  • Run npm test before opening a PR.
  • Run npm run ci before PR-ready or release-ready changes.
  • Treat docs/BROWSER_DISTRIBUTION.md as 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.