Chrome extension
The Chrome extension is a Manifest V3 build (@crxjs/vite-plugin) with three jobs: pull a
web page into your knowledge base with one click, enforce the not-now
feed quotas you’ve set for distracting sites, and act as the automation host that lets
instant messaging and outreach send and receive through the
browser you’re already logged into. All server calls happen in the service worker; the
popup and any content scripts reach it through chrome.runtime.sendMessage, never the
server directly.
The extension needs activeTab, scripting, tabs, storage, and alarms
permissions, and host access to http://127.0.0.1/*, http://localhost/*, and
https://*/*. It reads which server to talk to from chrome.storage.local.serverUrl
(http://127.0.0.1:4780 by default).
Capture
Section titled “Capture”The popup shows a connected indicator (green once health.ping succeeds), a “This page”
section naming the active tab, and a Save page + PDF to knowledge base button.
-
Extract
The popup grabs the tab’s DOM via
chrome.scripting.executeScript, runs it through@mozilla/readabilityto pull the article content, then converts that to Markdown withturndown, all inside the popup, before anything crosses the wire. -
Screenshot
The service worker captures the visible tab (
chrome.tabs.captureVisibleTab) and uploads the image toPOST /files. -
File it
ingest.capturelands the result as a page underingestion/raw, with the source URL stored inmetadata.capture.url. The popup confirms with a “Saved” message and a link to the new page.
You can also capture individual posts rather than a whole page, the same Readability +
screenshot pipeline runs against a narrower selection, landing under the same
ingestion/raw area.
Not-now feed quotas
Section titled “Not-now feed quotas”A content script enforces per-site feed quotas set up on not-now, directly in the DOM of the site you’re browsing:
- The site key is the hostname with a leading
www.stripped. - Feed items (
[data-feed-item], falling back toarticleon real sites without that marker) are counted as they cross 50% viewport visibility, the same “you actually saw this” threshold rather than counting everything that merely loaded. - Counts sync to the server every 3 items and on every
visibilitychange(notnow.counters.sync), bucketed into the rule’s configured window (windowStartrounds down to the nearestwindowMinutesboundary) and merged server-side withGREATEST, so a sync is idempotent even if it fires twice for the same item. - Once the server-side total reaches the rule’s limit, the extension hides the feed
container, hides the site’s notification-count button (
[data-notif-button]), and overlays a full-page “not now, quota spent, reopens in N min” message. - A fresh tab re-checks the running total on load, so the quota is enforced consistently across every device and tab you have open on that site, not just the one that tipped it over.
The automation host
Section titled “The automation host”The service worker doubles as an automation host: it opens the shared WebSocket bus,
subscribes to the browser-jobs topic, and drains queued jobs
(browser.jobs.pull) on three triggers, a WS invalidate push, a 1-minute
chrome.alarms tick (the practical floor for MV3 timers), and every time the service
worker restarts. A 20-second WebSocket ping keeps the worker alive longer than its normal
idle timeout on Chrome 116 and newer, so an approved job typically runs within about a
minute of being queued, even though MV3 service workers are designed to die after roughly
30 seconds idle.
Managed sites are WhatsApp Web, Messenger, and LinkedIn, plus any site with an open
fixture tab in development. A per-site opt-out
(chrome.storage.local.siteOptOut[site] = true) stops the host from (re)opening that
site’s tab automatically, for when you’d rather it stay hands-off.
Opens and pins the site’s tab if it isn’t already, scrapes [data-im-message]
elements, and completes with {ok, result: {messages}}. If the page shows
[data-logged-out], it completes with {result: {loggedOut: true}} instead, and the
server files a connector-logged-out task card with a log-back-in link, routed to
whichever user’s job it was (see per-user comms).
Appends payload.body into the site’s outbox field ([data-outbox]) and completes
with {result: {sent: true}}. Every send job traces back to an approved action, the
extension only executes what the server already decided to queue.
Because jobs run through your real, already-logged-in browser session rather than a headless bot account, this is the lowest-ban-risk way to automate sites that actively fight automation. It’s also why the automation host is scoped per user: a job is attributed to the user who triggered it, and a paired device only drains its own user’s jobs (an admin device additionally drains unowned system sweep jobs).