Context
Today the chromium side of this plugin is split into three pieces:
psychological-operations-chromium/ — bundles a Chrome for Testing build at fixed version, ships with the cli.
psychological-operations-chromium-extension-scrape/ — content-script extension that captures tweets off x.com pages as the operator scrolls.
psychological-operations-chromium-extension-auth/ — extension that wires up OAuth 2.0 (PKCE) to authorize the per-psyop X account for likes / retweets.
The extension model has friction:
- Two manifest.json files, two builds, two install paths into the chrome profile.
- Chromium's extension API only exposes what the upstream allows. Anything we need that isn't in the API (e.g. observing internal navigation events, persistent state between page reloads, hooking deeper rendering paths) requires hacks via content-script injection,
chrome.scripting.executeScript, or DOM polling.
- Every Chrome for Testing version bump risks an MV3 / API surface change that breaks one of the extensions silently.
- The auth extension exists purely because OAuth's redirect handling is awkward when Chrome treats us as a third-party site; we have to intercept the redirect via webRequest hooks and shuttle the auth code back to the cli via native messaging.
Proposal
Replace both extensions with a custom Chromium build purpose-built for X:
- Fork the same Chrome for Testing tag we already bundle (latest stable line — we already track and bump it).
- Bake the scrape behavior directly into the renderer: hook the tweet-display path at the point where the page's React state lands a fresh tweet, capture every (id, content, author, engagement) tuple verbatim into a process-IPC channel back to the host cli. No extension, no content-script polling, no DOM scraping.
- Bake the auth flow: intercept the OAuth redirect at the network-service layer (where chromium owns the URL routing) and forward the code directly to the cli via native messaging — no extension needed for the auth side either.
- The cli still spawns chromium per-psyop with a per-psyop profile dir + per-psyop env (
PSYOP_NAME / PSYOP_COMMIT_SHA) the same way it does today; only the chromium binary itself changes.
Advantages
- Removes both extension crates. One fewer manifest, one fewer build, no MV3 surface dependence.
- Deeper integration: we can capture tweet state at the data layer rather than scraping rendered DOM. Less brittle to UI changes from X.
- No more native-messaging dance for auth — the redirect intercept happens in-process.
- One bundle to ship instead of three.
Costs / open questions
- Build pipeline. A Chromium fork is multi-GB source + hours-long builds per platform. We'd need our own CI legs for linux_x86_64 / macos_x86_64 / macos_aarch64 / windows_x86_64 (matching the existing release matrix). Chromium's build prerequisites (depot_tools, GN, ninja) need to be staged.
- Chrome for Testing tracking cadence. CfT publishes weekly. Our fork has to rebase against new tags continuously; otherwise we drift from upstream security fixes. Same constraint exists for any fork.
- Patch surface. The smaller the patch, the easier the maintenance. Plan: keep our changes confined to a single new `//components/psyops/` subdir + a handful of `//chrome/browser/` hook sites. Resist temptation to scatter changes across the tree.
- License / branding. CfT's BSD-style license permits forks. Strip Google branding (logo, name strings) per the upstream's mark-cleanup convention so the binary doesn't impersonate Chrome.
- Distribution size. Each platform binary is ~150 MB compressed. We already ship CfT bundles at similar size, so no net delta — but worth confirming.
Out of scope for this issue
- Whether to deprecate the extension crates immediately upon shipping the fork, or keep them in parallel for a release.
- Detailed list of which Chromium components we'd patch — that's a design pass after this issue is accepted.
Acceptance criteria for the eventual PR
- A single `psychological-operations-chromium/` produces a fork binary per platform with scrape + auth baked in.
- Both `psychological-operations-chromium-extension-*` crates and their manifest/build scripts are removed.
- The cli's chromium-spawn path is simplified (no extension load arg, no extension-state shuttling).
- Integration tests still green against the new binary.
Context
Today the chromium side of this plugin is split into three pieces:
psychological-operations-chromium/— bundles a Chrome for Testing build at fixed version, ships with the cli.psychological-operations-chromium-extension-scrape/— content-script extension that captures tweets off x.com pages as the operator scrolls.psychological-operations-chromium-extension-auth/— extension that wires up OAuth 2.0 (PKCE) to authorize the per-psyop X account for likes / retweets.The extension model has friction:
chrome.scripting.executeScript, or DOM polling.Proposal
Replace both extensions with a custom Chromium build purpose-built for X:
PSYOP_NAME/PSYOP_COMMIT_SHA) the same way it does today; only the chromium binary itself changes.Advantages
Costs / open questions
Out of scope for this issue
Acceptance criteria for the eventual PR