ArcFlow
Company
Managed Services
Markets
  • News
  • LOG IN
  • GET STARTED

OZ brings Visual Intelligence to physical venues, a managed edge layer that lets real-world environments see, understand, and act in real time.

Talk to us

ArcFlow

  • World Models
  • Sensors

Managed Services

  • OZ VI Venue 1
  • Case Studies

Markets

  • Sports
  • Broadcasting
  • Robotics

Company

  • About
  • Technology
  • Careers
  • Contact

Ready to see it live?

Talk to the OZ team about deploying at your venues, from a single pilot match to a full regional rollout.

Schedule a deployment review

© 2026 OZ. All rights reserved.

LinkedIn
ArcFlow Docs
Start
  • Quickstart
  • Installation
  • Bindings
  • Platforms
  • Get Started
  • Cookbook
Concepts
  • World Model
  • Graph Model
  • Evidence Model
  • Observations
  • Confidence & Provenance
  • Proof Artifacts & Gates
  • SQL vs GQL
  • Graph Patterns
  • Parameters
  • Query Results
  • Persistence & WAL
  • Snapshot-Pinned Reads
  • Error Handling
  • Execution Models
  • Causal Edges
  • Adapter Discipline
  • Time Decay
  • Layers
  • 1. World Store
  • 1a. World Store · Smart Reader
  • 2. Perception Lake
  • 3. World Graph
  • 4. Query Engine
  • 5. Live Surface
  • 6. Event Bus
  • 7. Behavior Engine
  • 8. Algorithm Library
  • Virtual Computed Columns
  • Threading Model
  • Typed ID Contract
  • The Information Layer
  • The Memory Engine
WorldCypher
  • Overview
  • Execution Options
  • Statements
  • MATCH
  • WHERE
  • RETURN
  • OPTIONAL MATCH
  • CREATE
  • SET
  • MERGE
  • DELETE
  • REMOVE
  • ASOF JOIN
  • CREATE NODE LABEL
  • CREATE PROGRAM
  • CREATE TRIGGER
  • CREATE LIVE VIEW
  • CREATE WINDOW
  • CREATE DECAY POLICY
  • REFINE EDGE / REPROCESS EDGES
  • Sessions & Transactions
  • Composition
  • WITH
  • UNION
  • UNWIND
  • CASE
  • FOREACH
  • Schema
  • Schema Overview
  • Indexes
  • Constraints
  • Functions
  • Built-in Functions
  • Aggregations
  • Procedures
  • Shortest Path
  • EXPLAIN
  • PROFILE
  • Temporal Queriesfacet
  • Spatial Queriesfacet
  • Algorithmsfacet
  • Triggers
Capabilities
  • Live Queries
  • Vector Search
  • Trusted RAG
  • Spatial Knowledge
  • Temporal
  • Behavior Graphs
  • Graph Algorithms
  • Skills
  • CREATE SKILL
  • PROCESS NODE
  • REPROCESS EDGES
  • Sync
  • Programs
  • GPU Acceleration
  • Agent-Native
  • MCP Server
  • Event Sourcing
  • Intent Relay
  • Event Bus
Use Cases
  • Agent Tooling
  • Trusted RAG
  • Knowledge Management
  • Behavior Graphs
  • Autonomous Systems
  • Physical AI
  • Digital Twins
  • Robotics & Perception
  • Sports Analytics
  • Grounded Neural Objects
  • Fraud Detection
Walkthroughs
    Guides
  • Agent Integration
  • Building a World Model
  • Modeling a Social Graph
  • Build a RAG Pipeline
  • Using Skills
  • Behavior Graphs
  • Swarm & Multi-Agent
  • Fleet Coordination
  • From SQL to GQL
  • Filesystem Workspace
  • Data Quality
  • Code Intelligence
  • Scale Patterns
  • Lakehouse Fast-Path
  • Python Executor SDK
  • Tutorials
  • Knowledge Graph
  • Entity Linking
  • Vector Search
  • Graph Algorithms
  • Recipes
  • CRUD
  • Multi-MATCH
  • MERGE (Upsert)
  • Full-Text Search
  • Batch Projection
  • Multi-Source Observation
  • Sports Analytics
