A Go retrieval engine with no chunking, no embeddings and no vector database.
It finds a document's own contents, picks the pages that answer a question, returns those pages verbatim, and pins each claim in the answer to a line on its page.
See it work · Status · How it works · Results · Dependencies · Quick start · API · Config
How Vectorless reads, as one machine. The cabinet is the document, one drawer per section. The dials are Jev's probabilities, and the crane only goes where a needle points. On the light table the stylus marks the exact line the answer cites. Ingest → ask → select → read → cite → answer. As video.
Recorded live on vectorless.store on 12 October 2026, before the hosted service shut down. The 3M 2018 10-K (160 pages) was ready to query 13 seconds after upload. The question below was answered in 5.4 seconds, with 3 of its 4 citations pinned to exact lines. Clicking one opens page 39 with the "Total Company" capital spending row highlighted: $1,577M in 2018 against $1,373M in 2017.
Watch the full 78-second walkthrough: upload, live ingest, the structure tree, the question and the pincite.
The hosted Vectorless service is closing (decided 2026-10-11). The code is open source under Apache 2.0 and is no longer actively developed. Issues and pull requests may not get a reply. For anything else, write to halleluyaholudele@gmail.com.
The engine ships as a single binary and runs on Postgres plus object storage. Ingest builds a table of contents with page numbers. A query picks sections, then pages, and returns the pages as they are. The only generated text anywhere in the pipeline is the final answer. The machine at the top of this page shows the whole loop; here it is stage by stage.
- Layout. pdfgrab, a pure-Go port of pdfplumber, reads every word and its rectangle on every page. The engine imports it under its earlier module path,
github.com/hallelx2/pdftablev0.4.0. - Contents found. Code lists the candidate entries of the document's contents page. Jev, TypeSafe's System One model, confirms which ones are real.
- Contents placed. For each entry, code searches the body for where that heading actually appears. Jev picks the page.
- Leaf splitting. Any section longer than 20 pages is split at its own headings, so no leaf of the tree is huge. Jev decides which sub-headings to keep.
- Page images. After the document is ready,
pdftoppmrenders each page at 144 dpi for the citation view.
Ingest makes no generative call (2026-09-18 report). Markdown, HTML, DOCX and plain text are parsed into a heading tree instead. They have no pages, so they get no page images and no pincites.
The retrieval strategy is called judgewalk in configuration and navigate in API responses.
- Jev ranks the tree's sections against the question.
- It skims the head of each candidate page, then reads the best pages in full.
- It follows cross-references such as "see Note 7" to the page they point at.
- The chosen pages come back verbatim, with their page ranges.
Retrieval makes no generative call (2026-09-18 report).
A pincite is a citation to the exact page or line, the way a legal brief cites a page of a judgment.
- A chat model of your choice writes the answer over the evidence pages, marking claims with
[n]. - For each claim, code lists candidate quotes from that page's own sentences and table rows, and keeps a shortlist of 10.
- Jev picks one quote per claim, in one batched call for the whole answer. It can also pick
none. - If Jev's confidence is below 0.35, the citation falls back to page level.
- The quote is located on the page exactly, or at 80% or more token overlap. Quotes under 4 tokens must match exactly.
- The citation is drawn as one highlight rectangle per line, in page coordinates normalised to 0–1.
Citations come at line, page or section precision. There is no table-cell precision. Every quote is taken from the page's own text, so no generated text can reach a citation.
Every answer is stored with a trace_token. POST /v1/replay with that token returns the stored response byte for byte. Replay is a record of what was answered. It does not re-run the query, and re-running would not necessarily give the same result (see Results). The store is an in-memory LRU, 1,024 entries for 24 hours by default (retrieval.replay), so it does not survive a restart.
If a decision can be written as a closed set of options, it goes to the judge.
Choosing a contents entry, a page, a section or a quote is a selection, not writing. Jev selects. A generative model writes only the answer. The reasoning is in Judgement, not generation (2026-09-25).
All figures are from FinanceBench 10-K filings unless stated. Each links to the report that measured it. The full list is in docs/evaluations.
| Claim | Number | Date | Report |
|---|---|---|---|
| Building the contents tree on Jev instead of a generative model | 12–143 s and $0.0003–0.0015 per filing, against 103–840 s and $0.017–0.095; title recall 0.961 against the generative trees | 2026-09-18 | toc-extraction-on-a-judge |
| Placing contents entries on their pages (11 filings) | Generative trees with no page resolver: 16/20 gold pages covered, median leaf 183 pages, $0.246. Same trees resolved on Jev: 20/20, median leaf 36 pages, $0.0046 | 2026-09-18 | jev-page-resolver |
| Navigation, first Jev run (40 questions) | 37/40 right section, 34/40 every gold page, $0.0029 per question | 2026-09-18 | retrieval-navigation-on-a-judge |
| Navigation after leaf splitting | 40/40 right section, 36/40 every gold page, $0.0039 per question | 2026-09-19 | leaf-granularity |
| Head-to-head at k=5: Vectorless, BGE-small embeddings, BM25 | hit@5 0.925 / 0.375 / 0.200; F1@5 0.498 / 0.170 / 0.096; answer in top-1 0.750 / 0.225 / 0.075 | 2026-09-21 run | head-to-head-chunk-and-embed |
| An embedding pre-filter does not help (negative result) | At k=50, BGE finds every gold page for 0.875 of questions, below judgewalk's 0.925 at k=5 | 2026-09-25 | query-latency-and-the-embedding-prefilter |
| Query latency, navigator called directly | median 50.5 s became 1.9 s, still 36/40; $0.00402 became $0.00388 per question | 2026-09-28 | latency-was-our-cpu |
| Ingest latency per filing | median 66.1 s became 4.1 s, still 47/47 gold pages covered; $0.133 for 21 filings | 2026-09-28 | latency-was-our-cpu |
| Against PageIndex 0.2.22 on the 3M 2018 10-K (160 pages, 6 questions × 3 repeats) | retrieval median 1.70 s vs 7.05 s; full answer median 5.27 s vs 10.51 s; ingest 10.5–19.2 s vs 204–210 s; retrieved pages hold the answer 18/18 vs 17/18 | 2026-10-08 | pageindex-latency |
| Run-to-run agreement across two repeats | The returned set matched exactly 0.38 of the time; Jaccard 0.83 | 2026-09-21 run | head-to-head-chunk-and-embed |
What these numbers do not show:
- Retrieval is not answer accuracy. hit@5 says the evidence was among the returned pages. It says nothing about whether the answer written from them was right. Do not set these figures against answer-accuracy figures such as PageIndex's published 98.7%. A small answer pilot (27/30 correct on 5 filings, graded with GLM-4.6 using the grading prompt from PageIndex's Mafin evaluation) is too small to compare with anything, and is not published as a report.
- 1.9 s and 4.1 s are benchmark-tool figures. navbench calls the navigator directly, without the HTTP layer, database reads or the answer. The production API answered in about 3.5–6 s.
- The PageIndex comparison is one filing on one machine. When both systems use the same generative model for navigation, the answer gap narrows to 7.6 s against 10.5 s at the median and PageIndex wins 2 of 6 questions. The report says what it does not claim.
- Jev is not deterministic. See the run-to-run row above. Replay returns a stored answer; it does not make the engine deterministic.
- Multi-hop questions are unmeasured.
Pincite figures are from one filing, the 3M 2022 10-K (252 pages): the page layout was built in 3.0 s, the pincite step takes 0.4–0.6 s inside /v1/answer, and every claim checked by eye landed at line precision. That last one is a small visual check, not a benchmark.
To reproduce the FinanceBench runs, see cmd/navbench, cmd/tocdump and the harness in vectorless-bench. Each report lists its harness, corpus and commands.
- Jev is a hosted API. It is TypeSafe's System One model (
jev-latest, endpointapi.typesafe.ai/v1/systemone). System One models make fast, structured decisions and write no prose. One call carries a shared state and N typed questions: yes/no with a probability, a choice among options with the distribution, or a position on an ordered scale. It costs $0.042 per million input tokens and output is free. Its size is not published. With a key set, document text is sent to TypeSafe, so "nothing leaves your network" is not true. - Set the key with
TYPESAFE_API_KEY(orVLE_TYPESAFE_API_KEY/VLS_TYPESAFE_API_KEY).TYPESAFE_BASE_URLandTYPESAFE_MODELoverride the endpoint and model. - Without a key the engine still runs, but not the pipeline measured above:
- Ingest runs the contents-page decisions on your generative model, one call per page. It is minutes slower on long filings, and leaf splitting is off.
judgewalkfalls back totreewalk, where your chat model walks the pages in up to eight generative hops.- Pincites choose quotes by word overlap instead of Jev.
- When Jev refuses (no credit, a rejected key, an outage), the server binary serves the query with
treewalkinstead and logsretrieval: primary model unavailable, served by fallback. This was added after the Judge provider ran out of credit on 2026-10-08 and every production query failed for as long as it lasted. - A generative model writes answers: Anthropic, OpenAI, Gemini, or an Anthropic-compatible gateway such as GLM on z.ai. Callers can also send their own key per request.
- poppler's
pdftoppmrenders page images.Dockerfile.serverinstalls it.
The dashboard, control plane, engine and Postgres all start from one command in the vectorless repository. You sign up as the first user, enter your model keys on Settings → Model providers, and upload a document. Keys take effect without a restart.
Clone the three repositories side by side, then start the stack:
mkdir vectorless && cd vectorless
git clone https://github.com/hallelx2/vectorless.git
git clone https://github.com/hallelx2/vectorless-engine.git
git clone https://github.com/hallelx2/vectorless-control-plane.git
cd vectorless/selfhost && docker compose up -d --build
# when every service is healthy: http://localhost:3000There is no .env to write. Secrets are generated on first boot.
First boot of a self-hosted instance: the administrator account, then model keys entered on Settings → Model providers. The full guide is in selfhost/README.md.
The engine has two entry points:
| Binary | What it is | Use it for |
|---|---|---|
cmd/server (Dockerfile.server) |
The multi-tenant HTTP server production ran. It has pincites, page images, live ingest progress and the strategy fallback. | Running the pipeline described above. |
cmd/engine (Dockerfile, Dockerfile.allinone) |
The standalone engine with --local zero-config mode and the /v1/replay route. |
Trying the engine on a laptop. |
One container with the engine, a bundled Postgres and a web UI. Build it from this repository with docker build -f Dockerfile.allinone -t vectorless .. The halleluyaholudele/vectorless image on Docker Hub was last pushed on 2026-07-04, before Jev, judgewalk and pincites existed, so don't use it for the pipeline above. The image defaults to fast minimal ingest and GLM through z.ai, so it does not run the Jev pipeline unless you turn it on:
docker run -p 8080:8080 -p 7654:7654 \
-e VLE_LLM_ANTHROPIC_API_KEY=<your key> \
-e TYPESAFE_API_KEY=<your TypeSafe key> \
-e VLE_INGEST_MODE=toc \
-e VLE_RETRIEVAL_STRATEGY=judgewalk \
vectorless
# UI → http://localhost:8080
# API → http://localhost:7654git clone https://github.com/hallelx2/vectorless-engine.git && cd vectorless-engine
docker compose up -d postgres
export VLE_LLM_ANTHROPIC_API_KEY=<your key>
export TYPESAFE_API_KEY=<your TypeSafe key>
export VLE_INGEST_MODE=toc VLE_RETRIEVAL_STRATEGY=judgewalk
go run ./cmd/engine --local # zero-config local mode on :7654For the dashboard and everything around it, use the self-host stack above.
Routes are versioned under /v1. This is the server binary's surface.
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/health · /v1/version |
Liveness and build |
POST |
/v1/documents |
Ingest a document (multipart or URL; async, 202) |
GET |
/v1/documents · /v1/documents/{id} |
List, or get status and ingest progress |
DELETE |
/v1/documents/{id} |
Delete a document and its stored files |
GET |
/v1/documents/{id}/tree · /structure |
Section tree; structure with page spans |
GET |
/v1/documents/{id}/llms.txt · /text · /source |
Contents as llms.txt; plain text; original bytes |
GET |
/v1/documents/{id}/pages · /pages/{n}/image |
Page sizes for pincites; one page image |
GET |
/v1/sections/{id} |
One section with its content |
POST |
/v1/query · /v1/query/stream |
Retrieve evidence (streamed: one event per navigation stage) |
POST |
/v1/query/multi · /v1/query/multi/stream |
Retrieve across several documents |
POST |
/v1/answer |
Retrieve, answer and pincite in one round trip |
POST |
/v1/answer/treewalk |
Answer with the generative page walk |
POST |
/v1/answer/store |
One answer across a store of documents, with per-document citations |
POST /v1/replay is served by cmd/engine. On this commit cmd/server records replay traces but does not register the route.
Per-request model keys (BYOK): X-LLM-Api-Key, with optional X-LLM-Provider, X-LLM-Base-Url and X-LLM-Model. Nothing about the key is logged or stored.
Clients for TypeScript (npm vectorless), Python (PyPI vectorless-sdk) and Go live in vectorless-sdk. vectorless-mcp exposes seven tools to agents: list, ingest, get and delete document, get_tree, get_section, and query.
A single Go binary with pluggable boundaries:
| Boundary | Implementations |
|---|---|
| Storage | Local filesystem · S3-compatible (R2, MinIO, B2, Spaces) · Google Cloud Storage |
| Queue | River (Postgres) · Asynq (Redis) · QStash (serverless) |
| Generative model | Anthropic · OpenAI · Gemini, and Anthropic-compatible gateways (GLM on z.ai), through llmgate |
| Judge | TypeSafe Jev through llmgate's Judge interface |
| Retrieval | judgewalk (the pipeline above) · treewalk (generative page walk) · auto (the config default) · single-pass · chunked-tree · agentic |
| Ingest mode | toc (the pipeline above) · minimal (parse and store, seconds) · full (adds the older generative per-section summaries and enrichment) |
single-pass, chunked-tree, agentic and the full ingest mode are the original design, in which a chat model summarised every section at ingest and navigated those summaries at query time. They still build and run, but the evaluations above did not use them. Design docs in docs/ predate the current pipeline in places and say so at the top.
Config layers, in increasing priority: --config <yaml>, then VLE_* (engine) or VLS_* (server) environment variables, then CLI flags. config.example.yaml is the full reference. The settings production ran with:
engine:
llm:
driver: anthropic # the answer model; key from VLE_LLM_ANTHROPIC_API_KEY
judge:
typesafe: {} # key from TYPESAFE_API_KEY
retrieval:
strategy: judgewalk
replay: { enabled: true }
ingest:
mode: tocAnthropic-compatible gateways (GLM on z.ai):
base_urlmust include/v1. The client posts to${base_url}/messages.
Windows and local storage: Windows Defender's real-time scan can briefly hide a freshly written file, which under heavy concurrent ingest shows up as transient
object not founderrors. The local backend retries through this, but for bulk loads add a Defender exclusion for your storage root:Add-MpPreference -ExclusionPath "C:\path\to\data\documents".
pdfgrab: the PDF layout parser (MIT)llmgate: provider clients, router, retries, budgets, and the Judge interface (Apache 2.0)vectorless-sdk: TypeScript, Python and Go clientsvectorless-bench: a harness that reports quality, cost, latency and determinism together



