extension v0.3 Local-first ChatGPT search + RAG

Your ChatGPT history, finally searchable. Search, ask, and reason over it on your machine.

convsearch is a Chrome extension and a local Python server. The extension quietly captures conversations as you read them on chatgpt.com; the server indexes them for hybrid keyword + semantic search, explainable ranking, cited question-answering, and structured memory — entirely on your own disk. No account, no required API key, nothing leaves your machine.

convsearch side panel beside the message: Search, ask, and reason over your own ChatGPT history.
Local-first by construction: hybrid retrieval, cited answers, and a reason for every rank.
// 01 No sync button Capture is passive — open a conversation, it is saved.
// 02 No required API key Search, capture, and local Ask need no cloud key; cloud is opt-in.
// 03 Loopback only The server binds 127.0.0.1 and never phones home.

01 What it does

Six capabilities, one local process.

F.01

Hybrid retrieval

SQLite FTS5 lexical search fused with local FAISS embeddings via reciprocal rank fusion, then aggregated to conversations.

F.02

Explainable ranking

Every result can show a "ranked because…" reason and the full score breakdown — lexical, semantic, title, reranker, fused, final.

F.03

Structured memory & projects

Durable facts, decisions, constraints, and project dashboards extracted from your conversations and queryable over HTTP.

F.04

Local Ask via Ollama

Cited answers synthesized from your own history — a local Ollama model first, with an opt-in cloud fallback.

F.05

Private self-improvement

Your clicks and asks are logged locally and nudge future ranking. On-device heuristics plus local-LLM summaries — no shared model.

F.06

Side-panel extension

A Chrome side panel beside chatgpt.com — an icon-rail nav across eleven views, a light/dark theme toggle, a universal command bar, and a right-click search.

02 How it works

Browse, capture, search — no step in between.

There is nothing to remember to do. You use ChatGPT the way you already do; convsearch watches, saves, and indexes in the background. Everything runs on-device: a local server on 127.0.0.1:8756 backed by SQLite, FAISS, and a memory graph.

Browse, capture, search flow You browse a conversation on chatgpt.com. A content script captures it and sends it to the local convsearch server over loopback HTTP, which writes it to SQLite. A few seconds later the server builds embeddings and updates the FAISS index. You then search or ask from the extension side panel or the CLI, and results link back to the real conversation on chatgpt.com. 1. Browse Open a conversation on chatgpt.com 2. Capture & index Content script → local server SQLite write, then auto-embed ~seconds later, incrementally 3. Search & ask Side panel, popup, or CLI Links back to chatgpt.com Everything above the arrows happens on your machine — nothing is sent to a cloud service.
STEP 01

Browse normally

No sync button, nothing to click. A conversation only needs to be one you've opened in a tab while the extension was installed.

STEP 02

Capture is automatic

A content script scrapes the conversation after the page settles (about 1.5s of no DOM changes), sends it to 127.0.0.1:8756, which writes it to SQLite.

STEP 03

Indexing runs on its own

The server debounces and rebuilds embeddings for new passages a few seconds after capture — and catches up any unindexed content on startup — so conversations become searchable without a click. The panel shows an ambient index status; a manual "Rebuild" stays out of the way as a quiet recovery affordance.

03 Local-first

Nothing leaves your machine.

convsearch was built around one constraint: your conversation history should never have to leave your disk to be searchable.

Conversation text goes from the browser tab, to the extension's background worker, to 127.0.0.1, to a SQLite file on your machine — and nowhere else.

  • No cloud service. No conversation text is ever sent to a third party — not for search, not for embeddings.
  • No API key. The CLI and server never require a cloud API key. Embeddings run locally with Sentence Transformers.
  • Loopback bind. convsearch serve binds 127.0.0.1 by default and should never run with --host 0.0.0.0 — there is no authentication on the server.
  • Strict CORS. The server echoes an allow-list — the extension's origin plus chatgpt.com and chat.openai.com, never a wildcard — so an arbitrary page cannot read your results.
  • Everything on disk stays on disk. Raw exports, SQLite data, the FTS5 index, and the FAISS vector index are all local files in your workspace.

04 Retrieval

Keyword search fused with local embeddings.

Search runs two independent retrieval channels over your workspace and combines them, rather than relying on either alone.

SQLite FTS5 (lexical) + FAISS IndexFlatIP (semantic) Reciprocal Rank Fusion conversation-level aggregation

Passages are embedded locally with a Sentence Transformers model (BAAI/bge-small-en-v1.5 by default, configurable) and stored in a FAISS IndexFlatIP index. Lexical and semantic candidates are fused with Reciprocal Rank Fusion, then rolled up from passages to conversations using explicit features — best passage score, top-three mean, distinct messages and segments touched, channel diversity, and a small length penalty.