Operations
  • CLI
  • REPL Commands
  • Snapshot & Restore
  • Filesystem Projection
  • Plugin Management
  • Agent Governance
  • Server Modes & PG Wire
  • Persistence (ops)
  • Import & Export
  • Deployment
  • Deployment Modes
  • Daemon (UDS)
  • Why not Docker
  • Architecture
  • Engine Architecture
  • Cloud Architecture
  • Sync Protocol (Deep Dive)
  • World Graph Substrate (Preview)
Reference
  • TypeScript API
  • Glossary
  • Naming & Domain Map
  • Data Types
  • Operators
  • Error Codes
  • GQL Reference
  • Known Issues
  • Versioning
  • Licensing
  • Conformance
  • GQL Conformance
  • openCypher TCK
GQL Reference
    Conformance
  • Conformance Dashboard
  • openCypher TCK Results
  • Features
  • MATCH Basic
  • CREATE Nodes Edges
  • SET REMOVE Properties
  • DELETE Detach DELETE
  • RETURN WITH WHERE
  • Order BY Limit Skip
  • Order BY Nulls First Last
  • UNWIND
  • Aggregate Functions
  • OPTIONAL MATCH
  • Variable Length Paths
  • Label OR AND NOT Expressions
  • Label Wildcard
  • Quantified Path Sugar
  • Path Modes Walk Trail Simple Acyclic
  • Shortest Path Variants
  • IS Labeled Predicate
  • Element ID Function
  • IS Type Predicate
  • Binary Literals
  • Line Comments Solidus
  • Line Comments Minus
  • GQLSTATUS Result Codes
  • GQL Error Code Mapping
  • Transaction Control Syntax
  • SET Session
  • Conditional Execution WHEN THEN ELSE
  • RETURN NEXT Pipeline
  • Primary Key Constraint
  • Unique Constraint
  • Deterministic MERGE Via PK
  • Undirected Edge MATCH
  • Cast Type Conversion
  • GQL Directories
  • Multiple Labels Per Node
  • GQL Flagger
  • NEXT Linear Composition
  • Cardinality Function
  • INT64 BIGINT Type Names
  • FLOAT64 Double Type Names
  • Log10 Log2 Functions
  • Trim Leading Trailing Both
  • FILTER Clause
  • LET Statement
  • Group BY Explicit
  • EXCEPT SET Operations
  • INTERSECT SET Operations
  • ALL Different Predicate
  • Same Predicate
  • Property Exists Function
  • Path Variable Binding
  • USE Graph Clause
  • FOR IN List
  • Typed Temporal Literals
  • Session SET Value Params
  • Typed List Annotations
  • arcflow.cosine() function
  • arcflow.embed() function
  • arcflow.similar() procedure
  • arcflow.graphrag() procedure
  • ArcFlow Extensions
  • LIVE Queries
  • Reactive Write-Back Views
  • Evidence Algebra
  • Relationship Skills
  • AI Function Namespace
  • Graph Embedding Algorithms
  • ASOF JOIN
  • Durable Workflows
  • Incremental Z-Set Engine
  • GPU GraphBLAS
  • Triggers
  • HNSW Vector Index
  • Extensions Moat

Language Bindings

One engine, every language. The full install matrix is sourced from the engine release manifest — no hand-rolled commands here.

Install matrix temporarily unavailable. Check github.com/ozinc/arcflow for current artifacts.

All bindings wrap a single C ABI shared library (libarcflow). Direct function calls when in-process. TCP / HTTP / MCP when you need network access.

If you are writing a custom binding, read Adapter Discipline first — it is the boundary contract every binding inherits. The short version: the edge translates; only the core decides.

Rust SDK#

The primary interface.

use arcflow::{open, open_concurrent, compile, Label, Properties};
 
// In-memory graph — direct store access
let mut store = arcflow::open();
let id = store.create_node(
    vec![Label::new("Person")],
    Properties::new(),
);
 
// Thread-safe concurrent access — recommended for applications
let db = arcflow::open_concurrent();
db.execute("CREATE (n:Person {name: 'Alice', age: 30})").unwrap();
 
