punt-quarry
Local semantic search for AI agents and humans.
Quarry indexes documents in 20+ formats, embeds them with a local ONNX model (snowflake-arctic-embed-m-v1.5, 768-dim), stores vectors in LanceDB, and serves semantic search to Claude Code, Claude Desktop, and the CLI. Everything runs locally — no API keys, no cloud accounts. The embedding model (~120 MB int8) downloads once on first use. CUDA GPUs are auto-detected for faster inference.
Platforms: macOS, Linux
Quick Start
curl -fsSL https://raw.githubusercontent.com/punt-labs/quarry/6f90f11/install.sh | sh
Restart Claude Code, then:
> /ingest report.pdf # index a document (runs in background)
> /quarry status # after a moment, confirm it's there
> /find "what does the report say about margins" # search by meaning
Once installed, a plugin hook auto-indexes your current project directory on every session start — you don't need to /ingest your codebase manually.
uv tool install punt-quarry
quarry install
quarry doctor
curl -fsSL https://raw.githubusercontent.com/punt-labs/quarry/6f90f11/install.sh -o install.sh
shasum -a 256 install.sh
cat install.sh
sh install.sh
Remote Server
Run quarry on a GPU server and connect from any Mac or Linux client over TLS.
Server (GPU host, serves remote clients):
export QUARRY_API_KEY=$(openssl rand -hex 32)
curl -fsSL https://raw.githubusercontent.com/punt-labs/quarry/6f90f11/install.sh | sh -s -- --network
Generates TLS certificates, binds daemon to 0.0.0.0, registers a systemd service, and prints a CA fingerprint. NVIDIA GPUs are auto-detected for CUDA inference.
Client (connects to remote server):
curl -fsSL https://raw.githubusercontent.com/punt-labs/quarry/6f90f11/install.sh | sh
quarry login <server-hostname> --api-key <token>
No special flag needed --- the default install runs a local daemon on localhost. quarry login redirects queries to the remote server over wss:// with TOFU certificate pinning.
Claude Desktop
Download punt-quarry.mcpb and double-click to install. Alternatively, quarry install configures Claude Desktop automatically.
Note: Uploaded files in Claude Desktop live in a sandbox that quarry cannot access. Use remember for uploaded content, or provide local file paths to ingest.
Features
- 20+ formats --- PDFs (with OCR for scanned pages), source code (AST-aware splitting), spreadsheets, presentations, HTML, Markdown, LaTeX, DOCX, images
- Semantic search --- retrieval is by meaning, not keyword. A query about "margins" finds passages about profitability even if they never use that word
- Daemon architecture --- one
quarrydprocess loads the embedding model once and serves the CLI and hooks over a versioned REST API (/v1); Claude Code connects through the stdioquarry mcpserver - Passive knowledge capture ---
quarry enablesets up three scoped collections per project: file sync, passive captures (web fetches + session transcripts), and per-agent memory. Captures are separated from the code index so research doesn't pollute code search - Named databases --- isolated LanceDB directories with independent sync registries. Switch with
usefor work/personal separation - Research agent ---
researchersubagent combines quarry local search with web research, auto-ingests valuable findings
What It Looks Like
Ingest a document
> /ingest report.pdf
▶ Ingesting report.pdf (background)
Check what's indexed
> /quarry
▶ Database: default
Documents: 47
Chunks: 1,203
Size: 12.4 MB
Model: snowflake-arctic-embed-m-v1.5 (768-dim)
Search by meaning
> /find "what were the Q3 revenue figures"
▶ [report.pdf p.12 | text/.pdf] (similarity: 0.4521)
Third quarter revenue reached $142M, up 18% year-over-year,
driven primarily by expansion in the enterprise segment.
Gross margins improved to 71% from 68% in Q2.
Commands
Slash Commands (Claude Code)
| Command | What it does |
|---|---|
/ingest <source> |
Ingest a URL, directory, or file |
/remember <name> |
Ingest inline text under a document name |
/find <query> |
Semantic search. Questions get synthesized answers; keywords get raw results |
/explain <topic> |
Search and synthesize an explanation |
/source <claim> |
Find which document a claim comes from |
/quarry [sub] |
Manage: status, sync, collections, databases, registrations |
MCP Tools
| Tool | Purpose | Execution |
|---|---|---|
ingest |
Index a file or URL | Background |
remember |
Index inline text | Background |
register_directory |
Register directory for sync | Background |
sync_all_registrations |
Re-index all registered directories | Background |
find |
Semantic search with filters | Sync |
show |
Document metadata or page text | Sync |
list |
Documents, collections, databases, registrations | Sync |
status |
Database statistics | Sync |
delete |
Remove document or collection | Background |
deregister_directory |
Remove registration (validates; errors if unknown) | Sync |
use |
Switch active database | Sync |
CLI
quarry ingest report.pdf # index a file
quarry ingest https://example.com # index a webpage
echo "notes" | quarry remember --name notes.md # index inline text
quarry find "revenue trends" # hybrid search (vector + FTS)
quarry list documents # list indexed documents
quarry register ~/Documents/notes # watch a directory
quarry sync # re-index registered dirs
quarry use work # switch database
quarry enable # set up project collections + captures
quarry disable # remove project registration + data
quarry captures init # bootstrap the private capture shadow repo
quarry captures push # re-scrub + push captures to the shadow
quarry status # database dashboard
quarry doctor # health check
quarry install # set up daemon service, TLS certs, mcp-proxy
quarryd # run the engine in the foreground (normally the service runs it)
# Remote connections
quarry login okinos.local --api-key <token> # TOFU login to remote server
quarry logout # disconnect, revert to local daemon
quarry remote list --ping # show remote config and health
# Agent memory tagging
quarry ingest notes.md --agent-handle claude --memory-type fact
quarry find "deployment steps" --agent-handle claude
echo "key insight" | quarry remember --name insight.md --agent-handle claude \
--memory-type observation --summary "Key insight from review"
Setup
Quarry works with zero configuration. These environment variables are available for customization:
| Variable | Default | Description |
|---|---|---|
QUARRY_PROVIDER |
(auto) | ONNX execution provider: cpu, cuda, or unset (auto-detect) |
QUARRY_API_KEY |
(none) | Bearer token for quarryd (required for a non-loopback bind) |
QUARRY_ROOT |
~/.punt-labs/quarry/data |
Base directory for all databases |
CHUNK_MAX_CHARS |
1800 |
Max characters per chunk (~450 tokens) |
CHUNK_OVERLAP_CHARS |
200 |
Overlap between consecutive chunks |
For the full configuration reference, see Architecture section 7.
Passive Knowledge Capture
Beyond explicit /ingest and /find commands, quarry runs as a Claude Code plugin with hooks that capture knowledge automatically during your sessions:
| Hook | When it fires | What it does |
|---|---|---|
| Session start | On every session start | Auto-registers your project directory and syncs it in the background. Your codebase is searchable without manual ingestion. |
| Web fetch | After any WebFetch tool call |
URLs Claude fetches during research are auto-ingested into the project's <name>-captures collection (or global web-captures if no project is enabled). |
| Pre-compact | Before context compaction | Captures the conversation transcript into the project's <name>-captures collection (or global session-notes if no project is enabled). |
All hooks are fail-open — failures are ignored and never block Claude Code. Each hook is individually toggleable via .punt-labs/quarry/config.md YAML frontmatter. See AGENTS.md for the full integration model.
Privacy: captures are scrubbed before write
Captures (session transcripts and auto-ingested web fetches) are redacted at write time, before anything touches disk or the <name>-captures/web-captures collection. A single write choke point scrubs, in order, secrets → filesystem paths (/Users//home/<user>/ → ~/) → emails (→ [REDACTED:email]) → the local hostname (→ [REDACTED:hostname]) → profanity. Web-fetch captures also redact the source URL's userinfo, query, and fragment so a URL like …/reset?email=…&token=… cannot leak. Redaction is idempotent and fail-closed (on any scrub error, no capture is written). Deliberately-ingested content (quarry ingest, remember) is not scrubbed — only the automatic capture paths are, since those feed the shared/pushable collections. See DES-036.
Private capture shadow repo (<repo> → <repo>-quarry)
Redacted capture .md files live in the gitignored .punt-labs/quarry/captures/ dir with no durable home. Enable the optional shadow sync to push them to a per-project private repo <repo>-quarry instead. Opt in by uncommenting the shadow: block in .punt-labs/quarry/config.md:
shadow:
enabled: true # off by default — a network + security action
remote: "" # empty → derive <origin>-quarry from the public remote
acknowledge_unverified: false # required to push when gh cannot confirm the remote is private
Pre-create the private repo first. quarry does not create it by default (a wrong owner or an accidental public repo would leak). Create <repo>-quarry as private and let the remote derive, or run quarry captures init --create to create it via gh (which verifies visibility is private before any push).
Once enabled, the captures dir becomes a standalone nested git repo. quarry captures push — and the tail of every quarry sync — re-scrubs the staged captures with the same DES-036 scrubber, aborts the commit if any file is not clean, then commits and pushes. Auth reuses your existing git credentials; quarry stores no new secret. The push is fail-open: a network or auth failure never blocks a session.
Visibility is load-bearing. The re-scrub cannot catch every residual (IDN emails, non-/Users//home paths, a hostname from another machine), so those are backstopped only by the remote being private. quarry therefore refuses to push to a verifiably public remote, and requires acknowledge_unverified: true in .punt-labs/quarry/config.md when gh is absent and it cannot confirm the remote is private. quarry doctor reports the shadow state, and flags a required failure if the public repo already tracks capture files (which git rm --cached stops going forward, but an already-pushed capture needs a history purge — git filter-repo/BFG + force-push — coordinated with the repo owner). See DES-039.
How It Works
Quarry runs as a daemon — one quarryd process per machine holds the engine (embedding model, LanceDB, pipeline, sync registry) and serves a versioned REST API under /v1. The CLI and Claude Code hooks reach it over HTTP; Claude Code's MCP tools run through the stdio quarry mcp server, which is itself a thin REST client of the daemon — it loads no engine.
HTTP /v1 (TLS, pinned CA)
CLI + hooks <---------------------------------------> quarryd
(one engine)
stdio, MCP JSON-RPC HTTP /v1
Claude Code <--------------------> quarry mcp <-----> quarryd
One resident engine means the CLI and hooks reuse the daemon's loaded model instead of a per-invocation ~200 MB reload. Remote and loopback REST connections use TLS with a self-signed, pinned CA — even on localhost.
The target is that every interface — CLI, library, and MCP — is a thin client of that one daemon over a versioned REST contract; this daemon-first model is specified in DES-031 v2 (ACCEPTED, in implementation). MCP is now a pure client: quarry mcp reaches the daemon through QuarryClient (loopback token, or a remote login's TLS + pinned CA), loading no engine of its own. The CLI and library still load the engine in-process on some local calls; those remaining local-engine paths are being removed.
Claude Code's plugin runs quarry mcp directly — no mcp-proxy in quarry's MCP path. quarry install still downloads mcp-proxy (SHA256-verified, correct platform) because it remains a supported tool for other consumers, and configures MCP clients to spawn quarry mcp.
Managing the daemon
quarry install registers quarryd as a per-user background service that starts at login and restarts on crash — launchd on macOS (label com.punt-labs.quarry) and systemd on Linux (unit quarry.service). You rarely need to touch it, but after upgrading the wheel you must restart the service so the new engine loads — a running daemon holds the old code in memory (quarry doctor reports the CLI version, not the daemon's).
macOS (launchd):
launchctl kickstart -k gui/$(id -u)/com.punt-labs.quarry # restart (use after an upgrade)
launchctl unload -w ~/Library/LaunchAgents/com.punt-labs.quarry.plist # stop
launchctl load -w ~/Library/LaunchAgents/com.punt-labs.quarry.plist # start
launchctl list com.punt-labs.quarry # status (shows PID, or "-" if stopped)
Linux (systemd, user scope):
systemctl --user restart quarry # restart (use after an upgrade)
systemctl --user disable --now quarry # stop
systemctl --user enable --now quarry # start
systemctl --user status quarry # status
quarry doctor confirms quarryd: running and ready. quarry uninstall removes the service and its unit file. To run the engine in the foreground for debugging, run quarryd directly — it blocks until you stop it.
Documentation
Architecture | Z Specification | Design | Agents | Changelog
Development
uv sync # install dependencies
make check # run all quality gates (lint, type, test)
make test # test suite only
make format # auto-format code
make docs # build LaTeX documents
make eval # retrieval-quality eval harness (MRR/success@k, dev/CI only)
Roadmap
Direction tracks the PR/FAQ. Current focus:
- Retrieval quality — a
make evalharness (per-bucket MRR/success@k + a metadata-pollution diagnostic) now baselines search on quarry's own data; embedding levers (contextual embeddings, late chunking) are measured against it before adoption. See docs/eval-harness-design.md. - Private capture sync — captures redact PII at write time, and the opt-in per-project private shadow repo (
<repo>→<repo>-quarry, above) now pushes the redacted captures off the public repo entirely.
License
MIT
No comments yet
Be the first to share your take.