Skip to content

Repository files navigation

Mycelium

Mycelium is a local-first semantic memory and knowledge graph for Chrome. It quietly extracts readable long-form pages as you browse, stores their metadata and text in your Chrome profile, and makes everything searchable from a command palette. A separate graph view turns the same browsing memory into an explorable network of related pages.

Moss is the retrieval engine. Mycelium uses @moss-dev/moss-web and a Chrome offscreen document to run Moss's embedding and vector-search runtime in WebAssembly. Once the index is loaded, query embedding and retrieval happen inside the extension without a query network round trip.

OpenRouter is optional. The complete ingestion, search, snippet, history, and graph experience works without an OpenRouter key.

Why Mycelium exists

Browser history is useful when you remember a page's title or URL. It is much less useful when you only remember an idea, argument, or passage.

Mycelium treats browsing history as semantic memory:

  1. You read a substantial article.
  2. Mycelium extracts its readable text after a short delay.
  3. The page is cached locally and upserted into a Moss index.
  4. You later search for a concept instead of an exact title.
  5. Moss returns the most relevant pages from the loaded in-browser index.
  6. The graph lets you inspect related pages and return to the original source.

No separate Moss tab needs to remain open. The Moss runtime is owned by the extension.

Core features

Automatic article memory

  • Uses Mozilla Readability first, with article, main, and [role="main"] as conservative fallbacks.
  • Waits about 1.8 seconds after page load or in-page navigation before extraction.
  • Indexes only HTTP(S) pages with at least 500 readable words.
  • Records the URL, title, domain, favicon, excerpt, full readable text, word count, first visit, last visit, revisit count, and search-match count.
  • Uses a stable URL hash so revisiting the same URL updates one memory instead of creating duplicates.
  • Detects URL changes in single-page applications and schedules another extraction.

Command-palette search

  • Opens directly over an ordinary webpage in an isolated Shadow DOM.
  • Debounces input by 150 ms and returns up to eight ranked results.
  • Displays query-aware snippets, favicons, timestamps, relevance scores, result count, retrieval source, and measured latency.
  • Supports arrow-key navigation, Enter to open, and Escape to close.
  • Shows recently ingested pages before a query is entered.
  • Keeps a non-blocking local lexical fallback available while Moss initializes or if the runtime is temporarily unavailable.

Knowledge graph

  • Renders up to 300 weighted page nodes with d3-force and Canvas 2D.
  • Groups pages loosely by domain and assigns stable domain colors.
  • Sizes nodes using revisit and search-match activity.
  • Builds and caches local content-affinity edges using normalized shared-term overlap, an 0.08 threshold, and at most five outgoing candidates per source node.
  • Supports pan, zoom, node dragging, hover focus, graph search, and a detailed node inspector.
  • Shows related pages, source metadata, word count, cached summary or local snippet, index size, last Moss sync, and total/visible node counts.
  • Preserves the full memory count even when the visualization is capped at 300 nodes.

Optional AI summaries

  • Generates a single compact graph-node label after a page is indexed.
  • Uses OpenRouter asynchronously, so summarization never blocks ingestion or search.
  • Caches one summary per URL in chrome.storage.local.
  • Falls back silently to the page title or a locally generated snippet on missing keys, model errors, rate limits, or network failures.
  • Is disabled by default and is not required for semantic retrieval.

Keyboard shortcuts

Action macOS Windows/Linux Notes
Open or close search on a normal page Command + K Ctrl + K Captured by the content script. Some sites may also use this shortcut.
Extension command for search Command + Shift + K Ctrl + Shift + K Chrome-managed; configurable at chrome://extensions/shortcuts.
Open the knowledge graph Command + Shift + G Ctrl + Shift + G Opens graph.html in a new extension tab.

Clicking the Mycelium toolbar icon also opens the palette. Chrome does not permit content scripts on internal pages such as chrome://extensions, the Chrome Web Store, or certain browser-owned tabs. Use the extension command or toolbar icon from a normal HTTP(S) page.

Prerequisites

  • Google Chrome 121 or newer
  • Node.js 20.19 or newer
  • pnpm 11 or newer
  • A Moss project ID and project key
  • An OpenRouter API key only if optional AI summaries are enabled

Install and build

From this directory:

pnpm install
pnpm build

The build creates the unpacked extension in dist/.

The build runs three Vite targets:

  1. The graph and offscreen extension pages.
  2. A single-file Manifest V3 background service worker.
  3. An IIFE content script that can be injected into ordinary pages.

The background build deliberately rejects dynamic imports because Chrome Manifest V3 service workers must be statically loadable. Moss's WASM runtime runs in offscreen.html, where it has the lifetime and DOM-capable environment it needs.

Load the unpacked extension

  1. Open chrome://extensions.
  2. Enable Developer mode in the upper-right corner.
  3. Click Load unpacked.
  4. Select this project's generated dist folder—not the repository root and not dist/assets.
  5. Confirm that the Mycelium card appears and is enabled.
  6. Open or refresh a normal article tab so the content script is present.
  7. Press Command/Ctrl + K, or click the toolbar icon.
  8. Enter a Moss project ID and project key in Settings.
  9. Click Save & search.

If the extension was already loaded before a rebuild, click its refresh icon on chrome://extensions and then refresh any article tabs you want to test. Removing and re-adding the extension clears its chrome.storage.local data, including credentials and cached pages.

First-run test

Use this checklist to verify the full path:

  1. Open a public article containing at least 500 readable words.
  2. Leave the article open for two or three seconds.
  3. Press Command/Ctrl + K.
  4. Confirm that the article appears under Recently ingested.
  5. Search for a specific concept expressed in the article rather than only a word from its title.
  6. Check the search footer:
    • LOCAL WASM means Moss handled the query from the loaded index.
    • LOCAL CACHE means Mycelium returned its lexical fallback while Moss was unavailable or warming up.
  7. Open two or three additional long articles on related topics and repeat the wait.
  8. Open Knowledge graph from the palette or press Command/Ctrl + Shift + G.
  9. Search inside the graph, select a node, inspect its related pages, and open the original source.

The first index creation, credential change, or extension reload can take longer than a warm query because the client may need to connect, synchronize documents, and load the index. The latency badge measures the completed search path; it does not include earlier index creation or synchronization work.

Configuration

Moss credentials

Moss credentials are entered at runtime, not build time:

  1. Open the command palette.
  2. Select the gear icon.
  3. Enter the Moss project ID and project key.
  4. Click Save & search.

The credentials are trimmed and stored in chrome.storage.local. They are not compiled into the JavaScript bundle and are not stored with Chrome Sync. They are sent to Moss when the extension constructs the browser client.

Changing either credential disposes the active client, resets the loaded index state, reconnects using the new project, and begins syncing already cached pages in the background.

chrome.storage.local is profile-local storage, not a dedicated encrypted secrets vault. Treat the Chrome profile and local machine as trusted, and rotate any credential that has been exposed publicly.

Index name

The Moss index name is not secret. It defaults to mycelium-history and can be changed at build time:

cp .env.local.example .env.local
MOSS_INDEX_NAME=mycelium-history

Rebuild the extension after changing the value. Project IDs, project keys, and OpenRouter keys should not be placed in .env.local for this extension.

OpenRouter summaries

To enable optional summaries:

  1. Open Settings.
  2. Turn on Enable AI summaries.
  3. Enter an OpenRouter API key.
  4. Keep openrouter/free, select a listed model, or enter a custom OpenRouter model ID.
  5. Save the settings.

When enabled, Mycelium sends the page title and up to 12,000 characters of readable text to https://openrouter.ai/api/v1/chat/completions. The request asks for one sentence under 20 words, uses a 60-token cap, and caches a successful response by URL.

The OpenRouter key:

  • is stored only in chrome.storage.local;
  • is never included in Moss documents;
  • is sent only as the OpenRouter bearer token;
  • is not needed for search or graph generation.

Free-model availability and rate limits can change. Check the OpenRouter model list before relying on a specific model in production.

How the Moss integration works

Mycelium uses the browser SDK rather than the Node SDK:

const client = await MossClient.create(projectId, projectKey, {
  model: "moss-minilm",
});

await client.createIndex(indexName, documents, {
  modelId: "moss-minilm",
});

await client.addDocs(indexName, documents, { upsert: true });
await client.loadIndex(indexName);

const result = await client.query(indexName, query, { topK: 8 });

The browser client does not expose the Node SDK's session API. Mycelium therefore manages a durable named index with createIndex and addDocs, then calls loadIndex after completed mutations.

Index lifecycle

  1. Local capture: the content script writes the extracted page to chrome.storage.local immediately.
  2. Background handoff: it sends a MOSS_UPSERT_PAGE runtime message.
  3. Offscreen runtime: the background worker creates offscreen.html if needed and forwards the request.
  4. Index creation or update: the offscreen Moss client creates the named index or upserts documents in batches of 50.
  5. Snapshot load: the client loads the updated index into the browser WASM runtime.
  6. Warm query: query embedding and vector retrieval execute against the loaded index without a query network round trip.