let result = db.execute("MATCH (n:Person) RETURN n.name").unwrap();
for row in &result.rows {
    println!("{}", row.get("name").unwrap());
}

Key Types#

| Type | Description | |--- ---|---#

----| | GraphStore | Single-threaded graph store with full low-level API | | ConcurrentStore | Thread-safe wrapper with execute() — auto-detects read vs write | | Engine | Read-only query engine | | MutableEngine | Read-write query engine | | QueryResult | Rows + columns from query execution | | compile() | Parse WorldCypher text into executable IR |

Compile + Execute#

let db = arcflow::open_concurrent();
db.execute("CREATE (n:Person {name: 'Alice'})").unwrap();
 
// Pre-compile for repeated execution
let query = arcflow::compile("MATCH (n:Person) RETURN n.name").unwrap();
 
// Execute compiled query
let result = db.execute("CALL algo.pageRank()").unwrap();
assert!(result.rows.len() > 0);

Python#

Native bindings over the C ABI. No compile step needed — loads the shared library at runtime.

from arcflow import ArcFlow
 
# In-memory or persistent
db = ArcFlow()                  # in-memory
db = ArcFlow("/tmp/mydb")       # persistent (WAL-journaled)
 
# Execute queries — typed parameters, no manual string escaping
db.execute("CREATE (n:Person {name: 'Alice', age: 30})")
result = db.execute(
    "MATCH (p:Person) WHERE p.name = $name RETURN p.age",
    params={"name": "Alice"},
)
for row in result:
    print(row)
 
# Algorithms work the same way
db.execute("CALL algo.pageRank()")
 
# Context-manager for deterministic cleanup
with ArcFlow() as db:
    db.execute("CALL db.demo()")
 
# Version — both return the same string. See /docs/reference/versioning
import arcflow
print(arcflow.__version__)        # "0.8.0"
print(ArcFlow.version())          # "0.8.0"

Bulk ingest — three throughput tiers#

For ingest, pick the highest-throughput path that matches your input shape. Each successive tier removes one layer of Python overhead.

PathInputThroughputUse when
db.execute("CREATE …")parser-bound Cypher3–10 K wpsone-off writes, exploratory
bulk_create_nodes / _relationshipslist of (labels, props) / (from, to, props) tuples~360 K wpsrows already in Python
bulk_create_nodes_from_arrow / _relationships_from_arrowpyarrow.Table / RecordBatch~1 M+ wpsParquet / Arrow / Polars pipelines
load_parquet / load_csvpath on diskparquet ≈ Arrow pathbulk import from a file
import pyarrow as pa
 
# Tier 1 — Python tuples (most common shape)
entity_ids = db.bulk_create_nodes([
    (["Entity"], {"id": i, "x": float(i), "y": 0.0})
    for i in range(95)
])
frame_ids = db.bulk_create_nodes([
    (["Frame"], {"id": f"frame_{t}", "ts_ms": t * 16})
    for t in range(46_000)
])
edges = [(eid, fid, {"x": float(ei), "y": float(fi)})
         for fi, fid in enumerate(frame_ids)
         for ei, eid in enumerate(entity_ids)]
db.bulk_create_relationships("OBSERVED_AT", edges)
 
# Tier 2 — Arrow-direct (no JSON marshal, saturates the engine)
tbl = pa.table({
    "id": pa.array(range(95), type=pa.int64()),
    "x":  pa.array([float(i) for i in range(95)], type=pa.float64()),
})
db.bulk_create_nodes_from_arrow("Entity", tbl)
# Spatial Struct columns ({x: f64, y: f64[, z]}) round-trip as native Point/Point3d.
 
# Tier 3 — File-direct (no Python in the loop at all)
n_loaded = db.load_parquet("data/tracking.parquet", "TrackingObs")
db.load_csv("data/players.csv", "Player", chunk_size=100_000)

bulk_create_* and load_* have CREATE semantics — every call allocates a new node or edge, even between the same pair. Use Cypher MERGE if you need find-or-create.

