Chrome Extension Web Clipper
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:
- Clone the xbrain repo:
git clone https://github.com/mrboups/xbrain.git - Open Chrome → Settings → Extensions → Enable Developer mode (toggle top right)
- Click "Load unpacked" → Select the
chrome-extension/folder in the cloned repo - 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:
- EPHEMERAL — Quick notes, unverified snippets, transient information
- WORKING — Actively useful, in progress, not yet validated
- VALIDATED — Verified information, cross-checked
- CANONICAL — Team-approved truth, the authoritative version
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.