Live-captured conversations only ever have one path, since the browser renders only the branch you're looking at. Conversations imported from an export ZIP can carry alternate branches, indexed but excluded from search unless you pass --include-branches (or branches=1 on the popup/API).

Query syntax is intentionally small: quote phrases ("local indexes"), exclude terms with -term, and technical identifiers like IndexFlatIP or BAAI/bge-small-en-v1.5 are preserved as-is.

Be aware: quoting and -exclusion only constrain the lexical (FTS5) channel. Because results are fused across both channels, a conversation can still surface through the semantic channel even if it doesn't satisfy an exclusion — rank fusion, not a strict filter, decides what you see.

Explainable ranking. Every result shows why it ranked where it did — a short "Ranked because…" reason plus a legible score mini-chart — on by default in the side panel, with a toggle to hide it. From the CLI or API it's convsearch search --explain or GET /search?explain=1.

05 Ask, don't just search

A cited answer from your own conversations.

Retrieval is nice, but sometimes you want the answer, not ten links. ask retrieves the most relevant passages from your past conversations and has a language model synthesize a written answer that cites the source conversations it drew from — one numbered entry per source, with title, date, and a quote.

convsearch ask "how did I set up FAISS locally?" -w ./workspace
# or over HTTP:  GET /ask?q=how%20did%20I%20set%20up%20FAISS%20locally

Generation backend is selectable with --backend auto|ollama|anthropic:

  • auto (default) tries a local Ollama model first — ollama serve and ollama pull gemma3:1b — and only falls back to the cloud if it isn't there.
  • ollama forces the fully local path. Nothing leaves the machine.
  • anthropic forces the cloud path, which needs ANTHROPIC_API_KEY and the llm extra — the only path that sends retrieved passages off the machine, and only if you choose it.

The local query planner (convsearch plan) goes a step further: it returns a grounded, cited natural-language answer with supersession tracing (which decisions replaced which) and open-task filtering, drawing on the same structured memories and projects.

Structured memory & projects

GET /memories lists facts, decisions, constraints, and open questions; GET /memories/{id} returns one with evidence, relations, and status history. GET /projects/{name} returns a dashboard — summary, architecture, decisions (including superseded ones), open/completed tasks, risks, known bugs, milestones, and the conversations behind it all. Extraction is deterministic by default; memories extract --llm (--backend auto|ollama|anthropic) opts into LLM-assisted extraction and degrades cleanly when no backend is present.

06 Measured, not marketed

What it actually costs, on one machine.

These numbers come from scripts/bench.py against the real embedding model (BAAI/bge-small-en-v1.5) on CPU, on the machine this project was built on. They describe the shape of the cost, not a guarantee for your hardware.

Corpus sizeCapture round-tripWarm search
30 conversations31.5ms93.5ms
105 conversations37.0ms91.6ms
305 conversations36.5ms122.7ms

At 300 conversations (about 1,200 passages), a full warm search is ~96ms, and roughly 60% of that is a single forward pass to embed the query text — close to the floor for this model on CPU. A capture (writing a scraped conversation to SQLite) is cheap, around ~35ms end to end, and stays flat as the workspace grows because indexing is incremental rather than a full rebuild.

A conversation you open does not become searchable instantly. The server debounces embedding for a few seconds after capture, so expect a short, single-digit-second delay between opening a conversation and finding it in search — not zero, and not minutes.

07 The extension

A side panel, not just a popup.

The extension declares a Chrome side_panel and a contextMenus entry. The side panel is the main surface — it stays open beside chatgpt.com behind a left icon rail spanning eleven views, with a light/dark theme toggle (dark by default, persisted) and a universal command bar. The toolbar popup remains as a quick-search fallback.

A real screen recording of the Ask & Search flow in the side panel — cited answer first, ranked results below.
  • Ask & Search — a level switcher (Conversations / Segments / Passages), profile and branch controls, and "Ranked because" shown by default with a legible score mini-chart per result.
  • Plan, Tasks & Timeline — grounded cited answers, open-task filtering, and a chronological view of your history.
  • Memories, Projects & Review — the structured memory store, project workspace dashboards, and an extraction review queue.
  • Captures, Privacy & Status — an ambient index/capture status, local-only privacy controls, and troubleshooting — plus right-click → "Search convsearch for …" on selected text.

The core stays local. The panel talks only to the loopback server through the background service worker — no conversation text leaves your machine unless you opt into the cloud Ask backend.

convsearch
Convs Segments Passages Ranked because
Choosing a vector index for local search 2026-07-24 · score 0.412
Ranked because a strong exact match on IndexFlatIP agreed with the semantic channel, and the reranker kept it on top.
assistantIndexFlatIP does exact search over normalized vectors, which is fine at this scale…
lexical0.82
semantic0.71
reranker0.64
final0.41

Mock UI for illustration — not a screenshot. Query text, reasons, and scores are examples, not live data.

08 Private self-improvement

It gets better as you use it — on-device.

convsearch logs your own interactions — searches, opens, inspects, asks — to a local interactions table, and uses them to improve future results. All of it runs on-device; nothing is uploaded, and there is no shared model.

Learned preferences are heuristic plus local-LLM summaries of your own usage, not a trained model — a nudge on ranking, not a guarantee. learn clear --yes deletes the logged interactions whenever you want a clean slate.

  • Click-informed ranking. Prior opens can boost future ranking via GET /search?boost=1; interactions are recorded with POST /feedback.
  • Query suggestions. GET /suggestions surfaces your recent and popular queries.
  • Learned preferences. convsearch learn run uses the local LLM to summarize your behavior into durable notes in a learned_preferences table. Inspect them with learn show and GET /learn/stats.

09 Install

Run the server, load the extension.

Requires Python 3.12+ and uv. Everything below runs on your own machine.

One command: run bash scripts/convsearch-up.sh (macOS/Linux) or powershell -File scripts/convsearch-up.ps1 (Windows) to sync, index, and start the server in one go — then just load the extension (step 3). No ChatGPT export is needed: the launcher starts on an empty workspace and fills up as you browse. Prefer to see each piece? Step through it manually below. Once the native messaging host is installed (install-native-host), the extension can even auto-start the server for you.

No export required. Live capture is the primary path — install the extension, keep the local server running, and every conversation you open on chatgpt.com is captured and auto-indexed. Importing a ChatGPT export ZIP is an optional accelerator to backfill older history you never reopen; skip it entirely and convsearch still works from your first browse.

  1. Install and create a workspace

    uv sync --extra ml --extra llm
    uv run convsearch init ./workspace

    Optionally seed it from an existing ChatGPT export ZIP, then build the index once so imported conversations are searchable immediately:

    uv run convsearch import ~/Downloads/chatgpt-export.zip --workspace ./workspace
    uv run convsearch index --workspace ./workspace

    Live capture auto-indexes in the background, so convsearch index is mainly needed after a bulk import or as a manual rebuild.

  2. Start the local server

    uv run convsearch serve --workspace ./workspace

    Leave it running while you browse. It binds 127.0.0.1:8756 (stdlib http.server, strict CORS allow-list) and exposes GET /health, GET /search (with level, explain=1, boost=1), GET /ask, GET /memories, GET /memories/{id}, GET /projects, GET /projects/{name}, GET /conversation/{id}, GET /suggestions, GET /learn/stats, POST /capture, POST /reindex, and POST /feedback.

  3. Load the extension, unpacked

    Chrome doesn't publish this to the Web Store — you load it directly from source:

    1. Open chrome://extensions.
    2. Turn on Developer mode (top right).
    3. Click Load unpacked and select the extension/ folder from the repository.
    4. Pin the extension and open its side panel from the toolbar. Right-clicking selected text also offers "Search convsearch for …".
  4. Browse, then search — or ask

    Open a few conversations on chatgpt.com. Open the side panel and search — indexing is automatic and the ambient status shows when it has caught up (usually within seconds). Use Ask & Search (or the CLI) for a cited answer:

    uv run convsearch ask "how did I set up FAISS?" -w ./workspace

    For a fully local answer, run ollama serve and ollama pull gemma3:1b first; otherwise set ANTHROPIC_API_KEY and install the llm extra to use the cloud backend.

10 Honest limitations

What this doesn't do.

Stated plainly, because the design choices behind them are deliberate trade-offs, not bugs.

Only conversations you open

Capture is passive and only sees conversations opened in a tab. Older conversations you never revisit need the export-ZIP import route.

Depends on ChatGPT's DOM

There's no official API — the content script reads the rendered page. A redesign can break capture. It's built to fail quietly rather than break the page.

The date isn't ChatGPT's timestamp

For a captured conversation, the date is when convsearch saw it. Conversations imported from an export ZIP do carry ChatGPT's real timestamp.

Query filters aren't absolute

-exclusion and quoted phrases only constrain the lexical channel; rank fusion means the semantic channel can still surface a match.

Visible branch only, live

Live capture only sees the branch you're currently looking at. Alternate branches exist only for conversations imported from an export ZIP.

No filtering by role

Tool calls and reasoning-style turns are indexed alongside ordinary prose — a search can occasionally surface machinery rather than conversation.

Segments lag behind live capture

Incremental indexing embeds new passages but doesn't rebuild segments, so --level segment search stays stale until the next full rebuild.

Local answers need Ollama or a key

ask and plan only generate text with a local Ollama model running or a cloud Anthropic key set. Retrieval still runs locally.

Learned preferences are heuristic

The self-improvement loop is on-device heuristics plus local-LLM summaries of your own clicks — a nudge on ranking, not a trained model.

Get started

Your ChatGPT history, searchable and local.