Prepared statements — db.prepare(query)#

Compile a Cypher query once for repeated execution. Each .execute(params) call skips the parser and goes straight to the executor. For analytics workloads that issue the same query thousands of times with different parameters (dashboards, real-time scoring, ML feature pipelines), this avoids the per-call parse cost.

stmt = db.prepare("MATCH (e:Entity {id: $eid}) RETURN e.x, e.y, e.speed")
for eid in entity_ids:
    row = next(iter(stmt.execute({"eid": eid})))
stmt.close()

The statement borrows from this database — close it before closing the database, or use it as a context manager.

Zero-copy result handoff — result.to_arrow() / to_polars() / to_pandas()#

Materializing a 1M-row result as list[dict] is slow and lossy. The Arrow handoff hands typed Arrow buffers to PyArrow / Polars / Pandas / DuckDB zero-copy:

result = db.execute("MATCH (p:Player) RETURN p.jersey, p.name, p.age")
tbl = result.to_arrow()      # pyarrow.RecordBatch
df  = result.to_polars()     # polars.DataFrame  (Arrow-backed, zero-copy)
df  = result.to_pandas()     # pandas.DataFrame   (Arrow-backed since pandas 2)
 
# Direct DuckDB interop:
import duckdb
con = duckdb.connect()
con.register("players", tbl)
con.execute("SELECT count(*) FROM players WHERE jersey > 50").fetchone()

Column types are honoured exactly — a Player whose name is "42" stays a String, not coerced to int.

Per-column type mapping:

Cypher / Engine typeArrow DataType
IntInt64
FloatFloat64
StringUtf8
BoolBoolean
[Int…]List<Int64>
[Float…]List<Float64>
[String…]List<Utf8>
Node ID / Rel IDUInt64
Point / PolygonUtf8 (geometry as WKT)

Snapshot-pinned reads — db.query_at(query, seq)#

Run a query against a specific past WAL sequence. Bit-for-bit deterministic replay of the world model at any point in its history.

# Capture the seq before running a decision loop
anchor_seq = db.execute("RETURN walSeq() AS s").one()["s"]
 
# … mutations happen …
 
# Replay the world as it looked when the decision was made
result = db.query_at(
    "MATCH (a:Account {id: 'ACC-789'}) RETURN a.balance, a.risk_score",
    seq=anchor_seq,
)

Equivalent to inlining AS OF seq <seq> in the Cypher, with the AS-OF mechanics kept out of caller code.

Live subscriptions — db.subscribe(view_name)#

Push-based subscription to a CREATE LIVE VIEW. The engine runs the standing query incrementally and pushes {added, removed, seq} events to the subscriber.

db.execute("""
    CREATE LIVE VIEW high_conf AS
    MATCH (n:Detection) WHERE n.confidence > 0.9
    RETURN n.id AS id, n.confidence AS conf
""")
 
with db.subscribe("high_conf") as sub:
    for event in sub:                  # blocks until next frontier advance
        for row in event["added"]:
            handle_new_detection(row)
        for row in event["removed"]:
            retract(row)

Each subscription owns a background poll thread; closing the subscription (or exiting the context manager) shuts it down deterministically.

CDC stream — db.changes_since(seq, limit=…)#

Return mutations recorded after a given WAL sequence. Use it to drive a pull-based change-data-capture loop without taking on a separate CDC service.

last_seq = 0
while True:
    changes = db.changes_since(last_seq, limit=1_000)
    if not changes:
        time.sleep(0.5)
        continue
    publish(changes)
    last_seq = max(int(c["sequence"]) for c in changes)

Pair with db.fingerprint() (content hash) and db.snapshot_uri() (content-addressed snapshot URI) when downstream consumers need to verify the mutations they applied match the source state exactly.

Event bus — arcflow.bus.Bus#

arcflow.bus.Bus exposes the engine's in-process pub/sub + request-reply surface to Python. Topics, durable consumers, NATS-style wildcard subscriptions, and synchronous request-reply all reach the same engine that db.execute() writes to. See Event bus for the full semantics.

from arcflow import ArcFlow
from arcflow.bus import Bus, EventPublisher, START_FROM_BEGINNING
 
