Embedded CLI
drsg is the command-line interface. It opens the database directly — it is
embedded, not a client of a running server — which makes it suited to local
scripting, bulk ingestion, inspection, backup, and operations. Every invocation
takes a global --db <path> (default graph.drsg); most commands also take a
--plane (default startup).
Because it opens the database in-process, drsg cannot operate on a database
that a drsg serve — or another drsg — currently holds open: the database
takes an exclusive lock, and a second open fails with a clear error rather than
corrupting it. Use the SDKs or the RPC API against the server for concurrent
access, and the CLI for offline operations.
Command reference
| Command | Purpose |
|---|---|
init [--dir] [--rebuild] | bootstrap this repository for agents: digest it, spawn serve watch detached, write .mcp.json (Chapter 10). In a --no-default-features build without digest, it creates an empty database instead — as most commands do on first use |
plane list | create | drop | show | plane lifecycle |
import <file> --plane | load JSONL nodes/edges |
export --plane | dump a plane as JSONL |
get <node> --plane | fetch one node by id or @external-key |
query <plan> --plane | run a serialized logical plan (JSON, or -) |
cypher <query> --plane | run an openCypher-subset statement (or -) |
catalog [--plane] | print the soft schema (one plane or the whole database) |
algo <name> --plane | run a graph algorithm |
hybrid <query> --plane | fused vector + keyword + graph-proximity search |
index ensure | keyword | declare a vector or keyword index |
ask <question> --plane | natural-language query |
digest <file|dir|url> --plane [--mode] | ingest a document, a repository, or a page and its links |
search <query> --plane | semantic lookup: embeds the query, returns the nearest nodes |
context | describe | trace | impact | fathom <name> --plane | the agent verbs over a digested code plane |
impact <name> --plane [--depth] [--across [attached|all]] | --across widens the radius past this repository: the planes this one attaches, or every plane this instance holds |
snippet <name | path:start-end> --plane | a symbol’s source text, or a range of any file in the parsed tree |
grep <pattern> --plane [--regex] [--path] [--all-files] | text search over that tree, ignored files excluded unless --all-files; each hit names the symbol it falls in |
traverse <key> --plane [--edge-type] | the neighbours a hop (or several) away |
history --plane [--limit] | the repository behind a plane: HEAD, branch and tag tips, rebases, newest commits |
deployed [<target>] --plane [--ops] [--history] | what commit a running thing was built from, and what runs a commit: the ops plane joined to the history beside it through the revision an image’s tag carries |
recall <name> --at <rev> --plane [--vs <rev>] [--pattern] | code as it was at a revision: a file, a range, a directory or a symbol; a search of that tree; or a diff from an older revision |
wire <path | name> --plane [--verb] | what serves an HTTP path and what asks for it, across every language in the plane and across the planes it attaches |
attach <name> --plane [--url] [--as] [--both] | record a plane this one may ask: one here, or one another drsg serves |
detach <name> --plane | forget an attachment (an absent name is reported, not an error) |
attachments --plane [--probe] | what a plane attaches; --probe asks each remote one whether it answers now |
mount <path> [--as] [--url] | serve another database’s planes on a running drsg, now — these three speak its RPC rather than opening a file, since the server holds the store’s lock |
unmount <name> [--url] | stop serving a mount, releasing its store’s lock — said when a fold in flight still holds it (an absent name is reported, not an error) |
mounts [--url] | what that server serves: each mount with its directory and planes, and every plane with the tree it records |
queries [<id>] [--limit] | the Cypher queries that have run; cypher --history <id> runs one again |
vectorize --plane | embed a plane’s nodes for similarity search |
plugin install | list | remove | manage preprocessor plugins (sandboxed wasm parsers) |
snapshot <out> / restore <in> | whole-database backup and restore |
stats / check | summary counts / integrity scan |
serve [--addr] | run the web dashboard + JSON-RPC API + MCP endpoint |
serve watch [--dir] | serve, and keep a code plane synced to every commit |
update [--bin] [--dir] | replace this binary with a newer release, if there is one |
A digested file may be Markdown or plain text, or any of Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, CSV and PDF — those are converted to Markdown first, so the model reads headings, tables and lists rather than loose characters. The format is detected from the file’s contents, so a wrong extension still works. A directory is walked and routed per file: source code goes through the installed parser plugins as facts, documents go to the model as prose.
Global options are --db <path> and --config <path> (the configuration file,
Chapter 2).
Planes
$ drsg --db graph.drsg plane create social
$ drsg --db graph.drsg plane list
$ drsg --db graph.drsg plane show social
$ drsg --db graph.drsg plane drop social
Data in and out
Import and export use JSONL — one node or edge per line — so a plane round-trips through the file system and integrates with other tooling:
$ drsg --db graph.drsg import nodes.jsonl --plane social
$ drsg --db graph.drsg export --plane social > social.jsonl
$ drsg --db graph.drsg get @ada --plane social
A node’s external_key is its identity within a plane, so --on-conflict
decides what happens when an imported key is already there:
| Policy | Effect |
|---|---|
error (default) | Import nothing and name the colliding keys |
skip | Keep the existing node; drop the incoming one |
update | Overwrite the existing node’s properties, and its labels when the line carries them |
$ drsg --db graph.drsg import nodes.jsonl --on-conflict update
imported 0 nodes, 0 edges, 2 existing updated
Under skip and update, edges in the file still resolve to the node already
in the plane, so relationships load against existing data. Edges themselves
carry no key, so they are always appended — the policy governs node identity,
not edge deduplication. Lines without an external_key cannot collide and are
always inserted.
Querying
A statement may be the openCypher subset or a serialized plan; either accepts -
to read from standard input. --param binds a $name placeholder to a JSON
value:
$ drsg --db graph.drsg cypher --plane social \
'MATCH (p:Person) WHERE p.age >= $min RETURN p' --param min=18
$ drsg --db graph.drsg query - --plane social < plan.json
$ drsg --db graph.drsg catalog --plane social
Every query that runs — from the CLI, the dashboard, or an agent over MCP — is
recorded. queries lists what has run, newest first, as id, time, plane and
the query folded onto one line; with an id it prints that one query’s text
alone. cypher --history runs a recorded query again, against the plane it was
written for unless --plane says otherwise:
$ drsg --db graph.drsg queries --limit 5
4 2026-09-05 08:14:02Z social MATCH (p:Person) WHERE p.age >= $min RETURN p
3 2026-09-05 08:11:47Z social MATCH (p:Person) RETURN count(*) AS people
$ drsg --db graph.drsg queries 3 > last.cypher
$ drsg --db graph.drsg cypher --history 3
The history is capped — two hundred entries by default — and the oldest is
dropped as a new one arrives. Re-running the query most recently recorded
restamps its entry rather than adding a second copy. Note that drsg history
is a different command: that one is a repository’s commits.
Graph algorithms
$ drsg --db graph.drsg algo pagerank --plane social --top 10
$ drsg --db graph.drsg algo components --plane social
$ drsg --db graph.drsg algo shortest-path --plane social --src 1 --dst 42
$ drsg --db graph.drsg algo louvain --plane social
Retrieval and ingestion
Declare indexes, run fused search, ask in natural language, and ingest documents (Chapter 3). The LLM-backed commands read provider keys from the environment:
$ drsg --db graph.drsg index ensure Doc embedding --plane social
$ drsg --db graph.drsg index keyword Doc body --plane social --lang english
$ drsg --db graph.drsg hybrid "how does time-travel work" \
--plane social --label Doc --vector embedding --keyword body --graph-hops 1
$ drsg --db graph.drsg ask "which companies does Ada work for?" \
--plane social --chat deepseek --embed qwen
$ drsg --db graph.drsg digest notes.md --plane social --apply
$ drsg --db graph.drsg digest paper.md --plane papers --mode super --apply
$ drsg --db graph.drsg digest https://example.com/paper --plane papers \
--topic "attention mechanism" --pages 6 --apply
An http(s):// argument is fetched rather than read from disk: the page is
converted to Markdown and its links followed under --pages / --depth, keeping
what is relevant to the page’s own subject and to --topic if given (Chapter
3). The scheme is required — a bare
example.com is a valid filename, and guessing which was meant would be worse
than asking. The command prints what it kept and what it dropped; there is no
selection prompt, which is what the dashboard’s page list is for.
--mode selects how thoroughly the extraction is cleaned up: coarse
reconciles the label and edge-type vocabularies, fine (the default) also
merges entities that name the same thing, and super also re-reads every entity
against all of its passages — the most accurate, at roughly 15× the input token
usage (Chapter 3).
Backup and integrity
snapshot writes a consistent whole-database bundle at one commit sequence;
restore rebuilds it into an empty database, preserving ids, the commit
sequence, and the built search indexes (Chapter 11).
stats and check report counts and scan every plane for readability:
$ drsg --db graph.drsg snapshot backup.drsgsnap
$ drsg --db fresh.drsg restore backup.drsgsnap
$ drsg --db graph.drsg stats
$ drsg --db graph.drsg check
check also names storage runs written before block checksums existed. A
compaction rewrites what it merges, so a store written to often upgrades
itself, but one that settles below the compaction threshold keeps unchecked
blocks for good — and corruption in them reads as data rather than an error.
upgrade rewrites those runs, entry for entry, so nothing is reclaimed and
the store holds what it held:
$ drsg --db graph.drsg upgrade
upgraded 2 storage runs to the checksummed format
Serving
serve is the exception to the embedded model: it opens the database and then
exposes it over the network for the dashboard, the SDKs, and the MCP server.
$ DRSG_TOKEN=please-change-me drsg --db graph.drsg serve --addr 0.0.0.0:7700
See Chapter 2 for the server and its configuration, Chapter 5 for the dashboard, and Chapter 6 for the clients.
One setting from that configuration is not the server’s alone: [server] retain_commits — how many commits of history time-travel can reach — is
applied by every command above that opens the database, so a store fed by
import or cypher from the shell keeps the same window (20 by default, 0
for unbounded) as it would under serve.