hermes-browser-extension
Skill by ara.so — Hermes Skills collection.
What it does
Hermes Browser Extension is a Chrome/Edge/Chromium side panel that connects active web page context to a Hermes Agent runtime (local or remote). It is not a standalone chatbot—it's a browser interface for the real Hermes Gateway/API server, accessing your configured models, tools, skills, sessions, memory, and MCP servers.
Key capabilities:
- Side panel UI powered by Chrome Side Panel API (MV3)
- Connects to local (
http://127.0.0.1:8642) or remote Hermes API server - Syncs providers, models, profiles, skills, sessions, and capabilities from Hermes
- Captures and sends active tab context (title, URL, readable text, selected text, metadata, headings, links, forms, buttons)
- Supports per-tab or global side panel mode
- Streams responses from Hermes
- Voice dictation via Hermes STT or browser speech fallback
- Read-only context capture (no browser control, no form submission)
- Multiple themes (Nous, Midnight, Ember, Mono, Cyberpunk, Slate) and light/dark modes
Installation
1. Clone and build
git clone https://github.com/abundantbeing/hermes-browser-extension.git
cd hermes-browser-extension
npm install
npm run build
The extension builds to dist/.
2. Load unpacked in browser
- Open
chrome://extensionsoredge://extensions - Enable Developer mode
- Click Load unpacked
- Select the
dist/folder (not repo root, notextension/) - Pin the Hermes extension icon and click to open side panel
After code changes, run npm run build and click Reload on the extension card.
3. Verify installation
# Check that dist/ contains manifest.json with version 0.1.6+
cat dist/manifest.json | grep version
Configuration
Local API server (recommended default)
On the machine running Hermes, edit ~/.hermes/.env:
API_SERVER_ENABLED=true
API_SERVER_HOST=127.0.0.1
API_SERVER_PORT=8642
API_SERVER_KEY=your-secret-key-here
API_SERVER_CORS_ORIGINS=chrome-extension://your-extension-id
Find your extension ID at chrome://extensions (looks like abcdefghijklmnopqrstuvwxyz123456).
Start/restart Hermes Gateway:
hermes gateway run
Verify API server is reachable:
curl http://127.0.0.1:8642/health
curl -H "Authorization: Bearer your-secret-key-here" http://127.0.0.1:8642/v1/models
Connect extension to local API
In the extension side panel:
- Click Connect to Hermes
- If approval flow is available, approve locally in Hermes Desktop/gateway
- Otherwise, click Manual setup → Local gateway
- Gateway URL:
http://127.0.0.1:8642 - API Key: paste your
API_SERVER_KEYor scoped browser token - Click Test connection, then Save settings
- Navigate to a normal webpage and test:
Summarize this page in one sentence.
Remote API server
For remote Hermes (same LAN, Tailscale, VPN), bind to reachable interface:
API_SERVER_ENABLED=true
API_SERVER_HOST=0.0.0.0
API_SERVER_PORT=8642
API_SERVER_KEY=your-secret-key-here
API_SERVER_CORS_ORIGINS=chrome-extension://your-extension-id
Security: Use private network/VPN with HTTP, or put behind HTTPS reverse proxy. Never expose Hermes API server naked to public internet.
Example URLs:
http://192.168.1.50:8642
http://hermes-desktop.local:8642
https://hermes.example.com
In extension:
- Choose Remote gateway
- Paste remote URL (include
http://orhttps://) - Paste API key/browser token
- Click Test connection
Remote dashboard mode (no API server)
If you only expose the OAuth-gated Hermes dashboard (no API server):
- Select Remote
- Enter dashboard
https://URL - Leave API key blank
- Open dashboard URL in normal tab and sign in (keep tab open)
- Extension mints WebSocket ticket from signed-in tab
- Click Test connection (opens socket and loads models)
Limitations: image attachments inline-only, skills/profiles unavailable (REST-only).
Usage patterns
Basic prompting with page context
# Side panel captures active tab automatically
Summarize this page
Explain the main concept here
What are the key action items on this page?
Quick commands
The extension includes built-in quick commands:
/summarize - Summarize active page
/explain - Explain main concepts
/rewrite - Rewrite or improve content
/tabs - Analyze all open tabs
/action-items - Extract action items
Context scope control
Click the context menu in composer header to choose:
- Chat only - no browser context attached
- Follow active tab - context auto-updates when you switch tabs
- Page only - pin to specific tab
- Choose tabs - select which open tabs to include
Voice dictation
Click microphone icon in composer:
- Uses Hermes audio transcription if available
- Falls back to browser speech recognition otherwise
What Hermes saw
After each turn, expand "What Hermes saw" to inspect:
- Active tab title, URL
- Selected text (if any)
- Readable page text (first N chars)
- Metadata, headings, links, forms, buttons
- Open tabs list (if included)
This is the exact context sent to Hermes.
Code examples
Programmatically send message (content script)
// From a content script, send message to background
chrome.runtime.sendMessage({
type: 'HERMES_PROMPT',
payload: {
message: 'Summarize this page',
context: {
url: window.location.href,
title: document.title,
selectedText: window.getSelection().toString()
}
}
});
Access stored settings
// From extension context (popup, side panel, background)
import { getSettings } from './lib/storage';
const settings = await getSettings();
console.log('Gateway URL:', settings.gatewayUrl);
console.log('Theme:', settings.appearance?.theme);
console.log('Context mode:', settings.browserBehavior?.contextScope);
Capture page context
// From content script (extension does this automatically)
const context = {
url: window.location.href,
title: document.title,
selectedText: window.getSelection().toString().trim(),
readableText: document.body.innerText.slice(0, 50000),
metadata: {
description: document.querySelector('meta[name="description"]')?.content,
author: document.querySelector('meta[name="author"]')?.content
},
headings: Array.from(document.querySelectorAll('h1, h2, h3')).map(h => ({
level: h.tagName,
text: h.textContent.trim()
})),
links: Array.from(document.querySelectorAll('a[href]')).slice(0, 50).map(a => ({
text: a.textContent.trim(),
href: a.href
}))
};
Stream Hermes response
// Extension internal pattern
async function streamHermesResponse(sessionId, messages, signal) {
const response = await fetch(`${gatewayUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
session_id: sessionId,
messages,
stream: true
}),
signal
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
const data = JSON.parse(line.slice(6));
const content = data.choices?.[0]?.delta?.content;
if (content) {
// Append to UI
yield content;
}
}
}
}
}
Custom theme integration
// Extension appearance settings
const themes = {
nous: { primary: 'rgb(79, 70, 229)', secondary: 'rgb(99, 102, 241)' },
midnight: { primary: 'rgb(59, 130, 246)', secondary: 'rgb(96, 165, 250)' },
ember: { primary: 'rgb(249, 115, 22)', secondary: 'rgb(251, 146, 60)' },
mono: { primary: 'rgb(71, 85, 105)', secondary: 'rgb(100, 116, 139)' },
cyberpunk: { primary: 'rgb(236, 72, 153)', secondary: 'rgb(244, 114, 182)' },
slate: { primary: 'rgb(100, 116, 139)', secondary: 'rgb(148, 163, 184)' }
};
// Apply theme
function applyTheme(themeName, colorMode) {
const theme = themes[themeName];
document.documentElement.style.setProperty('--color-primary', theme.primary);
document.documentElement.style.setProperty('--color-secondary', theme.secondary);
document.documentElement.classList.toggle('dark', colorMode === 'dark');
}
Environment variables
Extension does not use .env files directly. Configuration is stored in Chrome storage and managed via settings UI.
For Hermes Gateway API server (machine running Hermes):
# ~/.hermes/.env
API_SERVER_ENABLED=true
API_SERVER_HOST=127.0.0.1
API_SERVER_PORT=8642
API_SERVER_KEY=<use-env-var-or-secret-manager>
API_SERVER_CORS_ORIGINS=chrome-extension://<extension-id>
Never commit API keys. Use $(hermes auth token create --scope browser) to generate scoped tokens, or reference secrets from environment/vault.
Troubleshooting
Extension loaded but nothing works
- Ensure you loaded
dist/, not repo root - Selected folder must contain
manifest.jsondirectly - Check
chrome://extensionsshows version 0.1.6+
Cannot connect to Hermes
# Verify API server is running and reachable
curl http://127.0.0.1:8642/health
# Check models endpoint with auth
curl -H "Authorization: Bearer your-key-here" http://127.0.0.1:8642/v1/models
Common issues:
API_SERVER_ENABLED=truenot set in~/.hermes/.env- Hermes Gateway not running (
hermes gateway run) - Wrong
API_SERVER_KEYor extension API key - CORS origin mismatch (extension ID not in
API_SERVER_CORS_ORIGINS) - Firewall blocking port 8642
Wrong version after update
Browser cached old unpacked folder:
- Run
npm run buildto generate freshdist/ - Open
chrome://extensions - Click Reload on Hermes Browser Extension card
- If still wrong, click Remove, then Load unpacked and select fresh
dist/ - Verify version: click Details → see Version field
No page context captured
Extension cannot read restricted pages:
chrome://oredge://internal pageschrome-extension://pages- Banking, crypto, password, payment, health, government domains (intentional security restriction)
On normal https:// pages, check:
- Context scope is not "Chat only"
- Page has readable text (not just images/video)
- Click "What Hermes saw" to inspect captured context
Voice dictation not working
- Check Hermes capabilities: extension queries
/v1/capabilitiesfor STT support - If Hermes STT unavailable, extension falls back to browser speech recognition
- Browser speech requires microphone permission (granted on first use)
- Some browsers/OSes may not support Web Speech API
Computer use / browser automation not working
Hermes Browser Extension is read-only. It does not have browser control permissions (debugger, click, type, form submit).
For native computer use:
# On machine running Hermes
hermes tools list
hermes computer-use status
Computer use comes from Hermes Agent's computer_use toolset via cua-driver, not the browser extension.
CORS errors in console
Extension origin must be allowlisted on Hermes API server:
# ~/.hermes/.env
API_SERVER_CORS_ORIGINS=chrome-extension://your-extension-id-here
Find extension ID at chrome://extensions. Restart hermes gateway run after changing.
Session persistence issues
- Extension binds one Hermes session per pinned tab
- Switching tabs creates new sessions (unless "Follow active tab" is enabled)
- Check
/api/sessionsin Hermes to see active sessions - Clear local history: Settings → Browser behavior → Clear history
Streaming response fails
Extension auto-falls back to non-streaming if:
- Gateway does not support
stream: true - Network drops chunks
- Response is not SSE format
Check gateway logs for streaming errors. Non-streaming mode still works.
Security notes
Extension is intentionally conservative:
- Read-only: no click, type, form submit, download, or browser control
- No sensitive permissions: no
debugger,nativeMessaging,cookies,history,downloads,bookmarks - Content wrapped as untrusted: page text marked as external context before Hermes sees it
- Local-first default: connects to
127.0.0.1:8642unless you configure remote - Bearer auth required: strong API key for all API access
- CORS allowlist: extension origin must be explicitly allowed
See repo docs: SECURITY.md, PERMISSIONS.md, DATA-FLOW.md, PRIVACY.md.
Key files
dist/ # Built extension (load this)
extension/
manifest.json # MV3 manifest with side_panel, activeTab, scripting
background.js # Service worker, message routing
sidepanel/
index.html # Side panel UI entry
app.jsx # Main React app
content/
content-script.js # Page context capture
lib/
hermes-client.js # Hermes API/WebSocket client
storage.js # Chrome storage wrapper
context-capture.js # DOM scraping logic
Common workflows
Install and connect to local Hermes
git clone https://github.com/abundantbeing/hermes-browser-extension.git
cd hermes-browser-extension
npm install
npm run build
# On Hermes machine, edit ~/.hermes/.env:
# API_SERVER_ENABLED=true
# API_SERVER_HOST=127.0.0.1
# API_SERVER_PORT=8642
# API_SERVER_KEY=<secret>
# API_SERVER_CORS_ORIGINS=chrome-extension://<id>
hermes gateway run
# In browser: chrome://extensions → Load unpacked → select dist/
# In side panel: Connect to Hermes → Local gateway → http://127.0.0.1:8642 → paste key → Test → Save
Switch between multiple local Hermes instances
Settings → Localhost agent picker:
Port 8642 - Default gateway
Port 8643 - Dev gateway
Port 8644 - Testing gateway
Click to switch. Extension stores per-port settings.
Use with remote Hermes on Tailscale
# On remote Hermes machine
API_SERVER_HOST=0.0.0.0
API_SERVER_PORT=8642
API_SERVER_CORS_ORIGINS=chrome-extension://<id>
# In extension
# Remote gateway → http://100.x.y.z:8642 → paste key → Test → Save
Capture and analyze open tabs
In side panel:
/tabs
# Or manually:
List all my open tabs and summarize each one
Extension sends tab list (title, URL) for all open tabs to Hermes.
Pin to specific page for isolated conversation
- Open page you want to analyze
- Click context menu in composer header → Page only
- Panel shows pinned tab indicator
- All messages in this panel now tied to that tab, with isolated Hermes session
- Switch tabs → panel stays on pinned tab
- Click context menu → Follow active tab to resume auto-switching
This skill covers: installation, local/remote configuration, context capture, voice dictation, quick commands, streaming, theming, security model, and troubleshooting for the Hermes Browser Extension.