db = ArcFlow()
bus = Bus(db)
 
bus.create_topic("orders.us")
seq = bus.publish("orders.us", {"order_id": 42, "amount": 100.0})
 
# Durable consumer — pull-style, per-consumer offset
bus.register_consumer("billing", "orders.us", start=START_FROM_BEGINNING)
for event in bus.pending("billing", "orders.us"):
    process(event.data)
    bus.ack("billing", "orders.us", event.seq)
 
# Wildcard consumer — fans out across every matching topic
tracks = bus.register_pattern_consumer("worker", "all_tracks.*")
for topic, event in tracks.drain():
    handle(topic, event.data)
 
# Synchronous request-reply
reply = bus.request("video.query.status", {"q": "n_cams"}, timeout_ms=5_000)
print(reply.data["count"])

EventPublisher is the application-layer convenience for rate-limited, per-camera, severity-tagged event streams — mirrors oz_core.events.EventPublisher for services migrating from NATS-and-Memgraph stacks:

pub = EventPublisher(bus, topic_prefix="framing.alert")
pub.set_rate_limit("ball_lost", interval_ms=10_000)
pub.emit("ball_lost", {"conf": 0.2}, camera="cam1", severity="warning")
# -> publishes to framing.alert.warning.ball_lost.cam1 (auto-creates the topic)

Repositories — arcflow.repository.BaseRepository#

A drop-in shape for services migrating off the Memgraph Bolt driver. Subclass and add domain methods that call self.execute(...) / self.execute_rows(...) / self.execute_scalar(...). Constructor takes data_dir=None (in-memory) or a path (persistent) — no host / port, no connection pool, no BAD_SESSION reconnection class because the engine is same-process FFI.

from arcflow.repository import BaseRepository, RepositoryTimeoutError
 
class PlayerRepo(BaseRepository):
    def get(self, player_id: int) -> dict | None:
        return self.execute_one(
            "MATCH (p:Player {id: $id}) RETURN p",
            {"id": player_id},
        )
 
with PlayerRepo("/var/lib/arcflow") as repo:
    alice = repo.get(42)

execute(..., timeout_s=5.0) enforces a per-call deadline via a worker-thread join; exceeding the deadline raises RepositoryTimeoutError. Pass timeout_s=None to disable per-call timeouts. Multiple repositories can share one open ArcFlow via the shared_db= constructor argument.

Streaming functions — arcflow.stream_fn#

Per-stream stateful scalar functions (lag, lead, delta) for collapsing per-step-derivative patterns. State is held in the engine, not in the calling service — register_stream_fn + feed_stream_fn return immediately with whatever output the function produces for that feed (zero-or-one entries per feed).

from arcflow import ArcFlow
from arcflow.stream_fn import StreamFn, Kind
 
db = ArcFlow()
pan_delta = StreamFn(db, "pan_delta", Kind.Delta)
ts_delta  = StreamFn(db, "ts_delta",  Kind.Delta)
 
for pan, ts in samples:
    pan_out = pan_delta.feed(pan)
    ts_out  = ts_delta.feed(ts)
    if pan_out and ts_out:
        pan_rate = pan_out[0].fn_value / ts_out[0].fn_value

See Streaming functions for the full semantics (Lag(n), Lead(n), and Delta).

Threading concurrency — concurrent.futures works#

ctypes releases the Python GIL for every native call; ArcFlow's ConcurrentStore is MVCC-safe for parallel readers. Multiple Python threads on one ArcFlow instance run in real parallel rather than serialized:

from concurrent.futures import ThreadPoolExecutor
 
queries = [
    "MATCH (p:Person) WHERE p.age > 30 RETURN count(*)",
    "MATCH (p:Player) RETURN avg(p.jersey)",
    # ...
]
with ThreadPoolExecutor(max_workers=8) as pool:
    results = list(pool.map(db.execute, queries))   # reads — lock-free