Index creation, document synchronization, and snapshot loading can use Moss's service. The local-query claim applies after loadIndex has completed.

Search resilience

The search UI is intentionally non-blocking:

  • Input is debounced for 150 ms.
  • The background attempts Moss retrieval when the index is ready.
  • Moss requests have a 1.2-second application timeout.
  • A local lexical search can render after roughly 250 ms while the semantic path is still pending.
  • If Moss returns useful hits, they replace the fallback results.
  • If initialization or querying fails, the cached local results remain usable and Moss is marked for reinitialization.

This separation keeps the palette responsive without pretending that lexical fallback results came from Moss. The UI reports the active source as LOCAL WASM or LOCAL CACHE.

Moss document shape

Each indexed page is sent to Moss as a document with the stable page ID and readable text. Metadata includes:

  • URL
  • title
  • favicon URL
  • domain
  • last-visited timestamp
  • revisit count

OpenRouter credentials and summaries are not included in Moss documents.

Architecture

ordinary HTTP(S) page
  │
  ├─ content script (React + Shadow DOM)
  │    ├─ Readability extraction after ~1.8 s
  │    ├─ sensitive-site and 500-word guards
  │    ├─ immediate chrome.storage.local write
  │    └─ command-palette UI
  │
  │ Chrome runtime messages
  ▼
Manifest V3 background service worker
  ├─ search orchestration and local fallback
  ├─ settings, page metadata, graph cache, and summary cache
  ├─ optional OpenRouter summary queue
  ├─ toolbar and keyboard-command handling
  └─ offscreen-document lifecycle
       │
       ▼
offscreen.html
  └─ @moss-dev/moss-web
       ├─ createIndex / addDocs synchronization
       ├─ loadIndex snapshot hydration
       └─ in-browser WASM query embedding + vector retrieval

graph.html
  ├─ React interface
  ├─ d3-force layout
  ├─ Canvas 2D renderer
  └─ local affinity-edge cache + Moss-backed graph search

The offscreen document matters because Manifest V3 service workers are short-lived and do not provide the environment expected by the Moss WASM stack. The background worker remains the coordinator, while the offscreen page owns the long-lived client and loaded index.

Local data model

Mycelium uses these chrome.storage.local keys:

Key Contents
mycelium_pages Page records keyed by stable URL hash
mycelium_settings Moss credentials, OpenRouter settings, and model selection
mycelium_summaries Optional summaries keyed by URL
mycelium_revision Incrementing page-data revision used for graph caching
mycelium_last_sync Timestamp of the most recent completed Moss sync
mycelium_graph_cache Affinity edges and the revision they were built from

The manifest requests unlimitedStorage because readable article text can outgrow Chrome's default extension-storage quota over time.

Privacy and exclusions

Mycelium is local-first, but not completely offline:

  • Extracted page records are cached in the current Chrome profile.
  • Moss documents are synchronized to the user's configured Moss project so the durable index can be created and updated.
  • Loaded Moss queries run in browser WASM without a query network round trip.
  • OpenRouter receives page content only when AI summaries are explicitly enabled.

The extension skips known or likely sensitive routes, including common webmail, inbox, banking, checkout, billing, payment, wallet, patient, and medical-record URLs. It also explicitly excludes several mail and payment hosts.

These guards are conservative URL heuristics, not a formal data-loss-prevention system. Review and extend isSensitivePage and isSensitiveUrl before deploying Mycelium in a regulated or enterprise environment.

Project structure

mycelium/
├── public/
│   └── manifest.json              Chrome Manifest V3 definition
├── src/
│   ├── background/
│   │   ├── index.ts               orchestration, search, graph data, summaries
│   │   └── storage.ts             typed chrome.storage.local helpers
│   ├── content/
│   │   ├── index.tsx              extraction, shortcut capture, Shadow DOM mount
│   │   ├── Palette.tsx            search and settings UI
│   │   └── palette.css            page-isolated palette styling
│   ├── graph/
│   │   ├── App.tsx                graph search, inspector, stats, controls
│   │   ├── GraphCanvas.tsx        d3-force simulation and Canvas renderer
│   │   └── graph.css              graph styling
│   ├── offscreen/
│   │   └── index.ts               Moss client and WASM index lifecycle
│   └── shared/
│       ├── types.ts               messages, records, settings, graph types
│       └── utils.ts               URL hash, snippets, timestamps, page weighting
├── graph.html                     graph extension-page entry
├── offscreen.html                 Moss offscreen-page entry
├── vite.config.ts                 graph/offscreen build and shared constants
├── vite.background.config.ts      single-file service-worker build
└── vite.content.config.ts         IIFE content-script build

