CLI reference
Engrava ships an engrava command-line tool for inspecting, querying, and
maintaining a database without writing code. This page documents every command
and option.
engrava [GLOBAL OPTIONS] COMMAND [ARGS]...
Global options
These apply to every command and go before the command name:
| Option | Values / type | Default | Description |
|---|---|---|---|
--db |
path | ./engrava.db |
Path to the SQLite database. Falls back to the ENGRAVA_DB env var, then the default. |
--config |
path | — | Path to engrava.yaml. Falls back to the ENGRAVA_CONFIG env var. |
--format |
table | json | csv |
table |
Output format for commands that print records. |
--verbose |
flag | off | Emit DEBUG logs from Engrava modules to stderr for this invocation. Command data on stdout keeps its selected format. |
--no-extensions |
flag | off | Prevent loading both engrava.cli and engrava.extensions entry points. ENGRAVA_DISABLE_EXTENSIONS=1 provides the same control. |
--help |
flag | — | Show help and exit (works on the root and on every command). |
Environment variables. ENGRAVA_DB and ENGRAVA_CONFIG are CLI fallbacks for
--db and --config respectively; the explicit flag always wins
(--db > ENGRAVA_DB > ./engrava.db).
ENGRAVA_DISABLE_EXTENSIONS=1 activates --no-extensions before command
resolution, including for root help and built-in commands.
export ENGRAVA_DB=/data/engrava.db
engrava info # uses /data/engrava.db
engrava --db other.db info # flag overrides the env var
Commands
| Command | Purpose |
|---|---|
info |
Show a metrics snapshot for the database. |
verify |
Verify the audit journal’s hash chain. |
query |
Run a MindQL query. |
remember |
Store a thought in one call and print its id. |
recall |
Search for thoughts relevant to a query and print ranked results. |
link |
Create a typed edge between two existing thoughts. |
snapshot |
Export the database’s core tables to a JSONL snapshot. |
restore |
Restore a database from a JSONL snapshot. |
gc |
Garbage-collect archived thoughts (and optionally expired ones). |
migrate |
Run pending schema migrations. |
export |
Export thoughts to a portable JSON file. |
Extension discovery
The executable discovers installed CLI extensions lazily. Built-in commands are
resolved without scanning engrava.cli. Root help scans that entry-point group
so installed commands appear in the command list, and an otherwise unknown
command triggers the scan so an installed extension command can resolve.
The built-in query command performs a second scan of
engrava.extensions. It loads each discovered manifest and registers its
mindql_extensions for that query. This query-time scan does not apply manifest
schema migrations.
Both scans import and may execute code from installed packages. Failed entry
points are skipped with a warning log. Put --no-extensions before the command,
or set ENGRAVA_DISABLE_EXTENSIONS=1, to prevent both entry-point groups from
being scanned or loaded:
engrava --no-extensions --help
ENGRAVA_DISABLE_EXTENSIONS=1 engrava --db memory.db info
engrava --no-extensions --db memory.db query "SELECT thought_id FROM thought LIMIT 5"
This control is specific to automatic CLI entry-point loading. It does not
disable explicit manifest paths or library-side manifests.discover settings
used by an application-created store. Continue to install only trusted
packages; see
Security and Trust Boundaries
for the full threat model.
--verbose enables DEBUG logging for the engrava logger hierarchy for the
duration of the command. Logs go to stderr, leaving JSON/CSV/table command data
on stdout.
Service resolution
The --service option on snapshot and restore resolves to the same
directory in both commands; whether the target database must already exist
does not:
--service |
Services config loaded? | Result |
|---|---|---|
--service NAME (explicit) |
either | Targets service NAME in the services data_dir if a config is loaded, otherwise in the parent directory of --db (i.e. <parent-of-db>/NAME.db). restore creates that database there if it does not already exist; snapshot requires it to already exist and exits if it is not found. |
| omitted | yes | Falls back to services.default_service. |
| omitted | no | Operates on the single --db database (not service mode). |
In short: an explicit --service works even without a services config (using
--db’s directory as the data directory), while omitting it only enters
multi-service mode when a services config is present.
Schema-version checks
info, verify, query, snapshot, restore, gc, export, recall,
remember, and link check the database’s schema version before they act,
and the kinds of command are held to different rules:
| Kind | Commands | Schema below head | Schema above head |
|---|---|---|---|
| Destructive | gc, restore |
Refuses, exit 1, names engrava migrate |
Refuses, exit 1 |
| Read | info, verify, export, snapshot, recall (with --db or the default path), a query parsing as FIND/COUNT/SELECT |
Warns on stderr, then runs anyway | Refuses, exit 1 |
Under --config, recall refuses on a schema below head as well as above it,
because a configured store applies pending core migrations as it opens and a
read must not. Once the core schema is at head, a recall whose target
resolves from --config also applies pending migrations for the extensions
listed under manifests.
remember and link do not migrate the core schema of a database that
already exists. Below head they refuse, exit 1, naming engrava migrate;
above head they refuse the way the commands above do. A path that does not
exist yet is created at head, and so is an empty file (no bytes, no schema).
A destructive command refuses below head because deleting rows through an
engine that does not understand the schema it is deleting from is how a
deleted thought’s vector came back on an unmigrated 0.6 database (see Known
Limitations).
Apart from recall under --config, a read command runs anyway below head,
but is never silent about the gap. query is classified by the parsed
command, not by the fact that it was typed as query: a registered extension
command that writes is refused like a destructive one. migrate sits outside
every row — running (or refusing) pending migrations is its entire job.
A refusal is a plain message on stderr, exit 1, and nothing on stdout —
also under --json. It is not an engrava.cli.error.v1 object.
$ engrava --db old.db gc
Database schema is at version 11; this engrava build's head version is 21. Run 'engrava migrate' before running 'gc' on it.
$ echo $?
1
Unreadable or corrupt databases
A database file that exists but is not a valid SQLite database (truncated,
corrupted, or a plain text file) now makes every built-in command exit 1 with a
message naming the configured path, instead of hanging or raising a raw
traceback — except snapshot --service / restore --service, which resolve
a database through EngravaManager instead of a single file, so the message
names no path:
$ engrava --db corrupt.sqlite info
info: corrupt.sqlite: unexpected DatabaseError: file is not a database
$ echo $?
1
Rerun with --verbose to log the caught exception’s stack (frame filename,
line, and function only, not a full traceback) at DEBUG for a bug report.
Command Reference
info
Shows a metrics snapshot (counts, etc.) for the current database. Takes no command-specific options.
engrava --db engrava.db info
--format json info reports two separate version numbers: metrics_schema_version
(the shape of the EngravaMetrics object itself) and database_schema_version
(the database’s own schema version, the one Schema-version
checks above gates on) — replacing the single
schema_version key a parser built against an earlier release may still read.
Use this after an upgrade or a restore to confirm the database is readable and the counts look right.
verify
Verifies the audit journal’s hash chain. It walks
every recorded journal_entry in sequence order, recomputes each SHA-256 hash,
and checks the parent-hash linkage. Takes no command-specific options. The
chain is verified regardless of whether journaling is currently enabled, so
a journal recorded in an earlier session is still auditable.
engrava --db engrava.db verify
# Journal integrity OK — 128 entries verified.
engrava --db engrava.db --format json verify
# {"valid": true, "entries_checked": 128, ...}
The exit code is 0 when the chain verifies, 1 when it does not (the
text output names the first broken sequence, and the JSON output carries
first_invalid_sequence / error_message) or when the database is missing —
so it drops straight into a CI job, a pre-backup hook, or a monitoring check. An
empty or absent journal verifies as valid with entries_checked: 0.
Read the Security model
first: this is a keyless in-file chain, so it detects covered-field changes and
broken linkage among retained rows — including an unrecomputed middle deletion
or reordering — not deletion of a self-consistent suffix, or a chain-aware
actor who rewrites the whole .db.
query
Executes a MindQL query and prints the results in the
chosen --format.
engrava query "MQL"
The MQL string is a positional argument. It accepts FIND, COUNT, SELECT,
or registered extension commands:
engrava query "FIND thoughts WHERE lifecycle_status = 'ACTIVE'"
engrava query "COUNT thoughts WHERE priority = 'P1'"
engrava --format json query "SELECT thought_id, essence FROM thought LIMIT 5"
engrava query "FIND thoughts WHERE valid_now" # only currently-valid facts
The bi-temporal valid_now, valid_at, valid_within, and valid_between
predicates work here too — see MindQL for their full
semantics.
Store resolution for remember, recall, and link
remember, recall, and link are one-shot verbs: store or search a
thought, or create an edge, in a single invocation, without writing a Python
script. None of the three takes a --service option, so they never read
services.default_service — that config section is consulted only by
snapshot / restore. Instead each resolves the
database it acts on through its own two-tier precedence, highest first:
- A non-empty
--db(orENGRAVA_DB). This wins outright — a--configfile is never even read. - Otherwise, a
--configfile’s owndatabase.path, loaded the same waySqliteEngravaCore.from_config()loads it, so a configuredembeddings,search, andjournalsection all apply. A named--configfile that does not exist or fails to parse is always an error (exit2), never silently skipped. - Otherwise, the CLI’s own default (
./engrava.db).
--verbose reports which tier fired and the resolved path.
Exit codes, consistent across all three commands: 0 success; 1 a
database refused for its schema version (see Schema-version
checks), or an unanticipated failure (a corrupt
database, an unreadable --config, …); 2
a usage or validation error (an unknown edge type, empty text, a malformed
--meta / --filter token, an out-of-range --top-k / --weight, or an
invalid --config); 3 the resolved database does not exist (recall
only — remember / link create it instead); 4 (link only) a FROM or
TO thought id that does not reference an existing thought.
--json on any of the three emits one JSON object instead of a bare id or a
formatted table: to stdout on success (schema engrava.cli.<command>.v1), or
to stderr on a failure the command body itself catches (schema always
engrava.cli.error.v1, with an error kind and message). A failure Click’s
own argument parser catches first — an invalid --type choice, a missing
required argument — prints plain usage text and exits 2 regardless of
--json, since that happens before the command body (and --json itself)
is ever read. A schema-version refusal is not covered either: it is a plain
message on stderr (see Schema-version checks). A
close failure after an otherwise-successful body still emits a second, error
object to stderr and exits 1, so a caller should treat only the first object
on each stream as authoritative.
remember
Stores TEXT as a thought and prints its id. Built over create_thought()
with an explicitly constructed ThoughtRecord — not the library’s
remember() shorthand, which always produces a NOTE / P3 thought and so
cannot honour --type / --priority. Creates the resolved database (and its
parent directory) if it does not already exist, printing the path to stderr.
The core schema of a database that already exists is never migrated by
remember or link; see Schema-version checks.
| Option | Type | Default | Description |
|---|---|---|---|
TEXT |
positional | required | Content to store. - reads it from stdin instead. |
--type |
thought type | NOTE |
One of TASK, OBSERVATION, BELIEF, REFLECTION, OUTPUT_DRAFT, NOTE. |
--priority |
priority | P3 |
One of P1, P2, P3, P4 (P1 highest). |
--meta |
KEY=VALUE |
— | Metadata entry (repeatable). |
--dedup |
flag | off | On matching content_hash with an existing row, bump confirmation_count and print that row’s id instead of inserting a duplicate — editing a thought’s content does not recompute its hash, so this can match content the row no longer holds. |
--json |
flag | off | Emit a JSON object instead of a bare id. |
engrava --db my.db remember "User prefers concise answers"
engrava --db my.db remember "Escalate the outage" --type REFLECTION --priority P1
echo "piped content" | engrava --db my.db remember -
engrava --db my.db remember "same content twice" --dedup # run again: same id, no new row
{"schema": "engrava.cli.remember.v1", "thought_id": "091aa106-fcc0-45a3-a19b-d335ad05ea45", "deduplicated": false}
deduplicated is true only when --dedup was given and it matched an
existing thought — the printed thought_id is then that existing thought’s,
not a new row’s.
recall
Searches for thoughts relevant to QUERY and prints ranked results. Calls
the library recall() directly, so a configured embedding provider and
hybrid-search weights apply exactly as they would to a direct library call —
unlike a bare connection, which has no embedding provider and so no vector
arm. This is not a substitute for query: query runs structural
MindQL with no ranking and no embedding provider of its own. Unlike
remember / link, recall never creates a database — against one that
does not exist, it exits 3 naming the resolved path rather than reporting
zero hits.
| Option | Type | Default | Description |
|---|---|---|---|
QUERY |
positional | required | Natural-language text to search for. |
--top-k |
int | 10 |
Maximum results to return. |
--filter |
KEY=VALUE |
— | KEY is a metadata filter path (a dotted key such as a.b addresses a nested field); equality only; repeatable — different keys are AND-combined, a repeated key keeps its last value. |
--json |
flag | off | Emit a JSON object instead of a formatted table. |
engrava --db my.db recall "concise answers"
engrava --db my.db recall "tagged" --filter topic=weather --top-k 5
Three checks run in this order. Token-shape checks run before the database is
resolved: a token missing =, or with an empty KEY, is malformed_filter,
exit 2. Next, a database that does not exist exits 3. Once the database
exists, a non-empty KEY that is not a valid metadata filter path is
malformed_filter, exit 2 too, with the offending KEY=VALUE token named in
the message.
{"schema": "engrava.cli.recall.v1", "query": "concise answers", "top_k": 10, "backends_used": ["fts5", "priority", "vector"], "results": [{"thought_id": "091aa106-fcc0-45a3-a19b-d335ad05ea45", "score": 0.4714285714285715, "essence": "User prefers concise answers"}]}
backends_used reports FTS5, vector, and recency when those signals are
active, even if they score no candidates; priority is reported only when
candidates exist, and graph-derived entries only when they produce boosts or
additions. "vector" appears only when a configured embedding provider
produced a query vector.
link
Creates a typed edge from FROM to TO and prints its id. Builds an
EdgeRecord and calls the public create_edge(). Creates the resolved
database (and its parent directory) if it does not already exist.
| Option | Type | Default | Description |
|---|---|---|---|
FROM |
positional | required | Source thought id. |
TO |
positional | required | Target thought id. |
--type |
edge type | required | One of ASSOCIATED, DEPENDS_ON, DERIVED_FROM, MESSAGE_OF, BRIDGE, CONSOLIDATED_FROM, CONTESTED_BY. |
--weight |
float | 1.0 |
Relation strength, 0.0-1.0. |
--json |
flag | off | Emit a JSON object instead of a bare id. |
engrava --db my.db link 091aa106-fcc0-45a3-a19b-d335ad05ea45 f4620859-3dfa-4f13-8d2e-d35df62dbba4 --type ASSOCIATED --weight 0.8
An unknown --type exits 2 naming the value and every valid member; a
FROM or TO that does not resolve to an existing thought exits 4, naming
which one:
$ engrava --db my.db link ghost-id some-real-id --type ASSOCIATED
link: from_thought_id 'ghost-id' does not reference an existing thought.
$ echo $?
4
{"schema": "engrava.cli.link.v1", "edge_id": "b190dc41-9c87-4a66-9291-d70974fd2342", "from_thought_id": "091aa106-fcc0-45a3-a19b-d335ad05ea45", "to_thought_id": "f4620859-3dfa-4f13-8d2e-d35df62dbba4", "edge_type": "ASSOCIATED", "weight": 0.8}
snapshot
Exports the database’s core tables to a JSONL snapshot (one record per line) — see below for exactly what a snapshot includes and excludes.
| Option | Type | Default | Description |
|---|---|---|---|
-o, --output |
path | derived (see below) | Output JSONL file path. |
--service |
name | see below | The service to snapshot (multi-service mode only). |
Default output path depends on the mode:
- Single database:
<db-stem>.snapshot.jsonlnext to the database — e.g.--db engrava.db→engrava.snapshot.jsonl(the.dbsuffix is replaced). - Multi-service:
<data_dir>/<service>.snapshot.jsonl.
--service resolves in three ways (see Service resolution):
- Explicit
--service NAMEtargets that service even with no services config — the service database must already exist in the data directory, which is the services config’sdata_dirif one is loaded, otherwise the parent directory of--db;snapshotexits if it is not found there. - Omitted, with a services config loaded → falls back to
services.default_service. - Omitted, with no services config → snapshots the single
--dbdatabase.
engrava --db engrava.db snapshot -o backup.jsonl
engrava --db engrava.db snapshot # -> engrava.snapshot.jsonl
engrava --db /data/engrava.db snapshot --service tenant_a # -> /data/tenant_a.snapshot.jsonl
engrava --config engrava.yaml snapshot --service tenant_a # data_dir from config
A snapshot exports every column of the
thought,edge,embedding, andactionrecords — including the bi-temporalvalid_from/valid_untilfields — but not the audit journal (journal_entry) or the extension-manifest table (extension_schema_versions). See Backup & Recovery for what this means and when to use a physical file backup instead.
Failure safety.
snapshotwrites to a temporary file next to-oand replaces it only once every row is read and the export’s own read transaction has closed, so an existing file at-ois left byte-identical on an exception or a cancelled run rather than replaced by a truncated one.-omay not name the database currently open for--db, or that database’s-wal/-shmcompanion files — pointing it there is refused before anything is written.
restore
Restores a database from a JSONL snapshot produced by snapshot.
| Option | Type | Default | Description |
|---|---|---|---|
-i, --input |
path | required | JSONL snapshot file to restore. |
--clear |
flag | off | Replace the four core snapshot tables and the audit journal (and reset a persisted sqlite-vec index) before restoring — not the whole target database. |
--clear-identity |
flag | off | Also clear the target’s stored embedding identity (model name, dimension, prefix metadata). Requires --clear; recovers a target whose stored embedding_dimension is corrupt — --clear alone preserves an existing identity, corrupt or not. |
--skip-embeddings |
flag | off | Import without embedding records. |
--re-embed |
flag | off | Re-embed all thoughts via the target provider, ignoring source embeddings. Requires --config with top-level or per-service embeddings. |
--orphan-journal-entries |
flag | off | Allow a merge restore (no --clear) to replace a row that collides with the target’s journal, even though the journal will then describe data the merge discarded. Without it, such a collision is refused. |
--service |
name | see below | The service to restore into. |
For any restore --clear, an existing sqlite-vec table is dropped in the same
transaction as the canonical rows. The next sqlite-vec-enabled open recreates
and backfills it. Because SQLite must load the virtual-table module before it can
remove that table safely, install engrava[vec] before clearing a database that
already contains a persisted sqlite-vec index.
--service resolves exactly as for snapshot: an explicit
--service NAME targets that service even without a services config (its database
resolves in the services data_dir, or the parent directory of --db when no
config is loaded); omitted with a services config falls back to
services.default_service; omitted with no services config restores into the
single --db database.
Thought and edge timestamps are written in the canonical UTC form Engrava stores them in (a value without an offset is read as UTC), so a snapshot taken by an earlier version, which may hold another ISO-8601 form, restores with its timestamps compared by instant. A value that cannot be read as an ISO-8601 instant is restored as it was.
--skip-embeddings and --re-embed are mutually exclusive — passing both
fails with:
Error: --re-embed and --skip-embeddings are mutually exclusive.
Use --re-embed when the target should use a different embedding model than the
snapshot. In single-database mode, the provider comes from the top-level
embeddings section of the file passed with --config. In service mode,
services.configs.<name>.embeddings takes precedence; when no override exists,
the same top-level embeddings section is the fallback. Restore discards source
embedding rows and generates a replacement vector with the resolved provider
for each restored thought. A target that already contains embeddings requires
--clear; without it, restore refuses to relabel vectors that are not part of
the snapshot. The sqlite-vec reset described above prevents stale index rows
from surviving the re-embed.
If neither level declares a provider, restore fails before importing records and
names the missing configuration. An explicit --service without a config-backed
provider fails for the same reason. Use --skip-embeddings to import text
without vectors, or restore normally to retain the snapshot vectors.
engrava --db fresh.db restore -i backup.jsonl
engrava --db fresh.db restore -i backup.jsonl --clear --skip-embeddings
engrava --db fresh.db --config engrava.yaml restore -i backup.jsonl --clear --re-embed
engrava --config engrava.yaml restore -i backup.jsonl --service main --clear --re-embed
When services.default_service names the configured target, --service main
may be omitted. A restore that reaches its insert phase, whether or not
--re-embed or --skip-embeddings is given, first checks that whatever the
target holds at that point in the restore is internally consistent: its
stored embedding-model lock, if one survives (a --clear restore preserves the
lock even though it wipes the rows), or its existing embedding rows when
there is no lock and
nothing was cleared. A dreaming-centroid row is compared on dimension
only, never on model_name. A normal restore (neither flag given)
additionally checks every embedding row the snapshot declares against that
same reference before inserting it, with the same centroid exemption — never
against a configured provider, which a plain restore never resolves. On a
mismatch, restore fails before committing anything, naming both identities;
choose --re-embed or --skip-embeddings as above. Restoring into a target
that, when the inserts begin, holds neither a stored model nor any non-centroid
embeddings writes the identity established by the snapshot’s own non-centroid
embedding rows as the target’s new lock — nothing is adopted if the snapshot
inserts no non-centroid embedding row. This check cannot
see the document or query prefix the corpus was built with — see
Embeddings → Asymmetric prefixes for instruction-tuned
models.
Restore recreates thoughts, edges, embeddings, and actions, not the audit journal. A fresh target starts with an empty journal, and
--clearempties the journal along with the data it wipes. A restore without--clearmerges into the target instead — and if that target’s journal is non-empty, it refuses any record that collides with an existing row (primary key orUNIQUEconstraint) and rolls the whole restore back, rather than letting the merge silently replace it.--orphan-journal-entriesallows the merge anyway, restoring the pre-gate behaviour where a collision can orphan journal entries andverifystill reports the chain as valid. The gate never fires against an empty journal — the ordinary case, since journaling is opt-in. See Audit Trail and Backup & Recovery for the full mechanism.
gc
Garbage-collects ARCHIVED thoughts together with every edge touching one on
either end — including edges whose other end is still live — their embeddings and
the actions sourced from them, then reconciles the vector index by removing every
vec0 row whose embedding row is missing or no longer joins to a live thought.
With --expired it also runs the TTL expiry cleanup first.
gckeeps pinned and protected-priority rows by default. AnARCHIVEDthought that is pinned, or at a priority in the hygiene policy’s protected set (P1unless--confignames anengrava.yamlwhosehygiene_policy:setsprotected_priorities), survivesgc, and the run reports how many it kept. Pass--include-pinnedto delete them too. The TTL pass ofgc --expiredfollows its own rule: it never expires a pinned row, whatever the flag, but a protected-priority row whose TTL has passed still expires — see Data Lifecycle → Time-to-live. When--confignames anengrava.yamlwithjournal.enabled: true, each rowgcdeletes gets aDELETE_THOUGHTjournal entry in the same transaction; without it, nothing is journaled.
gcrefuses on a database that is not on this build’s head schema. If--dbis below or above head,gcdeletes nothing and exits1namingengrava migrate— see Schema-version checks above. If agcthat used to run cleanly now exits non-zero, that is this refusal working as intended, not a regression in what it collects: runengrava migratefirst.
| Option | Type | Default | Description |
|---|---|---|---|
--dry-run |
flag | off | Preview without changing anything; under the archive strategy the preview can list more than the real run does — see below. |
--expired |
flag | off | Also run expiry cleanup (archive or delete per ttl.strategy) before collecting. |
--include-pinned |
flag | off | Also delete pinned and protected-priority archived thoughts, which are kept otherwise. |
engrava --db engrava.db gc # delete ARCHIVED thoughts + their edges/embeddings/actions
engrava --db engrava.db gc --include-pinned # also delete pinned / protected-priority rows
engrava --db engrava.db gc --expired # run expiry cleanup first (per strategy)
engrava --db engrava.db gc --expired --dry-run
The behaviour of gc --expired depends on ttl.strategy: with delete it
removes expired rows and then collects pre-existing archived rows; with the
default archive it archives the expired rows and stops only if it archived at
least one — collecting the rows it just archived would defeat the soft-retire.
With no expired rows to archive it falls through to collecting pre-existing
ARCHIVED rows. See Data Lifecycle for how that
cleanup pass runs — including how --dry-run can preview both stages at once
under the archive strategy, which a real run under the same starting conditions
would not reach until its next invocation.
gcrefuses to delete on avec0-indexed store without the vector extra. If the database carries anembedding_vectable andsqlite-veccannot be loaded — most commonly becauseengrava[vec]is not installed, though an unsupported build, an OS error or a SQLite error fail the same way — a pass that is about to physically delete stops before deleting anything and exits1with:Error: This database has a sqlite-vec index, and collecting thoughts without removing their vectors would strand them in it. Install 'engrava[vec]' and retry.Removing the rows without removing their vectors would strand those vectors in an index nothing can then reach them through. Only deleting passes are refused:
--dry-runis never refused, and neither is a run with nothing to delete. Read the archive strategy carefully, though —gc --expiredunder the defaultttl.strategy: archivestops after archiving only when it actually archived something. With no expired rows to archive it falls through to the archived-collection pass, which is refused when there are archived rows to collect. Install the extra (pip install 'engrava[vec]') and retry.
migrate
Runs pending schema migrations (ensures the core tables exist and are up to date). Takes no command-specific options. Safe to run after an upgrade.
engrava --db engrava.db migrate
export
Exports thoughts to a portable JSON file (with edges and metadata). Unlike
snapshot (JSONL, core tables, for backup/restore), export writes a single
indented JSON document and can be filtered by lifecycle status.
| Option | Type | Default | Description |
|---|---|---|---|
-o, --output |
path | <db-stem>.export.json (derived) |
Output JSON file path. |
--status |
lifecycle status | all | Only export thoughts with this lifecycle_status (e.g. ACTIVE). |
engrava --db engrava.db export -o thoughts.json
engrava --db engrava.db export --status ACTIVE # -> engrava.export.json
Failure safety. Like
snapshot,exportreads from one consistent point in time and writes to a temporary file next to-o, replacing it only once every read has completed — an existing file at-ois left byte-identical on an exception or a cancelled run.-omay not name the database currently open for--db, or its-wal/-shmcompanion files.
See Verifying integrity for the
equivalent Python API, store.verify_journal().
For the exhaustive package reference, see the OSS cli.md docs.