mole

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: Apache-2.0

README

A deep-research agent with an enforced budget, verified quotes, and a privacy boundary for local data.

Ask a question. mole decomposes it, searches, reads sources, extracts claims, checks each claim against the text it came from, looks for contradictions between them, and writes an answer with citations. Every model call is reserved against a budget before it happens and settled after, so the ceiling you set is the ceiling it hits.

It runs as a single static binary on your machine, uses your own API keys, and speaks MCP so a coding agent can drive it — either by handing mole a question and collecting the answer, or, in toolkit mode, by doing the reasoning with its own model while mole supplies the parts that are not model calls.


Why mole

Three things mole does that a chat interface with web search does not.

The budget is enforced, not estimated. Every call is reserved before it is made and settled after, against a ledger with non-negative constraints in the database schema itself. --usd 0.50 means the run stops at fifty cents. Measured overshoot across the test corpus is 0%.

Every claim carries a quote, checked against the source. A claim whose quote does not appear verbatim in the page it was mined from is discarded at extraction, before it can reach an answer. Claims that survive can be re-read against their source afterwards, and one that turns out not to be supported is marked as such in the report rather than quietly dropped.

Your local data stays local. Point mole at a CSV or a folder and it will analyse it without the contents leaving your machine: the model chooses a hypothesis template and column names, mole renders and runs the SQL, and only aggregates — counts, means, test results, buckets covering at least five records — are allowed back. mole crossings shows you exactly what left.


Install

Script — Linux and macOS, amd64 and arm64:

curl -fsSL https://raw.githubusercontent.com/lajosdeme/mole/main/install.sh | sh

Downloads the release archive for your platform, verifies its SHA-256 against the checksums published with the release, and installs mole and mole-mcp into ~/.local/bin (or /usr/local/bin if that is writable). It uses sudo only if the target directory needs it, and --dry-run shows what it would do. If piping a script into a shell makes you uneasy — reasonable — read it first, or use one of the paths below.

Homebrew — macOS and Linux:

brew install lajosdeme/mole/mole

Fully qualified, and it has to be: an unrelated mole (a macOS cleanup tool) is in homebrew/core, so brew install mole will always mean that one. Both install a binary called mole, so only one can be linked at a time.

Arch Linux — from the AUR:

yay -S mole-research-bin      # prebuilt release binaries
yay -S mole-research          # build from source

Not mole: that name and mole-bin on the AUR belong to an SSH tunnelling tool that has held them since 2020. The package installs /usr/bin/mole and declares the conflict, so pacman will tell you rather than overwrite anything.

Debian and Ubuntu.deb from the releases page:

curl -fsSLO https://github.com/lajosdeme/mole/releases/latest/download/mole_amd64.deb
sudo dpkg -i mole_amd64.deb

An .rpm is published for the same platforms.

From source — needs Go 1.25+:

go install github.com/lajosdeme/mole/cmd/mole@latest
go install github.com/lajosdeme/mole/cmd/mole-mcp@latest

Or clone and make install, which stamps the version so mole version reports the tag rather than dev.

Every path installs the same thing: two static binaries with no runtime dependencies, built CGO_ENABLED=0. The database is SQLite, created on first use under your XDG data directory.

Configure

You need a search provider and a model provider. Keys live in ~/.config/mole/config.json, mode 0600 — never in environment variables that leak into process listings, and never in .mcp.json.

mole config set search.provider tavily          # or: brave
mole config set search.tavily-key tvly-...

mole config set llm.provider anthropic          # or: openai-compatible
mole config set llm.api-key sk-...
mole config set llm.model claude-sonnet-5
mole config set llm.cheap-model claude-haiku-4-5

mole doctor                                     # verify everything above

Any OpenAI-compatible endpoint works — DeepSeek, Ollama, llama.cpp, vLLM, a proxy:

mole config set llm.provider openai-compatible
mole config set llm.base-url https://api.deepseek.com/v1
mole config set llm.model deepseek-chat

A model served from localhost is priced at zero and still counted in tokens, so --tokens bounds a self-hosted run that costs no money at all.


Usage

Research a question

mole research "how much electricity does the bitcoin network use?" --usd 0.50
mole research "..." --tokens 200000            # token budget instead of dollars
mole research "..." --max-sources 8 --max-depth 3
mole research "..." --json                      # machine-readable result

Budget is required, and the two units are mutually exclusive. Only dollar mode can price a search call; only token mode can bound a model whose rates mole does not know.

Ask a follow-up

mole ask <session-id> "what did the Cambridge estimate say?"

Answers from the claims that session already collected. No new searching, no new spending beyond the one call to phrase the answer.

Build a dataset instead of prose

mole research "largest UK supermarket chains and their revenue" \
  --mode dataset \
  --schema 'company:text!,revenue:number=annual revenue in GBP,employees:number' \
  --usd 0.50

mole dataset <session-id> --format csv > chains.csv
mole dataset <session-id> --format json          # every value every source gave

! marks the field that identifies a row. Rows are merged across sources by fuzzy key, so Aldi and Aldi UK become one row with two sources. CSV holds one value per cell and says so — it carries a source count and a contested column naming the fields the sources disagree about. JSON carries every disagreeing value with the sources behind each.

Analyse local data

mole connect add sales ./exports/sales.csv       # one file
mole connect add exports ./exports               # or a whole folder
mole research "how does spend differ between regions?" \
  --actors local_compute --usd 0.30

mole crossings <session-id>                      # what left the machine

CSV, TSV, JSON and JSONL are supported; Parquet is not. The model never sees a row and never writes SQL — it picks a template and column names, and mole renders the statement.

Serve MCP clients

mole serve

Listens on a unix socket, mode 0600, in a private directory, and refuses connections from any other user. Point a client at the shim:

{
  "mcpServers": {
    "mole": { "command": "mole-mcp" }
  }
}

No credentials in that file — the shim forwards to the daemon, which holds them.

Use the subscription you already pay for (toolkit mode)

mole serve --toolkit

The arrangement above has mole own the model: it plans, mines and writes with your API key, and the coding agent driving it is pressing a button. Toolkit mode inverts that. The agent's model does the reasoning; mole contributes the deterministic half — which is the half worth having, and the half that does not care whose model is on the other side of it.

If you are inside Claude Code or Qwen Code on a subscription, your model tokens are already paid for. This is the mode for that.

Fourteen tools, each named mole.<tool>, alongside the research.* tools — the flag adds a surface rather than replacing one:

session session_open, session_close
retrieval search, fetch — through mole's SSRF guard, robots handling and rate limiter
evidence verify_quote, claim_add, claims_list, citations
local data connect_list, aggregate — the privacy boundary, unchanged
graph pairs_candidates, edge_add
dataset rows_add, dataset

Inspect a run

mole sessions                # recent sessions and what they cost
mole trace <session-id>      # per-call cost and timing breakdown
mole stats --fetch           # why fetches failed, across sessions

How it works

question
   ↓  planner            decompose into sub-questions, replan as evidence arrives
   ↓  executor           one lead at a time per worker, reserved and settled
   ↓  actor              search → fetch → extract → mine claims
   ↓                     every claim quote-checked against its source
   ↓  verifier           pair up related claims, adjudicate, build the claim graph
   ↓                     re-read a sample of claims against their sources
   ↓  output             synthesise from claims that survived, with citations
answer

Three actor types feed the same graph. web searches and reads pages. academic queries Crossref, OpenAlex, arXiv and PubMed, deduplicates by DOI and prefers open-access full text. local_compute runs deterministic SQL over data you registered and never lets a row reach the model.

Toolkit mode runs the same machinery with the arrows reversed: the agent decides what to search, what to read and which claims relate, and mole does the quote checking, the pair retrieval, the merging and the SQL rendering. Both modes share one copy of each — the same AcceptRow for dataset rows, the same aggregation gate, the same lexical retriever — so a toolkit graph and an autonomous one are built the same way.


Honest numbers

mole grades its own runs. mole eval <session-id> prints a scorecard, and any metric it cannot compute says so instead of quietly reading zero.

budget overshoot 0% — no run has exceeded its ceiling
claim integrity 100% — every stored claim carries a source and a verbatim quote
citation accuracy 100% — every quote found in the source it cites
grounding rate 80% — of claims re-read against their source, confirmed
contradiction precision 70% with the confirm pass, 51% without
merge precision / recall 1.000 / 1.000 on constructed ground truth

Contributing

Bug reports and issues are welcome. Code contributions go through a CLA — see CONTRIBUTING.md, which explains what it is for and what it cannot do.

Maintainers: the release runbook is RELEASING.md.

The one practice this project asks for that most do not: falsify your own fix. After a change, revert the mechanism and confirm the test fails. A test that passes with the fix removed proves nothing, and several of this project's own tests have been caught doing exactly that.

gofmt -l .      # must print nothing
go build ./...
go test ./...   # must be clean, and no new skips

Licence

Apache-2.0. See LICENSE and NOTICE.

Directories