Development

Commands

# Start the Vite development server for extension pages
pnpm dev

# Type-check without emitting files
pnpm typecheck

# Build every extension target into dist/
pnpm build

# Run the complete verification sequence
pnpm check

For normal extension testing, pnpm build plus Chrome's extension refresh button is more representative than pnpm dev, because the background worker and content script are separately bundled extension targets.

Development loop

  1. Change the source.
  2. Run pnpm check.
  3. Open chrome://extensions.
  4. Click the refresh icon on the Mycelium card.
  5. Refresh the article tab so Chrome replaces the old content script.
  6. Re-test ingestion, search, and graph opening.

If a content script from an earlier build is still mounted in a tab, Chrome can report Extension context invalidated. Refreshing that webpage attaches the newly loaded extension context.

Troubleshooting

The palette does not open

  • Confirm Mycelium is enabled at chrome://extensions.
  • Use a normal HTTP(S) page, not a chrome:// page, Chrome Web Store page, PDF viewer, or another restricted surface.
  • Refresh tabs that were already open when the extension was installed or reloaded.
  • Try the toolbar icon.
  • Check chrome://extensions/shortcuts for a conflicting Chrome-managed shortcut.
  • Remember that direct page capture uses Command/Ctrl + K, while the manifest command defaults to Command/Ctrl + Shift + K.

The current article is not listed

  • Wait at least two seconds after the page becomes readable.
  • Confirm the page contains 500 or more readable words.
  • Refresh the page after loading a new extension build.
  • Check that the URL is not caught by a sensitive-site rule.
  • Some heavily scripted or paywalled pages may not expose readable article text to Readability.

Search shows LOCAL CACHE

This means the local lexical fallback answered the query. Verify the Moss credentials, wait for the initial sync, and search again. Saving credentials with cached pages triggers a background sync automatically.

Search has no results

  • First confirm that pages appear under Recently ingested.
  • Search for concepts present in those pages.
  • Open Settings and re-save valid Moss credentials to force a sync.
  • Inspect the service worker and offscreen document from chrome://extensions for runtime errors.
  • Check the configured Moss project for credential or index-access problems.

The graph opens but is empty

The graph visualizes locally cached pages, so it needs at least one successfully extracted article. Visit several long articles, wait for ingestion, and reopen the graph. Related edges appear only when page pairs exceed the local affinity threshold.

Clicking the graph command closes the palette

That is expected: Mycelium closes the overlay after requesting graph.html in a new extension tab. If no new tab appears, check Chrome's pop-up behavior and extension runtime errors; the background message path provides a fallback when window.open is unavailable.

Service worker registration failed

  1. Run pnpm build again.
  2. Confirm dist/assets/background.js exists.
  3. Confirm dist/manifest.json points to assets/background.js.
  4. Remove any stale unpacked copy that points to a different folder.
  5. Load the generated dist folder again.

The build verifies that the service worker is a single emitted file with no static or dynamic imports. Do not point Chrome at a partially generated dist directory.

Settings do not appear to save

Both Moss fields are required. The save action is disabled until the project ID and project key contain non-whitespace characters. A successful save closes Settings and returns to search immediately; index synchronization continues in the background.

Current limitations

  • Mycelium is an unpacked development extension; it is not packaged for the Chrome Web Store.
  • Ingestion is limited to readable HTTP(S) pages with at least 500 words.
  • Sensitive-site detection is heuristic and may need organization-specific rules.
  • The graph renders at most 300 nodes for predictable Canvas performance.
  • Graph edges currently use local shared-term affinity; graph search uses the same Moss semantic path as the palette when Moss is ready.
  • Initial synchronization is not the same as warm-query latency.
  • There is no in-product delete, export, pause-indexing, or per-site allow/deny interface yet.
  • Removing the extension clears its Chrome-local cache and settings.

Relevant references

License

No license file is currently included. Add one before distributing or accepting external contributions.

About

Chrome MV3 extension that indexes long-form pages as you browse. Runs the Moss embedding and vector-search runtime in WebAssembly via @moss-dev/moss-web inside an offscreen document, so query embedding and retrieval happen locally with no network round trip. Command palette search plus a d3-force graph view.

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages