CLI reference
Every command, every query verb, and every flag.
langonrock --help
langonrock --versionRunning with no arguments prints the usage and exits 1. --help prints the same thing and exits 0.
Results go to stdout and everything else, statistics, diagnostics and status lines, goes to
stderr, so a result can always be piped. The database commands print one line of JSON.
Commands
| Command | What it does |
|---|---|
compile <dir> | Compile one bundle to a manifest on stdout, storing nothing |
import <dir> | Import a folder of bundles into a tenant, with conflict detection |
sync <dir> | The same command as import |
put <dir> | Import one directory as one bundle |
watch <dir> | Keep a tenant following a folder |
export <dir> | Write the current revision's exact Markdown to a new directory |
migrate <dir> | Move a legacy tenant into the engine, from its original sources |
manifest | Print the stored manifest |
get <id...> | Fetch concepts by id |
transact | Commit an atomic batch of document changes |
history | List retained revisions, newest first |
restore <rev> | Publish an older revision as a new commit |
verify [rev] | Check stored data, or one candidate revision |
repair <rev> | Make a verified revision the tenant's only retained root |
gc | Remove what retention no longer needs |
serve | Run the daemon |
token | Mint a token and record its grant |
query <dsn> … | Any read, source or database verb over any connection string |
mcp <dsn> | Serve MCP over stdio |
import is accepted although --help lists only its older name, sync.
compile
langonrock compile sources/acme/sales --bundle sales# bundle: sales
id kind status grain summary links
customers bigquery_table deprecated customer_id Registered customers, including churned. -
orders bigquery_table - order_id One row per completed customer order. customersWrites the manifest of one bundle to stdout and nothing to the store, so it is the fastest way to
see what the compiler makes of a folder. Diagnostics and the statistics line go to stderr. Without
--bundle, the directory name is used.
import, sync and put
langonrock import sources/acme --data ./data --tenant acme
langonrock put sources/acme/sales --data ./data --tenant acme --bundle salesimport, and sync under its older name, treats every immediate subdirectory as a bundle. put
imports one directory as one bundle, named by --bundle or after the directory. Each reports the
resulting snapshot on stderr:
snapshot cd9078b9af0b (new), 2 bundles [ops sales], 3 concepts, 1236 bytes on disk(reused) means the documents compiled to the snapshot that is already current; importing an
unchanged folder again commits nothing. The byte count is the size of the snapshot file.
An import remembers what it last read from each folder. A file that has not changed there since never overwrites a database edit, and a document changed on both sides fails the whole import, with nothing published:
folder and database both changed sales/tables/customers.md; reconcile the source hashes before importing--strict exits 1 when any diagnostic is reported, after the import has been committed. It changes
the exit code, not what is stored. A legacy tenant is refused until it is migrated:
legacy tenant: migrate with original sources before using database transactionswatch
langonrock watch sources/acme --data ./data --tenant acme --debounce 500Runs until interrupted. It prints watching sources/acme for tenant acme, then a snapshot line for
each import it runs, and reports a failed import, a conflict included, as an error: line without
stopping. Writes inside dot directories are ignored, because .git and .obsidian change
constantly and importing on them would never stop. A full rescan every 30 seconds backs up the
filesystem events. A legacy tenant is still compiled from its folder, as before.
export
langonrock export ./exported-acme --data ./data --tenant acme{"revision":"496744db3e3c76ce67cb8ba028b15fc6558ed3f3a5f3cd1b7827c628df0aab57","destination":"/home/me/exported-acme","files":3,"bytes":1115}Writes the current revision's documents to a new directory, exactly as stored, and refuses one that already exists:
export destination already exists; choose a new directory to preserve existing filesTo export an older state, restore it first.
migrate
langonrock migrate sources/acme --data ./data --tenant acme --dry-run
langonrock migrate sources/acme --data ./data --tenant acme{"alreadyMigrated":false,"dryRun":true,"snapshot":"cd9078b9af0b1d912a9e6e0c8c12d409be3aac3394085d56dc618458b5e23ad8","concepts":3}
{"alreadyMigrated":false,"dryRun":false,"snapshot":"cd9078b9af0b1d912a9e6e0c8c12d409be3aac3394085d56dc618458b5e23ad8","concepts":3,"revision":"bb87d48285cf586b1a4c9eee72e92e84a7427af22a001ec1406a1a810cfeb1df"}Moves a tenant created before the engine existed into it, using the original Markdown folder. The
dry run checks that the migrated tenant would compile to exactly the read model the legacy one
serves, without publishing anything. Running it again after a migration reports
"alreadyMigrated":true. Pass --bundle for a single-bundle source directory, and the
--summary-width the legacy tenant was compiled with. See Migrating a legacy
tenant.
manifest and get
langonrock manifest --data ./data --tenant acme
langonrock get orders customers --section schema --data ./data --tenant acmeget prints each concept prefixed by @@ <id>. It warns on stderr for any id it could not find,
warn: no such concept or section: <id>, and exits 1 when at least one is missing.
A partial read, from --offset, --limit or a --find window, frames itself instead:
langonrock get orders --find "customer_id" --limit 120 --data ./data --tenant acme@@ orders [158..278 of 476] 2 matches at 212 462--find is a literal, case-insensitive phrase. The output is a window around the first occurrence
and the offset of every one, which is how you jump to the others with --offset.
Both read the store directly. Use query to go through a daemon or a server.
transact, history and restore
langonrock transact --data ./data --tenant acme --from batch.json
langonrock history --data ./data --tenant acme --limit 20
langonrock restore "$REVISION" --expected-revision "$CURRENT" --data ./data --tenant acmetransact reads a JSON batch from --from, or from stdin without it, and prints the commit:
{"revision":"984e1dcb03ea2403acd84528949187909ef0e327504f03d92e00b199ba9dc9c9","snapshot":"0203add32834701b39b60d1b330ad88349a42c93e52bf29d0a660961372098ff","concepts":4,"bundles":["ops","sales"],"diagnostics":[]}A batch whose preconditions fail publishes nothing and exits 1 with the reason, such as
concept already exists: re-read the concept and retry with its new hash.
history prints a page of revisions, newest first: 20 by default, --limit up to 100. When older
revisions remain, the page carries a next cursor; pass it back with --before to continue.
restore needs the revision to restore and --expected-revision, the revision you believe is
current. If anything was committed since, it is refused with
database changed: re-read the revision and document hashes. See
Transactions and History and restore.
verify and repair
langonrock verify --data ./data --tenant acme{"ok":true,"headHash":"6e8800ac883efc977ac044808be4286b9e45f060cb24b54c1f1d776e3ce69f4b","verifiedRevisions":["496744db3e3c76ce67cb8ba028b15fc6558ed3f3a5f3cd1b7827c628df0aab57","984e1dcb03ea2403acd84528949187909ef0e327504f03d92e00b199ba9dc9c9","7a4b86c1a08808561bab370254e5ce68157797da2e56326a3406a4b6b586075c"],"issues":[]}verify reads the stored data without the ordinary caches and exits 1 when ok is false. Given a
revision, it checks that one candidate instead.
langonrock repair "$REVISION" --expected-head "$HEAD_HASH" --data ./data --tenant acmerepair makes a verified revision the tenant's only retained root. --expected-head is the
headHash that verify reported, or missing when there is no HEAD; the old HEAD is kept
under recovery/. It never runs on its own. See Durability and failures.
gc
langonrock gc --data ./data --tenant acme --keep 2 --dry-runacme: would remove 1 snapshot and 0 partials, kept 2, freed 421 bytesKeeps the newest ten committed revisions by default, and removes every file nothing retained
refers to: snapshots, source archives, revision records and import maps, all counted as
snapshots in that line, plus abandoned staging files, the partials. Without --tenant it
collects every tenant. On a legacy tenant it counts snapshots, honours --grace, and exits 1 when
current points at a snapshot that is missing or truncated. See Collection and
backups.
serve
langonrock serve --data ./data --socket /tmp/okf.sock
langonrock serve --data ./data --host 127.0.0.1 --port 7777langonrock serving ./data on /tmp/okf.sock (0 tokens, 0 writable tenants)Without --socket, --host or --port, the daemon listens on <data>/langonrock.sock. The last
count in the banner is the number of folders it watches from sources.json, not the number of
tenants it will write. The daemon commits writes to any database tenant a request is allowed to
write: by its token's grant, or, with no tokens at all, by anyone who can reach the socket. See
Running a server.
token
langonrock token --data ./data --tenant acme # read-only
langonrock token --data ./data --tenant acme --write # may writeThe token goes to stdout so you can capture it into a variable. Everything else goes to stderr. A
running server reads tokens.json only at startup, so restart it before a new token works. See
Tokens and TLS.
mcp
langonrock mcp "okf:///abs/path/to/data?tenant=acme"
langonrock mcp "okf:///abs/path/to/data?tenant=acme" --database-toolsSpeaks MCP over stdio against any connection string, with six tools by default and
transact, history and restore added by --database-tools. The transport owns stdout, so
diagnostics go to stderr only. See MCP server.
query
langonrock query <dsn> <verb> [args]One command against any connection mode, so a local store and a remote server are driven the same way.
Read verbs
| Verb | Output |
|---|---|
manifest | The manifest, or one bundle with --bundle |
snapshot | The current snapshot digest |
search <words...> | Ranked manifest rows plus a pos column; --k and --bundle apply |
get <id...> | Concepts; --section, --offset, --limit and --find apply |
Each direct search hit ends in pos, the offset of the passage densest in the query's words.
Follow it with get <id> --offset <pos> --limit 2000 to read the passage instead of the document.
Linked rows, and hits whose words appear only in the manifest row, show -.
langonrock query "okf+unix:///tmp/okf.sock?tenant=acme" search order grain --k 5
langonrock query "okf+http://127.0.0.1:7777?token=$TOKEN" manifest --bundle salesSource verbs
| Verb | What it does |
|---|---|
source | List documents as hash, bytes and bundle/path |
read <bundle> <path> | Print one document, its hash on stderr |
write <bundle> <path> | Commit one document, from stdin or --from |
delete <bundle> <path> | Commit the removal of one document |
delete-bundle <bundle> | Commit the removal of a whole bundle |
sync | Report the current commit; recompile a legacy tenant's sources |
langonrock query "$DSN" source
langonrock query "$DSN" read sales tables/orders.md
langonrock query "$DSN" write sales metrics/new.md --create < new.md
langonrock query "$DSN" write sales tables/orders.md --replaces "$HASH" < edited.md
langonrock query "$DSN" delete sales tables/old.md --force
langonrock query "$DSN" syncsource prints one line per document, the hash first:
b8475eb207b4239c0565fe75e0e95cd3d79732569a3c4ee4804e00452cd57f97 212 ops/runbooks/deploy.md
dce62d2e17f8bbf981c4e8bb419ab98d6db77fe322cf9b9ecc6dae2d90c41632 317 sales/tables/customers.md
3b3807a007b08a92991718ba790e3ff312793710a8d9ea141c7f19633deb5750 586 sales/tables/orders.mdEach write, delete and delete-bundle is its own commit, visible to the next read. write
confirms on stderr with the new hash:
wrote sales/metrics/new.md b8475eb207b4239c0565fe75e0e95cd3d79732569a3c4ee4804e00452cd57f97sync on a database tenant only reports the current state, without creating a revision:
snapshot f55e4feb8df8, 2 bundles, 4 conceptsOn a legacy tenant whose folder is registered in sources.json, the source verbs edit that folder
instead, and sync recompiles it.
A change that names no version is refused before anything is read, and one that names a stale version is refused by the database:
a write needs --replaces <hash> to update, --create for a new concept, or --force to overwrite whatever is there
concept changed since it was read: re-read the concept and retry with its new hashA write must name the version it replaces
--create, --replaces <hash> and --force are the command-line spelling of the same
precondition, and there is no default. A write or delete with none of them is refused, and so
is a stale hash, each with a message that says what to do next. --force reads the current hash
and writes against it, which is the honest name for taking whatever is there right now. See
Editing over the network.
Database verbs
| Verb | What it does |
|---|---|
transact | Commit a JSON batch from stdin or --from |
history | A page of revisions; --limit and --before apply |
restore <rev> | Restore with --expected-revision |
They print the same JSON as the local commands, through any connection, so a remote tenant is committed to, paged and restored exactly like a local one.
Flags
Store and tenant
| Flag | Default |
|---|---|
--data <dir> | $LANGONROCK_DATA, else the platform data directory |
--tenant <id> | required by most commands, up to 64 characters |
Reading
| Flag | Default | Applies to |
|---|---|---|
--section <name> | none | get |
--offset <n> | 0 | get |
--limit <n> | none for get, 20 for history | get, history |
--find <text> | none | get |
--k <n> | 8 | search |
--bundle <name> | the directory name for compile and put, no filter elsewhere | compile, put, migrate, and query's manifest and search |
--out <file> | stdout | anything that prints a result |
Compiling
| Flag | Default | Effect |
|---|---|---|
--summary-width <n> | 120 | Maximum characters per summary cell |
--strict | off | Exit non-zero when any diagnostic is reported |
Database
| Flag | Effect |
|---|---|
--from <file> | The batch for transact, or the content for write |
--before <cursor> | Continue a history page from its next cursor |
--expected-revision <revision> | restore only if this revision is still current |
--expected-head <hash|missing> | repair only if HEAD still has the hash verify reported |
--dry-run | Validate a migrate, or preview a gc, without publishing |
Serving
| Flag | Default |
|---|---|
--socket <path> | <data>/langonrock.sock |
--host <name> | 127.0.0.1 when TCP |
--port <n> | 7777 |
--tls-cert <file> | none, required off loopback |
--tls-key <file> | none, required with --tls-cert |
--watch <dir> | none, also import this folder into --tenant |
--debounce <ms> | 200 |
--rescan <ms> | 30000 |
--tls-cert and --tls-key must be given together, and neither works with a unix socket.
Writing source
| Flag | Effect |
|---|---|
--from <file> | Read the content from a file instead of stdin |
--replaces <hash> | The hash this change is based on |
--create | The document must not exist yet |
--force | Use whatever hash is there right now |
Tokens and MCP
| Flag | Effect |
|---|---|
--write | The token may write |
--database-tools | mcp also exposes transact, history, restore |
Collection
| Flag | Default | Effect |
|---|---|---|
--keep <n> | 10 | Revisions to retain per tenant; snapshots on a legacy one |
--grace <ms> | 3600000 | Legacy tenants only: never collect anything newer |
--dry-run | off | Report what would be removed |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | An error, with its message on stderr; a missing concept in get; ok: false from verify; a corrupt legacy current in gc; any diagnostic under --strict |