Reads parallelize cleanly via ArcSwap snapshots — no coordination needed. Writes through a single handle are guarded: a second write on the same handle while one is in flight returns HANDLE_BUSY_CONCURRENT_WRITER immediately (rather than queueing through the write mutex and regressing 10× silently). The two supported parallel-write patterns are multiple handles (arcflow.sharded) and threading.Lock() around a single handle.

See Threading Model for the complete read/write matrix, the typed-error payload, and substrate detail (writer_busy: AtomicBool + RAII WriterClaim guard).

Sharded engine (arcflow.sharded)#

For parallel writes across independent partitions, open a sharded engine — N independent store handles fronted by partition-key routing, so writes to different shards never contend:

from arcflow.sharded import ArcflowSharded
 
eng = ArcflowSharded("/data/chetak", shard_count=5, partition_key="camera_id")
shard = eng.shard("cam-03")          # ShardHandle — routed by partition key
shard.execute("CREATE (f:Frame {camera_id: 'cam-03', ts: 1024})")
shard.close()
  • ArcflowSharded(data_dir, shard_count, partition_key) — open a sharded engine; rows route to a shard by partition_key.
  • ShardHandle — a per-shard handle (.shard_id, .execute(query), .close()).
  • ArcflowShardedError(status, context) — raised on routing/handle errors; status is one of the SHARDED_* codes: SHARDED_OK (0), INVALID_UTF8 (1), ZERO_SHARDS (2), PARTITION_KEY_MISMATCH (3), UNKNOWN_SHARD (4), STALE_FFI_HANDLE (5), HANDLE_CLOSED (6), NULL_POINTER (7), OPEN_FAILED (8).

Result column types#

QueryResult.column_type(i) returns an integer tag for column i, matched against the TYPE_* constants:

from arcflow import TYPE_INT, TYPE_POINT, TYPE_STRING
 
res = db.execute("MATCH (s:Sensor) RETURN s.id, s.loc")
if res.column_type(1) == TYPE_POINT:
    ...

TYPE_UNKNOWN (-1), TYPE_NULL (0), TYPE_BOOL (1), TYPE_INT (2), TYPE_FLOAT (3), TYPE_STRING (4), TYPE_NODE_ID (5), TYPE_REL_ID (6), TYPE_INT_LIST (7), TYPE_FLOAT_LIST (8), TYPE_STRING_LIST (9), TYPE_POINT (10), TYPE_EXTENT (13), TYPE_LINESTRING (14), TYPE_POLYGON (15).

TypeScript / Node.js#

napi-rs native addon. The Rust engine runs in-process inside Node.js — function calls, not HTTP, no serialization, microsecond latency.

import { open, openInMemory, ArcflowError } from '@ozinc/arcflow'
 
// In-memory (testing, short-lived)
const db = openInMemory()
 
// Persistent (WAL-journaled, survives crashes)
const db = open('./data/graph')
 
// Queries return typed values — numbers are numbers, not strings
db.mutate("CREATE (n:Person {name: 'Alice', age: 30})")
const result = db.query("MATCH (n:Person) RETURN n.name, n.age")
result.rows[0].get('name')  // "Alice"
result.rows[0].get('age')   // 30 (number, not "30")
 
// Parameters (prevent injection)
db.query("MATCH (n:Person {name: $name}) RETURN n", { name: 'Alice' })
 
// Atomic batch mutations
db.batchMutate([
  "MERGE (a:Person {id: 'p1', name: 'Alice'})",
  "MERGE (b:Org {id: 'o1', name: 'Acme'})",
])
 
// Paginated cursor for large result sets
const cursor = db.cursor('MATCH (n:Log) RETURN n.ts ORDER BY n.ts', undefined, 500)
let page
while ((page = cursor.next()) !== null) process(page.rows)
cursor.close()
 
// Live subscriptions — callback fires with added/removed events on each relevant mutation
const sub = db.subscribe(
  'MATCH (n:Alert) WHERE n.level = "critical" RETURN n.id, n.message',
  ({ added, removed }) => console.log('new alerts', added)
)
// later:
sub.cancel()
 
// Sync
db.syncPending()    // 0 if up to date
db.fingerprint()    // hash of current state
 
