Chrome Extension Web Clipper

Phase 5 Phase 12 Manifest V3 Stable ID via manifest.json "key"

Phase 12 — GitHub App auth + stable extension ID

As of Phase 12 the extension authenticates via the xbrain GitHub App (Client ID Iv23liVnZvIN0Lo6isof) instead of Google directly. The extension's chrome.runtime.id is pinned to anigikcnmldoklcmogffmgcojdhhficb via a "key" field in the manifest so the OAuth callback URL https://<ext-id>.chromiumapp.org/ stays valid across dev machines.

What is the xbrain Web Clipper?

The xbrain Chrome Extension (Manifest V3) lets you send selected web content directly to your team's xbrain memory from any webpage. You choose the truth level before sending — making it easy to capture and classify information on the fly without leaving your browser.

The extension uses Chrome's native identity API (launchWebAuthFlow) for authentication. Post-Phase 12, it goes through the xbrain GitHub App: the auth popup lands on GitHub's consent screen, the OAuth code is sent to POST /v1/auth/github/signin, and the response gives an xbt_ session token that the extension keeps in chrome.storage.local and forwards as the Bearer token to memory-api.

Installation

The extension is not yet on the Chrome Web Store. Install it in developer mode from the source repository:

  1. Clone the xbrain repo: git clone https://github.com/mrboups/xbrain.git
  2. Open Chrome → Settings → Extensions → Enable Developer mode (toggle top right)
  3. Click "Load unpacked" → Select the chrome-extension/ folder in the cloned repo
  4. The xbrain icon appears in your Chrome toolbar

Team access

The extension requires a Google account that has been added to your xbrain team by an admin. Authenticate with the same Google account you use to sign in to LibreChat or Open WebUI.

Extension Manifest

The extension uses Manifest V3. Authentication goes through chrome.identity.launchWebAuthFlow with the xbrain GitHub App (post-Phase 12). The "key" field at the top pins the extension ID across all unpacked installs — without it, every developer's clone would generate a different chromiumapp.org callback URL and the GitHub App's registered callback would mismatch.

chrome-extension/manifest.json{
  "manifest_version": 3,
  "name": "xbrain Web Clipper",
  "version": "1.1.0",
  "description": "Send web content to your team's xbrain memory",

  // Pins chrome.runtime.id to anigikcnmldoklcmogffmgcojdhhficb (Phase 12).
  // See .planning/KB/chrome-extension-key.md for keypair derivation.
  "key": "MIIBIj...your-public-key-base64...",

  "permissions": ["identity", "activeTab", "storage"],
  "background": { "service_worker": "background.js" },
  "action": {
    "default_popup": "popup.html",
    "default_icon": { "48": "icon48.png", "128": "icon128.png" }
  },
  "content_scripts": [
    { "matches": ["<all_urls>"], "js": ["content.js"] }
  ],
  "host_permissions": [
    "https://api.grooveos.app/*",
    "https://grooveos.app/*",
    "https://github.com/*"
  ]
}

Where the GitHub App client_id lives

The constant GITHUB_CLIENT_ID = "Iv23liVnZvIN0Lo6isof" lives in chrome-extension/background.js. It is shared with the web app (app-site/account/teams/teams.js) because GitHub Apps accept multiple callback URLs, so the same App backs both surfaces.

Re-deriving the extension ID

If you regenerate the keypair (lost manifest.json "key"), the new chrome.runtime.id will not match the GitHub App's registered callback. You must either (a) restore the original "key" from D:/VSC/@security/chrome-extension-key/, or (b) re-derive the ID via the procedure in .planning/KB/chrome-extension-key.md and edit the GitHub App settings to add the new https://<new-id>.chromiumapp.org/ callback.

How to Use

Step 1 — Authenticate

Click the xbrain icon in your Chrome toolbar. On first use, click "Sign in with Google". The extension uses Chrome's launchWebAuthFlow to obtain a Google ID token — this opens a secure auth popup without triggering popup blockers on regular pages.

Step 2 — Select content

On any webpage, select the text you want to save. Right-click and choose "Send to xbrain" — or open the popup directly and the current page URL and title are pre-filled automatically by content.js.

Step 3 — Choose truth level

In the popup, select the truth level that reflects your confidence in the clipped content:

Step 4 — Send to Brain

Click "Send to Brain". The content is POSTed to memory-api with your team's scope. You'll see a success confirmation directly in the popup. The item is immediately searchable by your teammates.

Architecture

The extension follows a strict MV3 service worker model. All API calls are made from background.js — content scripts cannot make cross-origin requests.

Browser tab (content.js)
    │ selected text + URL + title
    ▼
popup.html (user selects truth level, clicks Send)
    │
    ▼
background.js (service worker)
    │ chrome.identity.launchWebAuthFlow → GitHub OAuth code
    │ POST /v1/auth/github/signin → xbt_ session token (Phase 12)
    │ stored in chrome.storage.local
    ▼
POST https://api.grooveos.app/v1/memory/upsert
Authorization: Bearer {xbt_token}
X-Team-Scope: {team_from_storage}
{
  "item": {
    "content": "selected text",
    "source": "chrome-extension:grooveos.app",
    "truth_level": "WORKING",
    "team_scope": "excalibur",
    "project_scope": "fundraising",
    "visibility": "team"
  }
}

CORS Configuration

The memory-api accepts requests from chrome-extension:// origins. This is configured in memory-api's CORS middleware using a regex pattern to allow any extension ID:

memory-api/app/main.pyfrom fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origin_regex=r"chrome-extension://.*",
    allow_origins=["https://api.grooveos.app"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Authentication via Bearer token (Google ID token) is the real security control — the wide chrome-extension://* CORS origin is acceptable because any request still requires a valid Google ID token that memory-api verifies against Google's public keys.

Configuring for Your Deployment

The values you must align before loading a fresh build:

Field Where What to set
host_permissions manifest.json Your memory-api URL + grooveos.app + github.com (for the OAuth flow)
"key" manifest.json Base64 public key — pins chrome.runtime.id to anigikcnmldoklcmogffmgcojdhhficb. See .planning/KB/chrome-extension-key.md.
GITHUB_CLIENT_ID background.js Your GitHub App client ID. Default for the public xbrain build: Iv23liVnZvIN0Lo6isof. Self-hosters must replace with their own App's Client ID and re-register both callback URLs.
team_scope popup.html / storage Saved automatically in Chrome local storage after first sign-in

Why share a client_id between web + extension?

GitHub Apps support multiple callback URLs. Setting two callbacks (https://grooveos.app/account/teams/ and https://anigikcnmldoklcmogffmgcojdhhficb.chromiumapp.org/) on the same App lets both surfaces share one App, one client secret, one webhook. This is the structural improvement Phase 12 delivers over the old OAuth-App-per-surface model.