# OpenVibe.Extensions > OpenVibe's first-party client surfaces: today, the **OpenVibe Companion** browser extension. **Docs:** https://openvibe.services · **For AI:** https://openvibe.network/llms.txt **Build and test:** `npm install && npm run pack` (Node 22 or newer) · **Tests:** `npm test` **Status:** alpha (roadmap Wave 18, client part). The extension builds, its tests pass, and headless Chrome loads the unpacked 0.9.2 build with no console or CSP errors (2026-11-07, Chrome 156 via CDP `Extensions.loadUnpacked`; the side panel's Agents tab draws the Dock's agent view against stubbed first parties, see the credential note below). A development build of the coupon part looked up codes and sent a report against a local OpenVibe.Coupons. Mozilla's `web-ext lint` (what addons.mozilla.org runs on upload) passes with no errors, warnings or notices and runs in CI; it hasn't been loaded in a running Firefox yet. It is **not published** to any store yet: the store zips, listing text and privacy answers are ready (`npm run pack:stores`, [store/](store/)), and only the owner's developer-account step is left. OpenVibe.Coupons is public at `https://openvibe.coupons` since 2026-10-09, so the release build talks to the live Coupons API. On 2026-10-22 Coupons held 169 codes for 16 of its 37 merchants, all from the DealNews feed through OpenVibe.Sources; it had issued no install token and received no code report. **Decision record:** [ADR-023](https://github.com/OpenVibers/OpenVibe.Contracts/blob/main/docs/adr/ADR-023-client-surfaces.md) (first-party client surfaces and their home). Roadmap §4.2 D. **License:** MIT. ## Purpose Client code ships on a different schedule from servers: store review, browser updates and per-install permissions. ADR-023 gives clients their own repository. | Surface | Directory | State | |---|---|---| | OpenVibe Companion (Chromium and Firefox, Manifest V3) | `browser/` | alpha, described below (it was the OpenVibe Coupons Helper until 0.2.0) | | Kiosk companion (Firefox, new-tab and history bridge for the /kiosk page) | `kiosk-companion/` | Moved from OpenVibe.Live `browser-extension/openvibe-kiosk-companion/` (ADR-023, Live 9c5aa64); Live keeps a redirect README | | Hardware / Raspberry Pi companions (streamer and controller scripts) | `hardware/` | Moved from OpenVibe.Live `hardware/` (ADR-023, Live 9c5aa64); Live keeps a redirect README | | Desktop client | — | Planned by ADR-023 for the long term. Nothing exists. | ## The OpenVibe Companion (`browser/`) One button in the browser for the OpenVibe sites that work with the page you are on: - **Keep on my Rent board.** On a rental listing (Craigslist, Facebook Marketplace, Zillow, Apartments.com, Zumper, Airbnb and the rest) or any page, it opens [openvibe.rent/board](https://openvibe.rent/board) with the listing's address and title filled in, and the price and city the page states (`lib/listing.js` reads them when you press: the structured data the site publishes for search engines, else its price tag), so every listing from every site sits in one list with your notes and how far you got. You check them there before saving. On a rental site this is the main button. - **Check the rent.** On a rental listing it opens [openvibe.rent/fair](https://openvibe.rent/fair) with the listing's address and title, and the monthly rent in dollars, the bedrooms and the city the page states (read the same way; a sale price or another currency is left out). Rent reads what is missing from the address and title (and says which it read), and compares the rent with the city's median for that size from the Census Bureau. - **Watch this page.** It opens a new watch on [OpenVibe.Watch](https://openvibe.watch) for the page, which tells you (or runs your agent) when it changes. - **Compare delivery apps.** On DoorDash, Uber Eats, Grubhub, Seamless, Caviar or Instacart it opens [openvibe.food/delivery](https://openvibe.food/delivery) with the restaurant or store's name (read from the page title, never the address or the cart), and every app's own search for it opens from there. - **Save its rating to OpenVibe Reviews.** On Glassdoor, Indeed, Yelp, the BBB, Trustpilot, Google Maps or Tripadvisor, it reads the rating the page states (the structured data the site publishes for search engines, else the stars' own label, like "4.5 star rating") and opens [openvibe.reviews/save](https://openvibe.reviews/save) with the page's address, its name and the numbers. You check them against the page there and save; the rating then shows beside that site's link on the OpenVibe.Reviews page, with the date. On a review site this is the main button. - **Try these codes on this page.** At a shop's cart or checkout, one button types the codes OpenVibe.Coupons knows into the page's own coupon box, presses that box's Apply button, and reads the order total before and after. It stops at the first code that lowers the total and leaves it applied. Codes reported working go first, codes reported not working are never tried, and at most ten are tried. It never presses checkout, pay, order, cart or remove buttons, never submits a form and never presses Enter. With no Apply button next to the box it presses nothing. If you connected the extension, you can send what happened to OpenVibe.Coupons in one click. - **Coupon codes.** On a shop's website it shows the codes [OpenVibe.Coupons](https://github.com/OpenVibers/OpenVibe.Coupons) knows for that shop: - the code, with a Copy button - its status (validity unknown, reported working, reported not working) - its confidence, or "no reports", and its expiry, or "Expiry unknown" - its restrictions, or "No restrictions stated" - how to apply it If you connected the extension, you can report whether a code worked. The codes are shown to copy; the Companion never rewrites links or adds referral tags. ### What it can see, and what it sends | | | |---|---| | **Permissions** | `activeTab`, `identity`, `scripting`, `storage`, `alarms`, `clipboardWrite`, and Chrome-only `offscreen` and `sidePanel` in the Chrome build (Firefox's `sidebar_action` needs no permission of its own). The host permissions are five OpenVibe origins: `https://openvibe.coupons/*` (the coupon API), `https://openvibe.network/*` (the Dock's state), `https://openvibe.codes/*` (the Dock's agent view and its four controls) and `https://openvibe.chat/*` (the Dock's Chats tab), plus `https://openvibe.locker/*` (encrypted password logins). `identity` starts the Dock's one-click OAuth sign-in (`chrome.identity.launchWebAuthFlow` / Firefox's `browser.identity`). `scripting` is used for four buttons: "Try these codes on this page" injects the packaged `lib/autoapply.js`, "Save its rating to OpenVibe Reviews" injects `lib/rating.js`, and "Keep on my Rent board" and "Check the rent" inject `lib/listing.js`, into the active tab (the access `activeTab` gives after you press the button), in its top frame and the extension's isolated world. | | **Not requested** | ``, `tabs`, `cookies`, `webRequest`, `history`, optional permissions, content scripts, web-accessible resources, `externally_connectable`. | | **Read from the page** | The active tab's **URL and title**, when you open the popup (that's what `activeTab` grants). The page's content only when you press "Try these codes on this page": `lib/autoapply.js` finds the coupon box, its Apply button and the order total, types the codes in, and returns to the popup which codes changed the total. It makes no request, reads no cookies or storage, and sends nothing anywhere; `test/manifest.test.js` scans it for that. On a review site, "Save its rating to OpenVibe Reviews" runs `lib/rating.js`, which only reads: the page's name, its rating, the scale, how many ratings, and the city from its structured data. "Keep on my Rent board" and "Check the rent" run `lib/listing.js`, which only reads: the price and its currency, the city and region (never the street) and the bedrooms, from the structured data, else the page's price tag. Browser pages, files, IP addresses and local names are "not a site", and nothing is sent or offered for them. | | **Sent by the extension** | The Coupons part sends to `https://openvibe.coupons` only, and only the **hostname** (`lib/host.js` drops the path, query, fragment, port and credentials first): `GET /api/v1/merchants/resolve?host=`, the merchant id (`GET /api/v1/merchants/:id/coupons`), and, when you report, the code id plus `worked` or `failed` with an optional reason. Every request goes with `credentials: 'omit'` (no cookies), `referrerPolicy: 'no-referrer'` and `redirect: 'error'`. The Dock sends the person's own bearer to `https://openvibe.network` (its state, `/api/v1/me/dock`), `https://openvibe.chat` (Chats), and, when the Agents tab needs it, `https://openvibe.codes` (the agent view and its controls). | | **Hand-offs (only when you press the button)** | "Keep on my Rent board", "Check the rent", "Watch this page", "Compare delivery apps" and "Save its rating to OpenVibe Reviews" open **a new tab you see** on `https://openvibe.rent/board`, `https://openvibe.rent/fair`, `https://openvibe.watch/watches/new`, `https://openvibe.food/delivery` or `https://openvibe.reviews/save`, with the page's address and title (and, for Reviews, the rating read from it; for Rent, the price, city and bedrooms read from it) in that tab's address. `lib/handoff.js` removes the credentials, the fragment and tracking parameters (`utm_*`, `fbclid`, `gclid`, …) first. The extension makes no request for it, nothing opens on its own, and nothing is saved until you save it on that site. `platform.tabs.open()` refuses any other origin, and the permission audit allows only `https://openvibe.` hand-off origins. | | **Stored** | The optional `cpx_` install token in `storage.local` (never synced); for the Dock, the one-click OAuth access token in `storage.session` (memory, cleared when the browser closes) and the rotating refresh token in `storage.local` (this device, not synced). A pasted 0.7.0 `networkToken` is still accepted as a fallback until a later release removes it. | | **CSP** | `default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' blob:; connect-src https://openvibe.coupons https://openvibe.network https://openvibe.chat https://openvibe.codes; frame-src https://openvibe.live; object-src 'none'; base-uri 'none'`. The Dock draws its own styles in a `