Path Synopsis
cmd
mcp-probe command
Command mcp-probe connects to a running mole daemon the way a coding agent's MCP client does, and prints what the agent would receive.
Command mcp-probe connects to a running mole daemon the way a coding agent's MCP client does, and prints what the agent would receive.
mole command
Command mole is the daemon and CLI.
Command mole is the daemon and CLI.
mole-mcp command
Command mole-mcp forwards MCP over stdio to a running mole daemon.
Command mole-mcp forwards MCP over stdio to a running mole daemon.
internal
actors
Package actors executes leads.
Package actors executes leads.
budget
Package budget enforces spend.
Package budget enforces spend.
cache
Package cache stops a session paying twice for the same artifact (§9.3).
Package cache stops a session paying twice for the same artifact (§9.3).
compute
Package compute runs one hypothesis against a connector and reports what crossed.
Package compute runs one hypothesis against a connector and reports what crossed.
compute/coderunner
Package coderunner executes model-authored analysis inside the sandbox (§12.1).
Package coderunner executes model-authored analysis inside the sandbox (§12.1).
compute/connector
Package connector registers local data sources and hands out read-only handles to them (M8, §12).
Package connector registers local data sources and hands out read-only handles to them (M8, §12).
compute/gate
Package gate is §12.1's aggregation boundary: the only path by which anything derived from local data reaches a model.
Package gate is §12.1's aggregation boundary: the only path by which anything derived from local data reaches a model.
compute/hypothesis
Package hypothesis renders the only SQL that may reach a connector (M8, §12.3).
Package hypothesis renders the only SQL that may reach a connector (M8, §12.3).
compute/sandbox
Package sandbox detects a container runtime and runs code inside one (§3.6).
Package sandbox detects a container runtime and runs code inside one (§3.6).
compute/sqlguard
Package sqlguard is §12.2's parse gate: the check every statement passes before it reaches a connector database.
Package sqlguard is §12.2's parse gate: the check every statement passes before it reaches a connector database.
compute/stats
Package stats is the arithmetic behind §4's statistical-validity check.
Package stats is the arithmetic behind §4's statistical-validity check.
config
Package config stores machine-local settings and provider credentials.
Package config stores machine-local settings and provider credentials.
core
Package core holds the domain types shared by every other package.
Package core holds the domain types shared by every other package.
daemon
Package daemon is the long-lived local server (§5).
Package daemon is the long-lived local server (§5).
dataset
Package dataset is §13's dataset output mode: a table instead of prose.
Package dataset is §13's dataset output mode: a table instead of prose.
eval
Package eval scores a finished session.
Package eval scores a finished session.
executor
Package executor runs a session's lead queue: dispatch, retry, replan, settle.
Package executor runs a session's lead queue: dispatch, retry, replan, settle.
llm
Package llm is the model provider boundary.
Package llm is the model provider boundary.
llm/jsonish
Package jsonish parses the JSON that models actually return.
Package jsonish parses the JSON that models actually return.
mcpserver
Package mcpserver exposes the daemon's research surface over MCP (§5.1).
Package mcpserver exposes the daemon's research surface over MCP (§5.1).
obs
Package obs provides structured logging and lead-level tracing.
Package obs provides structured logging and lead-level tracing.
output
Package output turns a finished session's claims into a report (§13).
Package output turns a finished session's claims into a report (§13).
planner
Package planner decomposes a question into leads and replans as evidence arrives.
Package planner decomposes a question into leads and replans as evidence arrives.
pricing
Package pricing converts model token usage into money.
Package pricing converts model token usage into money.
queue
Package queue is the lead queue: dispatch, leases, and recovery.
Package queue is the lead queue: dispatch, leases, and recovery.
record
Package record is the record/replay cassette layer.
Package record is the record/replay cassette layer.
session
Package session runs one research session end to end.
Package session runs one research session end to end.
store
Package store defines the persistence boundary.
Package store defines the persistence boundary.
store/sqlite
Package sqlite implements store.Store on SQLite via a pure-Go driver.
Package sqlite implements store.Store on SQLite via a pure-Go driver.
tools/academic
Package academic is the provider boundary for scholarly sources (§10.2).
Package academic is the provider boundary for scholarly sources (§10.2).
tools/extract
Package extract turns fetched bytes into clean text plus metadata.
Package extract turns fetched bytes into clean text plus metadata.
tools/fetch
Package fetch retrieves web content under egress, politeness, and rate controls, and classifies every attempt.
Package fetch retrieves web content under egress, politeness, and rate controls, and classifies every attempt.
tools/limiter
Package limiter provides per-key rate limiting.
Package limiter provides per-key rate limiting.
tools/search
Package search turns a question into candidate URLs.
Package search turns a question into candidate URLs.
verifier
Package verifier builds and scores the claim graph (§11).
Package verifier builds and scores the claim graph (§11).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL