Repository navigation
OmniGraph 0.14 has six main highlights. The CLI has been redesigned for consistent destination selection, configuration and credentials. AI agents can use MCP on self-hosted servers with bearer credentials. You can see the changes on a branch and do a test merge. You can submit a write for review before publishing it. A reported local LDBC SNB run loaded about 630 million rows in 261 seconds. Structured API errors tell you if the request changed data and what to do next.
This release changes the HTTP contract, the CLI configuration and the error bodies. Read the upgrade actions before you upgrade. Upgrade the CLI, the server and the SDKs together.
Highlights
CLI redesigned for consistency
Commands now use --target to choose a destination and --config to choose input files. Named targets replace overlapping flags and implicit folder selection; mutations show where they will run. See the before/after examples. #1047, #1090, #1126
MCP for any self-hosted server
Tool parity: /mcp is mounted whenever the server has a bearer credential, and its tools call the same handlers and policy checks as the HTTP routes, so an agent sees what a REST client sees. Write tools appear only with --mcp-allow-writes, never retry, and answer outcome_unknown instead of guessing. omnigraph mcp --target NAME bridges a terminal agent over stdio without handing it the token; the bridge also needs --write to expose write tools. #994, #1118
Branch diffs and merge previews
Snapshot diffs: diff compares any two branches or commits, branch diff reports changes since the merge base, branch merge-base names that base, and branch merge --dry-run (GQ: branch merge … dry run) computes the three-way merge and its conflicts without publishing. The merge base is now the newest common ancestor, and branch create --from-commit starts a branch at a retained published commit under the current schema. #966, #941, #1016
Unpublished commits for review
Detached manifest versions: as commit writes the complete result of a mutation or merge as an unpublished commit, readable by its id and invisible to branch reads while its recorded target remains current, until commit publish installs it in one compare-and-swap or commit drop removes it. as unattached branch writes snapshots that chain but never publish, for scratch work an agent can throw away. Unpublished commits and unattached chains expire when another write advances their recorded branch or that branch is deleted or recreated. #942, #956
CSV import: 630 million rows in under five minutes
Partitioned bulk load: an overwrite load csv cuts its source into record-aligned 8 MiB slices, parsed in up to stage_write_concurrency streams with the same cap on concurrent Lance writers (8 by default), and checks integer-keyed edge endpoints against integer key sets while preserving row order. The load is a planned statement, so explain shows it. Reported local LDBC SNB runs over this release took SF30 (about 630 million rows in 31 load statements) from an incomplete run with eight edge tables killed at 45 GB resident memory to 261.4 s in one measured run, and SF1 from 47.07 s to 8.64 s. #997, #1085, #1121, #1134
Errors that say whether a request ran
Typed failure dispositions: HTTP API errors and MCP tool execution failures carry a code and a disposition with their effects (none, partial or unknown) and the action to take, and CLI data-command exit codes follow it. A client can tell a refusal it should fix from a write it must reconcile without parsing messages. #1076, #1078
Upgrade actions
Follow the 0.13 → 0.14 checklist. The essentials:
- Back up and upgrade together. Settle uncertain operations with the old CLI and preserve its journals. Stop old processes, verify a whole-root backup, and upgrade the CLI, server and SDK together. Existing 0.13 graphs do not need an export/import rebuild. Rollback after new writes means restoring the backup.
- Replace operator configuration. Recreate named targets and install credentials explicitly; there is no automatic importer. Use
--targetfor destinations and--configfor deployment source. Old routing flags and folder selection are gone. The checklist includes before/after YAML and commands. - Update clients and scripts. HTTP integrations require
Omnigraph-Http-Api: 0.14. Both npm packages are published at0.14.0; 0.13 clients are incompatible. Refresh MCP tool schemas and saved query plans, and update consumers of changed JSON fields and exit codes. See client compatibility and failure handling. - Check schemas and embeddings. String full-text calls now require
@index. Regenerate older offline embeddings with--reembed-alland reload them. Review range constraints and branch names before deploying. See search, embeddings, ranges and branch names. - Verify before restoring traffic. Validate configuration and run representative reads and controlled writes. See the recovery guide for exceptional cases.
Features
/mcpis mounted whenever the server has bearer credentials, not only OIDC, with read tools matching the HTTP routes and write tools behind--mcp-allow-writes. See MCP. (#994)omnigraph mcp --target NAMEconnects a terminal agent such as Claude Code or Codex to a registered server target's/mcpover stdio, keeping the token from the agent. Select one graph with--graphor the target default; writes also require the bridge's--write. See connecting a stdio agent. (#1118)- Compare two points with
diff, review a branch withbranch diffandbranch merge-base, and preview a merge with CLIbranch merge --dry-runor GQbranch merge … dry run; HTTP exposes the same operations. See diffs and merge preview. (#966) branch createcan start a branch at a retained published graph commit under the current schema (--from-commit,from commit "<id>",from_commit), so a fix can be tried where a change was made; pre-upgrade commits and deleted branch lifetimes are refused. See Start a branch at a commit. (#1016)- A mutation or merge ending in
as commitwrites an unpublished commit, readable by its id while its recorded target remains current;commit publishinstalls it andcommit dropremoves it. Another write advancing the target, or deleting or recreating it, expires the unpublished result and prevents publication. See unpublished commits. (#942) - A mutation ending in
as unattached branchwrites a snapshot that is readable by its commit id, invisible to every branch and extendable by later writes while its recorded branch stays at the captured head. Advancing, deleting or recreating that branch expires the whole chain. See unattached branches. (#956) load csvimports one authorized CSV object into a node or edge type throughmutate, with values typed by the schema. See CSV loading. (#997)return distinct { … }keeps one row per distinct projected tuple, deduplicating beforeorderandlimit. See Return, order, and limit. (#957)count(distinct $x)counts distinct nodes andcount(distinct $x.prop)distinct non-null values, grouped or not, inreturnand as anorderkey. See Return, order, and limit. (#965)- An aggregated
returncan project a Boolean over aggregates, such ascount($p) > 0 as has_member, evaluated per group and usable as anorderkey. See Return, order, and limit. (#974) - The benchmark catalog takes the host's local storage default, so one
benchmarks.yamlruns on a macOS workstation and on an XFS NVMe lab, and adds a merge scenario aged by 64 commits. (#902) - An embedded
Sessioncan replace or clear one Blob value by exact id withput_blob_at_asandclear_blob_at_as, each one graph commit with an optional ETag precondition. (#908) omnigraph blob putstores a file or stdin in one Blob cell andomnigraph blob clearsets it to null, on a direct or served graph, with--if-matchand a receipt naming the commit. (#928)PUTandDELETE /graphs/{graph_id}/blobreplace or clear one Blob value of up to 32 MiB as one graph commit, withIf-Matchpreconditions and the newETagin the receipt. (#922)- A fresh S3 cluster set up with
bootstrap_servinghands its ownership to the first server with--bootstrap-handoff FILE. See first boot from a bootstrap receipt. (#893) omnigraph accessreports who you are at a direct or server target and which actions the graph's policy grants you, graph-wide or on a branch; managed targets are unsupported. See what you may do at a target. (#1104)omnigraph version --jsonprints the package version, the HTTP contract and the storage formats the CLI writes and serves, without contacting a server. See Verify. (#987)GET /graphs/{graph_id}/commits,omnigraph commit listand the MCPcommits_listtool page through history withlimitand a page token, and a page reads only the history it returns. See commits. (#1013)- Eligible new commits on
mainare published daily after CI passes as immutable prereleases,edge-<date>-<commit>, so a deployment can pin one withVERSION=edge-…; the 30 newest and the newest build of each storage format are kept. See release channels. (#1031) - Every edge build is also a container image, such as
modernrelay/omnigraph-server:edge-20261016-9c2b367, on Docker Hub and GHCR. See release channels. (#1060) - Built-in read-only types such as
@Operatorand@Settinganswer GQ reads about the running binary, beside a graph's types or underog_catalog. See Engine catalogue. (#1008) - The
explain_dump_dirsetting writes every finished explain document into a directory; a failed write never fails the operation. See Session settings. (#1007) - A full-text call on a table version an older release wrote and never indexed is refused with
409full_text_index_requiredinstead of matching case-sensitively. See the search guide. (#1066) - GQT files can omit schema and seed and run against an existing local, S3 or Azure store with
omnigraph-gqt --store <URI>. See the GQT runner guide. (#894) - GQT generates deterministic seeds and load steps (
generate: v1), vectors and Zipf endpoints included, so benchmark datasets live in.gqtfiles; its YAML admission is stricter. (#894) - GQT steps can generate their parameters with
--- params generate: v1, including a newblobcolumn, so a case can pass a 32 MiB Blob without carrying its text. (#927) - The GQT runner runs a case against a live
omnigraph-serverwith--server <URL> --graph <ID>, and every opted-in case also runs through a server in CI. See the GQT runner guide. (#923) omnigraph-gqt --tracerecords steps, assertions, decision seams and engine events in a bounded JSONL file per attempt. See the GQT runner guide. (#912)GET /graphs/{graph_id}/accessand the MCPaccesstool tell a caller its actor and the actions its credential allows, as the installed policy decides them. See Learn your access. (#1073)- Query profiles report ANN stage timings and prefilter work for each nearest-search attempt. See query profiles. (#1086)
- A
load csvreceipt reports what each executed plan operator did: its rows, compute time and wall time. See the load explain guide. (#1099) explainaccepts aload csvstatement and prints its plan without reading a payload byte, and an overwrite load runs that plan through the engine's load operators. See the load explain guide. (#1026)- The
load csvsource limit is theload_source_max_bytessetting (32 MiB by default) instead of a fixed constant. See settings. (#1000) omnigraph catalogue -e '<gq>'queries the local binary's engine registries without reading configuration, credentials or storage. See the engine catalogue guide. (#1115)cluster list --profile NAMElists the clusters you can reach, andcluster pull --target NAME --out NEW_DIRdownloads an exact configuration revision from a registered managed target. See managed clusters. (#897)omnigraph cluster members list --target NAMElists a managed cluster's enrolled members, as a table or--json. See the CLI reference. (#1002)- Managed organization administrators can invite teammates, list members, change cluster access and offboard users with
organization --profile NAME. See team administration. (#1004) /mcpserves the agent skill's query, data, schema, search and change-feed references asomnigraph://best-practices/*resources for the server's version. See MCP. (#995)/mcpgains aschematool and an opt-in ad-hocreadtool, its tools carry titles and output schemas, and OAuth clients find its metadata without a proxy rewrite. See MCP. (#994)- Every read plan is checked against its query before it runs, and
explainreports how much the check covered in avalidationfield. See the explain guide. (#839) omnigraph queries listtakes a server target and lists one served graph's stored queries. See the CLI reference. (#1114)- The per-query memory pool is the
query_memory_bytessetting (150 MiB by default), settable per query; operators that cannot spill refuse work beyond it. See settings. (#1003) - Release archives include the server's AWS Secrets Manager token source, so archive and image servers accept the same token configuration. See HTTP server. (#1060)
- Releases name the commit and channel they were built from:
omnigraph versionprints acommitline, and edge builds report versions such as0.14.0-edge+9c2b367. See Verify. (#1031) omnigraph-benchmeasures read-only GQT workloads against an externally provisionedomnigraph-server. See the served acquisition guide. (#932)- Use registered server targets over HTTPS; Unix-domain socket targets and the legacy
--serverflag are unsupported in the released CLI. See server targets. (#988) omnigraph skill export DIRwrites the agent skill built into the binary, so an agent reads the skill for the CLI it runs. See the CLI guide. (#995)- Query explain lists the name and type of every result column. See the explain guide. (#901)
- The benchmark catalog measures a write as an unattached branch root beside its named-branch twin. (#961)
- Bounded writes accept the
write_max_bytessetting, and row data and Blob payloads now have separate 32 MiB allowances. See settings. (#933)
Behavior changes
- Omnigraph uses Lance 13.0.0. Run only
omnigraph cleanupon graph storage: Lance's own cleanup can make snapshots unreadable. See cleanup. (#1072) - On S3, a server can take over a leased cluster lock after heartbeats have been silent for a lease and a half (45 seconds by default), without
cluster force-unlock; unleased CLI and older-binary locks still require the exact-ID unlock. See restart after a failure. (#1097) - Correlated block aggregates use the same result types as
returnaggregates; integersumis exact in 128 bits and rounded once. See the query guide. (#901) - A multi-hop bound on an edge between two node types, such as
worksAt{1,2}, is refused withT5instead of silently capped at one hop. See traversal. (#885) - Dropping a property no longer rewrites its table; the values stay in data files until
omnigraph optimizerewrites them. See schema changes. (#871) - Source builds from the
v0.14.0tag report package version0.14.0; later development builds can report a newer version. See checking the installed version. (#998) - Every explain document carries
pipelines, cut at each operator that holds its whole input and at join build sides. See the explain guide. (#1099) - Benchmark cases are
gqt-v1dataset and query pairs in onebenchmarks.yaml, with 33 bounded end-to-end scenarios; the oldbranch-merge-v1runner is retired. (#894) - The OpenAPI specification documents parameter bounds, graph catalogue routing and required error responses more completely. (#1131)
- HTTP routes, OpenAPI operations, MCP tools and CLI commands share one operation inventory, checked against their registrations. (#1132)
- A
load csvstatement's source, batches and staging share onequery_memory_bytesallowance, and a statement it cannot hold is refused before any fragment write. See settings. (#1026) - Managed cluster commands use immutable previews and native deployment delivery, with
--waiton status and history bounded to 100 entries. See managed commands. (#897) - An MCP write tool waits 30 seconds for its result instead of 60, so a client on the TypeScript SDK's default timeout gets
outcome_unknownfirst. See MCP. (#999) - A merge holds its source branch only while capturing it, so writes to the source no longer wait, and merges from one source into several targets run at once. (#822)
- Servers on local storage and S3 release writer admission after a proven clean shutdown, so the next server starts normally. See writer ownership. (#899)
- A return alias can follow
nearest(...)as a secondary sort key, as afterbm25(...)andrrf(...);T18is withdrawn. See return, order, and limit. (#960) - Whether an operator holds its input is one planner fact, so explain, the push executor and the engine cannot disagree; printed explain documents are unchanged. (#1100)
- An
rrf()whose arms rank two bindings one traversal connects is the type errorT59instead of planner codeP005. See the diagnostics guide. (#1069) - An
rrf(...)order sorts by fused score, then later keys, then binding ids, withlimitcounting rows;rrf(...)projects as<var>._rrf. See search. (#938) - A served CLI write refused with
401,403or400 api_contract_mismatchreportseffects: noneinstead of unknown effects. See failed commands. (#981) - The server logs Lance's
dataset_eventsandfile_audittargets only at warning and error, whateverRUST_LOGsays. See server logging. (#926) - A served write whose storage request gets no definite answer no longer stops the server, and fresh-file requests retry within Lance's bounds. See storage failures. (#1096)
- Numeric comparisons use the same compiler-selected types in scans, expressions and mutation predicates, so
F32against0.1matches the same rows on every path. (#901) - Search arguments run in their declared types:
fuzzyedit limits andrrfrank constants refuse null and out-of-range values. See search argument ranges. (#901) write_max_bytesno longer has a fixed maximum; any value of at least1is accepted, and the 32 MiB default is unchanged. See settings. (#1000)
Fixes
branch mergebases on the newest commit both branches descend from; the nearest by hops could drop a delete or report a false conflict. See merge. (#941)- Full-text functions lowercase and stem as soon as rows are written; before
omnigraph optimize,searchfor "deep" missed "Deep Learning". See the search guide. (#909) - Wide-column reads stream bounded scan batches instead of collecting the whole type; selective
limitplans fetch output-only columns for kept rows. The query memory limit still applies. See execution. (#887) - A
bm25()score no longer depends on the query's filters when rows were written after the full-text index was built. See the explain guide. (#839) - A graph opened on a captured storage destination sends every request there; some reads and merges followed
AWS_ENDPOINT_URLinstead. See operator targets. (#1103) - A branch named
merge,difformerge-basecan be deleted over HTTP; the route answered 405. See branches. (#1091) - On a local graph,
cleanupfinishes a table whose previous run stopped halfway instead of failing on it every run after. See cleanup. (#1011) - Graph-client setup uses one captured operator configuration, so edits during server discovery cannot redirect the client or its credential. See server targeting. (#1047)
- Cluster, stored-query, policy and graph-list commands return an error on a broken pipe instead of panicking. See deployment recovery. (#1047)
- The CLI reads operator configuration from a read-only directory and checks credential expiry before sending a request. See operator configuration. (#1126)
- The CLI sends a server's token only where its
endpointpoints after..segments resolve, sohttps://host/a/../bcannot carry it elsewhere. See server credentials. (#1030) - The CLI keeps a refusal's exit code and prints one JSON response when output fails, and timeouts no longer wait at exit. See CLI output and errors. (#1047)
commit publishandcommit dropreport a conservative outcome when their output fails, asmutatedoes. See failed commands. (#1047)- Local account, profile, version, skill-export and embedding reports return an error on a broken pipe instead of panicking. See CLI output and errors. (#1047)
- Maintenance commands return output errors without panicking or repeating completed work. See maintenance failures. (#1047)
queryand aliases keep the output format captured for execution, and JSON defaults also format execution errors. See CLI output and errors. (#1047)- Aliases and served cluster commands use one operator configuration during setup, keeping destination and credential consistent. See server targeting. (#1047)
- Schema, init and lint commands report output failures without panicking, and a lost schema-apply result keeps its guidance. See failed commands. (#1047)
- Load and Blob receipt output failures go through normal CLI error reporting without replaying the write. See failed commands. (#1047)
commit publishandcommit dropaccept--ason a direct graph, as other direct writes do. See unpublished commits. (#1029)- A write answered with 500
outcome_unknownlogs that 500 instead of its underlying error's status. See storage failure logs. (#1117) - A served write whose staging failure is proven unpublished no longer stops the server; uncertain publication still fails closed. See writer ownership. (#1070)
- A deployment reports
in_progressuntil the server releases its admission, pending result delivery included. (#1026) omnigraph embedembeds a record's@embedsource exactly as stored, as a textnearestquery embeds its text, instead of a templated line. See Embeddings. (#929)- Full-text queries resolve reused variable names separately in sibling blocks, avoiding false missing-index errors. See full-text search. (#1136)
- A historical query no longer reads a dropped property's values through a later property of the same name; it is refused. See historical reads. (#959)
- Integer
suminreturnis exact in 128 bits and rounded once toF64, so totals above 2^53 keep their cancellations. See the query guide. (#901) - Secure-store failures name the redacted operation and cause, with the macOS OSStatus when available. See Keychain access failures. (#1115)
explain load csvover HTTP or MCP needschangeon the target branch before probing the source, so readers cannot learn whether it exists. (#1026)- CSV overwrite validation holds less memory, and endpoint validation costs probes and scans by the table's rows and index coverage. (#1026)
login --create-profile NAME --organization IDverifies that organization through a fresh device session before saving the profile and credentials. (#897)- Managed login no longer counts OS keychain consent against its 35-second network deadline. See managed commands. (#897)
- An overflowing decimal float literal in a query is refused at type checking with
T3. (#934) - Operator configuration validation refuses unsupported identifiers and YAML anchors and aliases before execution. (#1115)
- A ranking the planner refuses by design is a bad request with planner code
P001, the refused expression and a fix. See the diagnostics guide. (#815) - A served write whose publication fails before its manifest request, such as a throttled put, returns 500 and keeps serving. See storage failures. (#1092)
@rangecompares values with their bounds exactly; rounding both to a double let anI64above 2^53 pass a maximum. See constraints. (#1071)- A
nearest(),bm25()orrrf()order on a traversal-reached binding ranks it whatever the declaration order. See the search guide. (#1069) - rustls 0.23.46 addresses four TLS security advisories, and unused legacy AWS HTTPS dependencies are removed. (#997)
schema applydrops a property that carries@index,@unique,@rangeor@check, with its index; such a drop was refused. See schema changes. (#1062)- A server target's
endpointrequires HTTPS; HTTP is allowed only for anonymous loopback servers. Unsupported endpoints fail configuration validation before a connection is attempted. (#988) - Server logs record an error's class instead of its message, which can hold object URIs or credentials. See HTTP errors. (#926)
- Rows tied on every
orderkey are ordered by declared bindings before anonymous traversal endpoints, solimitcuts them predictably. See the explain guide. (#839) - TLS connections reject distrusted emSign certificate chains (RUSTSEC-2026-0381, 0382 and 0383). (#1092)
- TLS verification no longer trusts distrusted intermediates:
rustls-webpki0.103.16 andwebpki-roots1.0.10 fix RUSTSEC-2026-0382 and drop the emSign root. (#1105) - Projected parameters keep their declared numeric width and list element type, and unused parameters are bounds-checked. (#901)
- Mutations run on the node or edge target the compiler chose, and every declared parameter is validated before any statement runs. (#901)
- Every 401 carries a
WWW-Authenticatechallenge so REST clients can discover OAuth sign-in when configured, andbeareris accepted in any case. See authentication. (#1122)
Performance
- Adding a nullable property, renaming one and dropping one change only table metadata, so a schema change on a large Blob table reads no rows. See schema changes. (#871)
- A warm read checks for new commits with two small HEAD requests instead of listing
__manifestversions, which on S3 returned up to 1,000 keys per read. (#962) - CLI change commands that follow every page ask for 8,192 changes per page instead of 1,000, so they send fewer requests. See change feeds. (#968)
- A correlated block that binds its own node by a key runs once from that node instead of per outer row, when cheaper. See the query guide. (#973)
- Export and change-feed baselines read each batch's Blob values with one planned read per column instead of one per row. (#910)
- A
load csvoverwrite writes its files through up tostage_write_concurrencywriters at once (8 by default), producing the files one writer would. (#1121) - A
load csvoverwrite parses its source in up tostage_write_concurrencyparallel streams (8 by default) of record-aligned 8 MiB slices, preserving row order and reporting the earliest failed slice deterministically. (#1134) - A
load csvoverwrite checks edge endpoints against integer key sets when endpoint types have an integer@key, and picks probe or scan by cost. (#1085) - A load statement reuses identical metadata, index and data reads across checks within its admitted caches; it falls back to uncached reads when their memory allowance cannot be admitted. See settings. (#1026)
- Merges into different target branches on one handle run concurrently instead of queueing; merges into one target still serialize. (#825)
- Automatic traversals price missing or incomplete edge indexes during planning, and root scans run their planned scalar-index access. See the explain guide. (#924)
Removals
- The embedded
Omnigraph::diff_between,diff_commitsand theirChangeSettypes are removed; useOmnigraph::diff_page. See compare two points. (#966)
Contributors
Thanks to @ragnorc, @azimafroozeh, @aaltshuler, @sean0407 and @pronskiy, who contributed to this release.
Full changelog: v0.13.0...v0.14.0 · 0.13 → 0.14 checklist