Skip to main content
The Qdrant VFS exposes a Qdrant collection as a virtual filesystem mounted at some prefix such as /q/. Group-by payload fields become nested folders, each point becomes a .json payload file (plus a .txt text file and an optional blob), and semantic search is the search CLI, which returns ranked points as canonical file paths. For connection setup (self-hosted, Qdrant Cloud, search), see Qdrant Setup. Install the extra:

Config

The mapping is config-driven; nothing about the dataset is hardcoded. Point group_by at different payload fields and the folder tree changes. Fields marked secret are redacted in snapshots.

Document and chunk lineage

Config fields use Qdrant’s dotted nested-key syntax. For example, a LangChain-style point can be mounted by source document and named by page:
This produces refund-2026.pdf/004__<point-id>.txt. The readable prefix comes from name_field; the Qdrant point id remains as a stable suffix so duplicate page labels cannot collide and a direct read never depends on a warm listing cache. Only the stem the listing publishes opens: another label in front of the same id reads as absent. A label that is not a string spells as compact JSON (true, 1, 1e-7), the same in Python and TypeScript. basename_fields strips URL/path parents from the named group_by fields. Leave a field out to preserve its complete value in one path-safe segment: / renders as ∕, and a blank or dot-led value is led by ⁄, so every value has its own segment that lists and opens. A basename longer than 255 bytes, which ext4 and APFS refuse, is cut to fit and ends in __ plus the md5 of the whole name, so two long leaves stay two directories. Basenames must be unique within their parent group; Mirage refuses an ambiguous listing instead of silently merging two source documents, and opening a basename directory checks every point of its parent group, so a second source past max_rows is refused rather than hidden.

VFS.md

The VFS.md an agent reads with Qdrant mounted at /<path>.

Virtual Filesystem

/<path> layout

Supported Unix Commands for VFS

Unix commands work on each path as on a local filesystem, as far as the path supports them:

Limitations of Unix Commands for VFS

Where a path differs from a local filesystem:Run man <cmd> or <cmd> --help for one command’s usage.
Every path is translated into a Qdrant query. Descending a folder adds one payload filter; the leaf level lists points.
<id> is the Qdrant point id. When name_field is set, leaf stems instead use <name>__<id>. When collection is set the collection level is elided, so the mount root is that collection:

Point files

A point is shown as its underlying data in its original format, never as the embedding vector:
  • <id>.txt is the embedded source text (the text_field value), exactly what the vector was built from.
  • <id>.json is the full payload as compact JSON (the metadata), with the vector and the raw blob omitted.
  • <id>.<ext> is the raw blob bytes when blob_field is configured.
Search is the search CLI, not a path. Register it on the workspace:
It returns each ranked point as its canonical content path (the .txt, or .json when no text_field is set) annotated with the similarity score, followed by the content:
Flags: --top-k <n> (default search_limit), --threshold <min-score>, --method semantic (the only supported method; grep/rg stay lexical).

Search embedding

search needs a vector for the query, and the config decides where it comes from. With embed set, an async (text: str) -> list[float] the caller brings, the query is vectorized in-process, so a self-hosted Qdrant works and mirage depends on no model runtime; examples/python/vfs/qdrant/qdrant_vfs.py feeds it a dependency-free bag of words, and any model that returns a vector plugs in the same way. Without it, cloud_inference=True sends the query text to the server, so the cluster must have inference enabled (Qdrant Cloud) and store vectors from the same embedding_model (default sentence-transformers/all-MiniLM-L6-v2). With neither, search refuses the query. The hook never lands in a snapshot: a restored mount asks for a fresh VFS.

Supported commands

All commands delegate to Mirage’s shared implementations.

Integration

What Qdrant answers through its own API, so fewer files are read. Integration explains each command. A folder listing scrolls every point’s payload anyway, so grep -r and rg scroll each folder once more, as a listing does, and read only the .json and .txt files whose text holds the pattern’s text in any case. A point changed since the last listing is still found. A blob, a .json or .txt over 8 KiB, and every file for a text that is not ASCII are read as usual.

Access pattern

The mount is read-only (MountMode.READ); writes are not supported. The two read modes are:
  • Browse by payload folders: scroll filters on group_by fields, no embedding.
  • Search by meaning: the search CLI’s search "<query>" <path> runs vector search and returns canonical point paths.
Folder listings are capped by max_rows. A filtered listing scrolls first and only creates keyword payload indexes for the group_by fields if Qdrant reports one is required, so already-indexed collections work under read-only keys. Keep group_by to low-cardinality fields for large collections.