# AutoFirmer Capture (Chromium extension) Scrapes the firm tab you are looking at and posts its HTML back to the dashboard. There is no on/off switch for that — see **Scope** below for what limits it instead. ## Install (unpacked) 1. Start the dashboard (`npm run dev`) so `http://localhost:3000` is up. 2. Open `chrome://extensions`, enable **Developer mode** (top right). 3. **Load unpacked** → select this `extension/` folder. 4. Click the extension icon to open the popup and set: - **Dashboard URL** — default `http://localhost:3000`. Use the VPS IP if the dashboard runs elsewhere. - **Poll interval** — seconds between status checks (default 3). - **Element selector** — optional CSS selector; matching elements get their on-screen position measured alongside the HTML. The badge shows `!` (red) if the dashboard is unreachable, and nothing otherwise — blank is the working state. Open the popup for what the last tick actually did. ## Flow ``` extension --GET /api/autobuyer/status--> { pendingLocate } extension --POST /api/autobuyer/capture-> html + viewport + element rects AutoBuyer page --GET /api/autobuyer/capture-> renders the HTML ``` Nothing in that loop is switchable. Every tick serves any queued clicks and then captures the tab you are on. ## Scope Capture has no switch, so scope is what keeps it honest. Three limits, all enforced by Chrome rather than by this code: 1. **`host_permissions` is deliberately narrow** — the firm sites plus localhost for the dashboard. The extension is *technically incapable* of reading any other site, so an accidental capture of your bank or mail tab cannot happen however the logic here is changed. 2. **Only the tab you are on.** `pickTab()` asks for the active tab of the last-focused window *and* a host_permissions match. A firm tab sitting in the background is not captured, and neither is anything else. Switch away and capture simply stops until you switch back. 3. **One content script, same hosts.** `captcha-hook.js` runs at document_start on the firm sites only. It wraps `turnstile.render` / `grecaptcha.render` / `hcaptcha.render` and remembers what the page passed them — the sitekey, and above all the callback a solved token has to be handed to, which a programmatically rendered widget puts nowhere else. It reads nothing else and sends nothing anywhere; the remembered arguments stay in the page's own world until a `solveCaptcha` step comes to collect them. To automate a different broker, add its pattern to `host_permissions` in `manifest.json` and reload the extension — that alone scopes capture to it. Avoid going back to ``: it would put every tab you visit in reach, and limit 1 is the only thing standing between this and scraping your whole browser. A new firm needs its host in *both* lists — `host_permissions` and the `content_scripts` matches — or captchas on that site will have no callback to fire. ## 2FA codes On a firm site, the extension shows the machine's person's authenticator code for that site in a small chip in the bottom-right corner, with a Copy button (minimise it to a "2FA" pill; that sticks for the tab's session). ``` extension --GET /api/autobuyer/otp?host=--> dashboard dashboard --GET /api/otp?person=&host=--> master ``` The master holds the secrets and matches the host against each firm's **Site** link (Firms page), then returns the codes assigned to this person and that firm (Codes page). The chip only needs a code to be assigned — it doesn't wait for a code field. It stays hidden for a minute after the runner last located something, so automated clicks never land on it. A new 2FA site needs its host in `host_permissions` (not the captcha `content_scripts` list), a Site link on its firm on the master, and its codes assigned on the master's Codes page. ## Vest session relay Vest signs people in with Google (through Privy) in the browser and renews the session with a device key the browser will not export, so the dashboard cannot log in to Vest by itself. The extension relays the browser's session instead: ``` vest-relay.js (Vest tab, MAIN world) keeps the access token the Vest app last renewed background.js relayVestSession lifts it every 10s, posts it when it changes (and every minute) dashboard POST /api/vest/session holds it in memory for the firm's Vest client, and binds an unset Vest firm to the login's wallet ``` - The hook only reads the Vest app's own `/v3/auth/refresh` and `/register` answers. It renews nothing and sends nothing; the session stays the app's, so the bot never logs the tab out. - It takes the login's token only. The app renews each trading account's own token through the same `/v3/auth/refresh` (with that account's refresh token in the body), and one of those sees a single account. Before 0.17.2 the hook relayed those too, and the dashboard dropped every other account; the dashboard now refuses such a token and falls back to the login's. - The token lives ~15 minutes and the app renews it by itself while the tab is open. The tab is marked not auto-discardable, since a discarded tab stops renewing. - When the dashboard has a Vest firm (its credential is the login's wallet) and no Vest tab is open, the extension opens one in the background. The app signs back in from its 30-day session. After that the person signs in again in that tab. - This is the one place the extension reads a tab you are not looking at. It only reads `next.vestmarkets.com` tabs, and only that one value. ## Notes - The extension never captures the dashboard's own pages — otherwise it would just mirror its own output back. - `chrome://`, `about:` and Web Store pages cannot be scripted by any extension; they're skipped. - MV3 service workers are torn down when idle. Each poll makes an extension API call, which keeps the worker alive; a 30-second alarm revives it if Chrome kills it anyway. So worst-case cadence is 30s, normal cadence is the poll interval.