// Error handling
try { db.query("INVALID") } catch (e) {
  if (e instanceof ArcflowError) console.log(e.code, e.category, e.suggestion)
}
 
db.close()

Sync, mesh & workspaces#

Beyond open/execute, the napi addon exposes the engine's sync, mesh, workspace, and live-view surfaces as direct methods. They all run in-process — no daemon required for the local operations.

// Pull/push deltas between peers
const manifest = db.sync_export()                 // local change manifest
db.sync_import(peerDelta)                          // apply a peer's delta
db.sync_ack(seq)                                   // advance the cursor
const open = db.sync_conflicts()                   // unresolved conflicts
FamilyMethods
Syncsync_export · sync_import · sync_ack · sync_status · sync_pending · sync_conflicts · sync_manifest · sync_manifest_diff · sync_set_epoch · sync_snapshot_export · sync_snapshot_import
Scopessync_add_scope · sync_remove_scope · sync_list_scopes · sync_export_scope · sync_scope_fingerprint
Meshsync_discover · sync_announce · sync_mesh_health · sync_with_cloud · sync_update_peer · start_sync_daemon
Workspacescreate_workspace · drop_workspace · list_workspaces · workspace_count · workspace_stats
Live viewssubscribe_live_view · unsubscribe_live_view · poll_live_view · poll_live_view_delta · list_live_views · live_view_status

For the wire-protocol form of these (when driving the engine remotely over a socket), see JSON-RPC v1.

Install#

See the Installation page for the manifest-driven install matrix. The TypeScript binding ships in the same arcflow package — pick the npm row in the matrix above.

React#

React hooks for live graph data in components. @arcflow/react ships alongside arcflow; install both per the Installation page.

import { openInMemory } from '@ozinc/arcflow'
import { useQuery, useLiveQuery } from '@arcflow/react'
 
const db = openInMemory()
db.mutate("CREATE (n:Alert {level: 'critical', message: 'Disk 95%'})")
 
function AlertPanel() {
  // One-time query
  const { data, loading, error } = useQuery(db, 'MATCH (n:Alert) RETURN n.level, n.message')
 
  // Live subscription — re-renders on every graph mutation that affects results
  const { rows } = useLiveQuery(db, 'MATCH (n:Alert) WHERE n.level = "critical" RETURN n.message')
 
  if (loading) return <div>Loading...</div>
  return (
    <ul>
      {rows?.map((row, i) => <li key={i}>{String(row.message)}</li>)}
    </ul>
  )
}

| Hook | Returns | Notes | |---

------
----
useQuery(db, query, params?, deps?){ data, loading, error }
useLiveQuery(db, query, deps?, pollIntervalMs?){ rows, loading, error }

C ABI#

The foundation all bindings build on. Stable extern "C" interface with opaque handles.

Header: arcflow.h#

#include "arcflow.h"
 
// Open runtime (NULL for in-memory, or path for persistent)
arcflow_runtime_t* rt = arcflow_open(NULL);
 
// Open session
arcflow_session_t* session = arcflow_session_open(rt);
 
// Execute query
arcflow_result_t* result = arcflow_execute(session, "MATCH (n) RETURN n.name");
 
// Read results
int64_t rows = arcflow_result_row_count(result);
int64_t cols = arcflow_result_column_count(result);
 
for (int64_t r = 0; r < rows; r++) {
    const char* name = arcflow_result_get_string(result, r, 0);
    printf("%s\n", name);
}
 
// Cleanup
arcflow_result_free(result);
arcflow_session_close(session);
arcflow_close(rt);
 
// Error handling
const char* err = arcflow_last_error();

Functions#

| Function | Description | |--- -------|---#

----| | arcflow_open(path) | Open runtime. NULL = in-memory | | arcflow_close(rt) | Close runtime and free memory | | arcflow_session_open(rt) | Open lightweight session on runtime | | arcflow_session_close(session) | Close session | | arcflow_execute(session, query) | Execute WorldCypher query | | arcflow_result_row_count(result) | Number of result rows | | arcflow_result_column_count(result) | Number of result columns | | arcflow_result_column_name(result, idx) | Column name by index | | arcflow_result_get_string(result, row, col) | Cell value as string | | arcflow_result_free(result) | Free result memory | | arcflow_last_error() | Last error message (thread-local) | | arcflow_version() | ArcFlow version string. See reference/versioning |

