InvariantDB stores what was true, when, and what the system knew when it acted. The catalog below lists the capabilities in the current release, from bitemporal Cypher and provenance to agent memory. Capabilities that are not yet available are listed under Things we don't claim.
| Feature | Surface |
|---|---|
| Append-only versioned history | Engine-level; verify with GET /graphs/{name}/audit/verify |
Bitemporal versioning on every property (VALID + RECORDED) | Cypher AT VALID / AT RECORDED / OVER VALID BETWEEN |
Hash-chained write history (each node and relationship write committed through Cypher, /execute or import records the SHA-256 hash of the previous record; schema, index and constraint changes and the legacy POST /write route are not recorded) | GET /graphs/{name}/audit/verify returns clean or the first broken record |
WORM mode (no API turns it off once engaged): updates and deletes sent through Cypher, MCP tools, /execute and import are refused; creates are accepted. It does not cover the legacy POST /write route on the default graph, schema or index changes, or an administrator dropping the whole graph | GET /graphs/{name}/worm, POST /graphs/{name}/worm/engage (Enterprise license) |
| Schema versioning + history | /graphs/{name}/schema/history |
| Recovery on restart | Automatic; acknowledged writes and index, constraint and schema changes are kept. A write statement interrupted by a crash leaves nothing behind |
| On-demand full maintenance (repair) | POST /graphs/{name}/compact-full |
| Per-property history walk | db.propertyHistory(nodeId, 'prop') |
| Online backup + restore (writes continue during a backup; pause writes for an archive of one exact moment) | /graphs/{name}/backup + /graphs/{name}/restore |
Implements openCypher with bitemporal extensions. Highlights:
| Feature | Example |
|---|---|
MATCH / WHERE / RETURN / WITH | standard |
CREATE / MERGE / DELETE / DETACH DELETE | standard |
SET / SET += (map-merge) | SET n += {a: 1, b: 2} |
AT VALID / AT RECORDED / AS OF | MATCH (n) AT VALID '2024-06-01' RETURN n |
OVER VALID BETWEEN, OVER RECORDED BETWEEN | history walks across a window |
OPTIONAL MATCH with multi-stage carryover | nested across WITH boundaries |
UNWIND ... CREATE bulk-seed + range() | UNWIND range(1, 1000) AS i CREATE (:Node {n: i}) |
| WITH-stage aggregation | WITH x, count(*) AS c WHERE c > 5 |
count(DISTINCT x) / collect(DISTINCT x) | dedup-aware aggregates |
| Pattern comprehensions | [(a)-[:KNOWS]->(b) | b.name] |
Quantifiers ANY / ALL / NONE / SINGLE | WHERE ALL(x IN xs WHERE x > 0) |
reduce(acc=init, x IN list | expr) | functional fold |
shortestPath / allShortestPaths | path algorithms in-query |
COUNT { MATCH ... } count-subquery | Cypher-5 |
Named paths p = (a)-[r]->(b) | length(p), nodes(p), relationships(p) |
=~ regex, STARTS WITH, CONTAINS | text matching |
Inline cosine() / euclidean() / dotProduct() | vector similarity in WHERE/RETURN |
$name param substitution | anywhere a literal goes, including LIMIT $page |
EXPLAIN / EXPLAIN ANALYZE / PROFILE | dry-run + with-timing; PROFILE (from 1.4.1) is the same as EXPLAIN ANALYZE |
CREATE INDEX (current + historical backfill) | property indexes |
| UNIQUE, NOT NULL, NODE KEY and property type constraints, checked on every write | CREATE CONSTRAINT [name] [IF NOT EXISTS] FOR (n:L) REQUIRE n.p IS UNIQUE / IS NOT NULL / (n.a, n.b) IS NODE KEY / n.p IS :: INTEGER; CALL db.constraints() lists them; CALL db.validateConstraints() checks stored data |
| Required-relationship constraint, with an optional quote check (from 1.4.2): a node of a label cannot exist without a relationship of a given type to a node of a target label. Checked on current records on every write; not checked at past valid times | CREATE CONSTRAINT [name] FOR (c:Claim) REQUIRE (c)-[:SUPPORTED_BY]->(:Passage) [OPTIONS { quote: true }]; a violation answers 409 CONSTRAINT_VIOLATION and applies nothing |
| All-or-nothing write statements | An error, time limit or cancel leaves nothing of the statement; write statements on one graph run one at a time |
| Feature | Surface |
|---|---|
| Fulltext search (BM25) | gds.fulltextSearch(label, property, query [, k]); from 1.4.3 an analyzer option that indexes every script |
| Declared full-text indexes (from 1.4.3) | db.createFulltextIndex(label, property) and db.fulltext.query(...): phrases, AND / OR / NOT, prefixes, proximity, same sentence or paragraph, exact totals, pages and snippets with offsets; kept current on every write; reads at a past time with history: 'all'. See Cypher → |
| Vector similarity search | gds.vectorSearch(label, prop, q, k); from 1.4.2.2 a config map with filter (property conditions) and nodeIds (the nodes the search may return), applied before the top k |
First-class Vector property type | Schema |
vector.similarity(), vector.distance(), vector.dot(); from 1.4.2.2 vector.similarity.cosine() and vector.similarity.euclidean() (Neo4j 5 names) | Inline in WHERE / RETURN |
| Predicate-filtered vector search | Cypher WHERE filter + ORDER BY cosine(...) in one query — no external vector DB. See Cypher → |
| Vector search narrowed by a graph pattern (from 1.4.2.2) | MATCH <path> WHERE ... ORDER BY vector.similarity(r.p, $q) DESC LIMIT k scores only the nodes the pattern reaches. See Cypher → |
| Top-k plan selection | ORDER BY vector.similarity(...) DESC LIMIT k keeps only the top k rows while scoring; a WHERE vector.similarity(...) > t filter scores every row it is given |
| Vector + graph fusion | gds.semanticMatchExpand(...) |
| Embedding model versioning + drift | db.embeddingModelDrift() |
| Pre-build + cache the vector index | db.buildVectorIndex(label, prop, metric) |
| Algorithm | Surface |
|---|---|
| shortestPath | gds.shortestPath(source, target) → (path, length); source and target are string ids or id(n) |
| pageRank | gds.pageRank([damping]) → (nodeId, score); damping defaults to 0.85 |
| connectedComponents | gds.connectedComponents() → (nodeId, componentId), weakly connected |
| degreeCentrality | gds.degreeCentrality() → (nodeId, inDegree, outDegree) |
Full guide: AI memory.
| Primitive | Procedure(s) |
|---|---|
| Episodic memory log | db.recordEpisode, db.replayEpisodes |
| Working-memory consolidation | db.consolidateSession, db.sessionSummaries |
| Inference provenance + belief revision | db.recordBelief, db.reviseBelief, db.beliefLineage |
| Salience decay + reinforcement | db.applySalienceDecay, db.reinforceSalience |
| Vector + graph fusion | gds.semanticMatchExpand |
| Embedding drift detection | db.embeddingModelDrift |
| Property provenance walks | db.propertyProvenance, db.derivedFrom |
Full guide: Compliance. Vertical mappings: Regulated industries.
| Primitive | Surface |
|---|---|
Field-level ACL: for authenticated callers, a property the caller may not read is returned as null or left out, and a search over it is refused; from 1.4.2 a node that hides the searched property from the caller through another of its labels is left out of search results and of their scores; write ACLs are checked only when a Cypher CREATE creates a node (not on MERGE, SET, /execute or import) + hash-chained ACL event log of denied reads in Cypher, refused searches and refused creates | Schema-driven; db.aclEvents, db.verifyAclChain() |
| Purpose binding (GDPR Article 5(1)(b)) | purpose claim on the caller's JWT, checked against purpose lists in the schema's field ACLs |
| Per-subject DSAR export (Article 20) | db.subjectExport(subject) |
| Per-property provenance | db.propertyProvenance + db.derivedFrom |
| Write provenance | provenance object on POST /cypher and POST /graphs/{name}/cypher requests, or X-Provenance-* headers; the engine also records the authenticated credential as principal: from 1.4.2 for every write of an authenticated request (not for import and other background jobs or the legacy POST /write route), on 1.4.1 and earlier only when the request carries provenance; read with db.propertyProvenance and, from 1.4.2, db.history |
| Private mode: noised aggregates | API-key privacyMode: only expressions built on count, sum or avg may be returned; noise is added and each aggregate spends from the key’s ε budget. This release does not give a differential-privacy guarantee; do not rely on private mode alone to protect individual records |
| Capability tokens | POST /capabilities/issue (signed, scoped, time-limited) |
| Compliance bundle export | /graphs/{name}/compliance/export (Enterprise license) |
| Tamper-evidence checks | GET /graphs/{name}/audit/verify, db.verifyAclChain(), db.dpVerifyChain() |
| Feature | Surface |
|---|---|
| HTTP + JSON Cypher endpoint | POST /cypher, POST /graphs/{name}/cypher |
| Schema | GET / POST /graphs/{name}/schema, GET /graphs/{name}/schema/history |
| Multi-graph registry | GET /graphs, POST /graphs, DELETE /graphs/{name} |
| Request limits | Per-graph concurrency limit (503 with Retry-After) and a per-credential request-rate limit on POST /cypher (429 with Retry-After) |
| Idempotency keys on write requests (from 1.4.2) | Idempotency-Key header on the Cypher, /execute, document and import routes: a resend with the same key returns the first answer and writes nothing. GET /graphs/{name}/idempotency-keys/{key} tells whether a keyed request committed. Keys are kept for 24 hours by default |
| Per-query timeout | Configurable at deploy time |
| Slow query log + counter | Configurable threshold; Prometheus surface |
| Cooperative yielding (long queries) | Configurable throttle threshold |
| Cursor / streaming reads | POST /graphs/{name}/cursor |
POST /graphs/{name}/explain | Plan + cost estimate |
/healthz / /metrics (Prometheus) | Liveness + observability |
| OpenAPI spec | GET /openapi.yaml + GET /docs (Swagger UI) |
| OpenTelemetry tracing | OTLP/gRPC export (self-hosted; configured by the operator) |
| Mode | Surface |
|---|---|
| Operator-static bearer tokens | Authorization: Bearer <env-token> (constant-time compared) |
| Runtime-issued API keys | gq_<random> prefix; SHA-256 stored, secret shown once |
| JWT (Cognito-style) | Configurable issuer + JWKS URL |
| HMAC | Per-request signed header |
| Capability tokens | Signed, scoped, time-limited bearer |
| Field-level ACL + roles | Schema-declared; principal carries role list |
| Access policy by label and relationship type (from 1.4.2) | db.access.* procedures: roles with allow and deny rules for read, write and grant. For a credential the policy restricts, hidden nodes and relationships are absent from Cypher reads and refused writes answer 403 ACCESS_DENIED. On this release such a credential is refused search, other procedures, temporal reads and the HTTP routes outside Cypher; there are no rules on individual records. See the Cypher reference |
| Purpose binding | purpose claim on the caller's JWT, matched against purpose lists in the schema's field ACLs |
| Graph ownership | Graphs created from the dashboard belong to your tenant; API keys can be limited to a list of graphs |
| Private mode | privacyMode on API key — only expressions built on count, sum or avg may be returned |
pip install https://sdk.invariantdb.com/python/invariantdb-0.8.0-py3-none-any.whl)The Python SDK is not published on PyPI. Install it from the URL above.
db.cypher(graph, query, parameters)db.graphs.ensure(name), db.graphs.list()db.indexes.ensure(graph, [{"label": ..., "property": ...}])db.search.fulltext(...), db.search.vector(...), db.search.prebuild(...)db.cursor.stream(graph, query, batch_size=1000)npm install https://sdk.invariantdb.com/node/invariantdb-0.8.0.tgz)Same surface, idiomatic to TypeScript.
Endpoint: https://invariantdb.com/graphs/{name}/mcp (HTTP POST, JSON-RPC 2.0; authenticated with Authorization: Bearer gq_... or OAuth sign-in). Works with MCP clients such as Claude, Cursor and VS Code. 28 tools for Cypher queries and lookups, search, agent memory, documents, lineage, and compliance. From 1.4.2, nine experimental tools for run traces and a shared workspace (off by default) are served where the server operator has turned them on. See the MCP page for client setup.
| Feature | Surface |
|---|---|
Prometheus /metrics | 30+ counters (queries, mutations, ACL events, private-mode counters, integrity ops) |
| Job lifecycle persistence | Long-running imports survive restart |
| Per-graph flush | POST /graphs/{name}/flush |
| Per-graph warm-up | /graphs/{name}/warmup |
| Per-graph backup + restore | /graphs/{name}/backup, /restore |
| Engine info / capabilities | /healthz |
count/sum/avg are available with noise; this release does not give a differential-privacy guarantee for any of them. min/max are not implemented in private mode. Do not rely on private mode alone to protect individual records.← Quickstart · AI memory → · Regulated industries → · Compliance →