The table above is the essential subset. The shipped arcflow.h declares the complete arcflow_* export surface — runtime/session lifecycle, sharded handles, pub/sub, virtual-partition registration, typed result accessors, and the ARCFLOW_TYPE_* / TRANSPORT_LANE_* constants. Header coverage is checked in CI against the engine's exported symbols, so arcflow.h never drifts from what the library actually exports — treat the header itself as the canonical, exhaustive C reference.

C++#

Header-only RAII wrapper over the C ABI. Include arcflow.hpp and link with -larcflow.

#include "arcflow.hpp"
 
int main() {
    // RAII: runtime auto-freed on scope exit
    arcflow::Runtime rt;                     // in-memory
    // arcflow::Runtime rt("/path/to/data"); // persistent
 
    auto session = rt.session();
 
    session.execute("CREATE (n:Person {name: 'Alice'})");
    auto result = session.execute("MATCH (n:Person) RETURN n.name");
 
    for (int64_t r = 0; r < result.row_count(); ++r) {
        std::cout << result.get(r, 0) << std::endl;  // "Alice"
    }
    // result, session, rt auto-freed on scope exit
}

Build#

g++ -std=c++17 \
    -I include/arcflow \
    main.cpp \
    -L /path/to/arcflow/lib -larcflow \
    -o myapp

MCP Server#

For cloud chat interfaces (ChatGPT, Claude.ai, Gemini web) that have no local shell. See MCP Server for setup, tools, and configuration.

Architecture#

All bindings target one engine. Pick the layer that matches your use case:

                        ArcFlow Rust engine
                               │
                    C ABI  (libarcflow.so)
                               │
           ┌───────────────────┼──────────────────────┐
           │                   │                       │
       Python              TypeScript / React        C++
     (ctypes)        (napi-rs — in-process)       (arcflow.hpp)
                                │
                              WASM
                      (browser / edge — zero-copy)

One engine. No protocol translation. Function calls go directly into the engine. For the browser, the WASM build runs the same engine via the WASM memory model.

WASM API (browser / edge)#

The WASM build (@ozinc/arcflow-wasm) runs the full engine in the browser or an edge worker. Every entry point is a plain function; query/result payloads are JSON strings.

import init, { execute, stats, snapshot, restore } from '@ozinc/arcflow/wasm'
 
await init()                                  // initialize the engine (call once)
const res = execute('MATCH (n) RETURN count(*)')   // → JSON string result envelope
const s   = stats()                           // → JSON store stats
const snap = snapshot()                        // → JSON full-graph snapshot
restore(snap)                                  // restore from a snapshot JSON
reset()                                        // clear the graph
FunctionPurpose
init()Initialize the engine (once, before any call).
execute(query) → JSONRun a WorldCypher query; returns the result envelope as a JSON string.
reset() → JSONDrop all nodes/relationships.
stats() → JSONStore statistics.
snapshot() / restore(json) → JSONExport / restore the full graph as JSON.

Multi-workspace (isolated graphs in one WASM instance):

create_workspace('tenant-a')
execute_in('tenant-a', 'CREATE (n:Doc {id: 1})')
list_workspaces()                              // → JSON list
snapshot_workspace('tenant-a')                 // → JSON; restore_workspace('tenant-a', json)
drop_workspace('tenant-a')

create_workspace(id) / drop_workspace(id) / list_workspaces() / execute_in(workspace_id, query) / snapshot_workspace(workspace_id) / restore_workspace(workspace_id, json) — each returns a JSON string.


See Also#

  • Installation — pre-built binaries for all platforms
  • Platform — runtime environments: browser WASM, Cloudflare Workers, mobile
  • Agent-Native Database — integration surfaces: napi-rs, CLI binary, MCP server
  • MCP Server — cloud chat UI integration (the fourth binding surface)
Try it
Open ↗⌘↵ to run
Loading engine…
← PreviousInstallationNext →Platforms