Getting Started
MeshDB is a single static binary. Install it, point a Bolt driver at it, and you have a Cypher-speaking graph database running locally in about a minute.
This page walks through:
- Installing
meshdb-server - Starting a single-node server
- Connecting with the official Neo4j Python driver
- Running your first Cypher query
For cluster mode, authentication, and TLS, see the project README.
Prerequisites
If you’re using the Docker image (see below), you only need a working Docker install — skip ahead to Install.
For a cargo install or build-from-source path:
Rust toolchain (stable) — the easiest install path is
rustup.clang/libclang-devon Linux.rust-rocksdbgenerates its bindings at compile time. On Debian / Ubuntu:sudo apt-get install clang libclang-devOn Fedora:
dnf install clang clang-devel. On macOS the Xcode command line tools already ship what’s needed.
protoc is not required — meshdb-rpc/build.rs uses a vendored
binary.
Install
Option 1: cargo install
cargo install meshdb-server
The binary lands in ~/.cargo/bin/meshdb-server. Check the install:
meshdb-server --version
Option 2: Docker
A prebuilt image is published on Docker Hub as
darkspar/meshdb-server.
Pull the version-pinned tag:
docker pull darkspar/meshdb-server:0.2.0
:latest tracks the most recent release; pin to a specific tag like
:0.2.0 for reproducible deployments. The image runs meshdb-server
as its entrypoint, so any flag or MESHDB_* env var documented below
works inside the container.
Option 3: Build from source
Clone the repo and run:
git clone https://github.com/mesh-db/meshdb.git
cd mesh
cargo build -p meshdb-server --release
The binary is then at ./target/release/meshdb-server.
Run a single-node server
The smallest useful configuration is a single node with a gRPC listener and a Bolt listener. You can provide settings via a TOML file, CLI flags, or environment variables — pick whichever fits your workflow.
Option A: TOML config
Save as /tmp/mesh.toml:
self_id = 1
listen_address = "127.0.0.1:7001"
data_dir = "/tmp/mesh-data"
bolt_address = "127.0.0.1:7687"
Start the server:
RUST_LOG=info meshdb-server --config /tmp/mesh.toml
Option B: CLI flags
RUST_LOG=info meshdb-server \
--self-id 1 \
--listen-address 127.0.0.1:7001 \
--bolt-address 127.0.0.1:7687 \
--data-dir /tmp/mesh-data
Option C: Environment variables
Every flag has a matching MESHDB_* env var:
MESHDB_SELF_ID=1 \
MESHDB_LISTEN_ADDRESS=127.0.0.1:7001 \
MESHDB_BOLT_ADDRESS=127.0.0.1:7687 \
MESHDB_DATA_DIR=/tmp/mesh-data \
RUST_LOG=info meshdb-server
When --config and flags are both present, the TOML file is loaded first
and any set flags override the matching fields. Structured settings
(peers, bolt_auth, bolt_tls, grpc_tls) stay TOML-only.
Run meshdb-server --help for the full flag list.
Option D: Docker
The same env vars work directly with the published image. Bind the
listeners to 0.0.0.0 inside the container so Docker’s port forwarding
can reach them, and mount a volume for MESHDB_DATA_DIR so your graph
survives container restarts:
docker run --rm \
-p 7001:7001 -p 7687:7687 \
-v meshdb-data:/data \
-e MESHDB_SELF_ID=1 \
-e MESHDB_LISTEN_ADDRESS=0.0.0.0:7001 \
-e MESHDB_BOLT_ADDRESS=0.0.0.0:7687 \
-e MESHDB_DATA_DIR=/data \
-e RUST_LOG=info \
darkspar/meshdb-server:0.2.0
To use a TOML config instead, mount it into the container and pass
--config:
docker run --rm \
-p 7001:7001 -p 7687:7687 \
-v meshdb-data:/data \
-v /tmp/mesh.toml:/etc/meshdb/mesh.toml:ro \
darkspar/meshdb-server:0.2.0 --config /etc/meshdb/mesh.toml
Remember that any listen_address / bolt_address set in the TOML must
also bind to 0.0.0.0 (or the container IP) for the published ports to
be reachable from the host.
Whichever option you pick, you should see:
INFO meshdb_server: starting meshdb-server self_id=1 listen_address=127.0.0.1:7001 data_dir=/tmp/mesh-data peers=0
INFO meshdb_server: meshdb-server listening addr=127.0.0.1:7001
INFO meshdb_server: meshdb-server bolt listening addr=127.0.0.1:7687
Leave it running. Ctrl-C to stop. Delete /tmp/mesh-data between
runs if you want a clean slate.
7001 is the gRPC port (cluster-internal, client reads and writes via
the gRPC surface when you skip Bolt). 7687 is the standard Neo4j Bolt
port — every driver defaults to it.
Connect from a Bolt client
Any Neo4j-compatible driver works. The official Python driver is the quickest way to verify the server:
python -m pip install --user neo4j
Save as /tmp/mesh_demo.py:
from neo4j import GraphDatabase
# In the default config auth is accepted-but-ignored — any credentials work.
driver = GraphDatabase.driver("bolt://127.0.0.1:7687", auth=("any", "any"))
with driver.session() as s:
# Auto-commit with parameters.
s.run("CREATE (n:Person {name: $name, age: $age})", name="Ada", age=37)
s.run("CREATE (n:Person {name: $name, age: $age})", name="Grace", age=85)
# Parameterized read.
result = s.run(
"MATCH (n:Person) WHERE n.age > $min RETURN n.name AS name, n.age AS age ORDER BY age",
min=40,
)
for record in result:
print(record["name"], record["age"])
# Explicit transaction — both creates land atomically on commit.
with s.begin_transaction() as tx:
tx.run("CREATE (n:Project {title: $title})", title="mesh")
tx.run("CREATE (n:Project {title: $title})", title="bolt-listener")
# UNWIND with a list parameter — canonical batch idiom.
s.run("UNWIND $items AS x CREATE (:Tag {name: x})",
items=["rust", "graph", "cypher"])
print("--- all tags ---")
for r in s.run("MATCH (n:Tag) RETURN n.name AS name ORDER BY name"):
print(r["name"])
driver.close()
Run it:
python /tmp/mesh_demo.py
Expected output:
Grace 85
--- all tags ---
cypher
graph
rust
That single script exercises the Bolt handshake, PackStream encoding,
parameter binding, pattern-property parameters, explicit transactions,
and UNWIND $list — the full driver-facing surface.
Your first graph in Cypher
Inside any session you can write regular openCypher. A minimal social graph and a reverse-lookup query:
CREATE (a:Person {name: "Ada"})-[:KNOWS]->(b:Person {name: "Grace"})
RETURN a, b;
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name AS person, collect(friend.name) AS friends
ORDER BY person;
MeshDB supports a broad openCypher surface: MATCH / OPTIONAL MATCH,
WHERE, RETURN (with DISTINCT), WITH, ORDER BY / LIMIT / SKIP,
UNION, CREATE, MERGE (with ON CREATE SET / ON MATCH SET),
SET, REMOVE, DELETE / DETACH DELETE, variable-length paths,
Neo4j 5 quantifier shorthand (->+, ->*, ->{n,m}), shortestPath /
allShortestPaths, list comprehensions, pattern comprehensions, CASE,
EXISTS { ... },
COUNT { ... }, COLLECT { ... }, CALL { ... } subqueries,
UNWIND, FOREACH, LOAD CSV — plus the full scalar-function and
aggregate surface you’d expect from Neo4j.
Schema. CREATE INDEX / DROP INDEX / SHOW INDEXES for node
(FOR (n:Label) ON (n.a, n.b)) and relationship
(FOR ()-[r:TYPE]-() ON (r.p)) scopes, including composite keys that
the planner rewrites pattern-property equalities and WHERE conjuncts
against. CREATE POINT INDEX backs point.withinbbox and
point.distance queries on Property::Point columns with a Z-order
(Morton) quantizer — Cartesian and WGS-84 (geographic) coordinates
both index. CREATE CONSTRAINT / DROP CONSTRAINT /
SHOW CONSTRAINTS for UNIQUE, NOT NULL, IS :: <TYPE>, and
composite IS NODE KEY, on node or relationship scope. DDL replicates
across Raft and routing clusters.
Transactions. Bolt BEGIN / COMMIT / ROLLBACK are fully wired
— multi-statement transactions accumulate their writes and commit
atomically through the same single-node / Raft / routing-2PC
machinery as auto-commit RUNs, with read-your-writes overlay between
RUNs in the same transaction. For large bulk writes that shouldn’t
land in one transaction,
CALL { ... } IN TRANSACTIONS [OF n ROWS] [ON ERROR { FAIL | CONTINUE | BREAK }] splits a stream across independently-committed batches.
APOC. MeshDB ships an APOC-compatible surface in the
meshdb-apoc crate, enabled by default. Included today:
apoc.coll.*, apoc.text.*, apoc.map.*, apoc.util.* (hashes),
apoc.convert.* (JSON), apoc.date.*, apoc.number.*,
apoc.create.* (both scalars and write procedures),
apoc.refactor.setType, apoc.meta.*, apoc.agg.* aggregates,
apoc.path.* (expand, expandConfig, subgraphAll,
subgraphNodes, spanningTree, plus path shaping scalars),
apoc.cypher.run / apoc.cypher.doIt,
apoc.load.{json,csv} and apoc.export.{csv,json,cypher}.{all,query}
(gated through an [apoc_import] security surface),
apoc.trigger.* (install / drop / list / start / stop,
all four phases, replicated across Raft / routing), and
apoc.periodic.iterate(iterateQuery, actionQuery, config) as a
planner-level rewrite over the batched-commit dispatcher. Embed
callers who want a slimmer binary can opt out per-namespace with
--no-default-features.
For the complete feature inventory and known limitations, see What works in the project README.
Next steps
- Lock down the Bolt listener. The default accepts any credentials —
fine for local dev, not for anything shared. Add a
[[bolt_auth.users]]block (plain text or bcrypt) to require authentication. - Add TLS. Both the Bolt listener
(
[bolt_tls]) and the cluster-internal gRPC surface ([grpc_tls]) can terminate TLS directly. - Go multi-peer. Set
mode = "raft"for a fully-replicated cluster,mode = "routing"for hash-partitioned sharding with 2PC, ormode = "multi-raft"for sharding with per-partition replication. See Cluster mode for worked three-peer configs. - Deploy to Kubernetes. The
deploy/tree ships a StatefulSet, headless + Bolt services, a PodDisruptionBudget, Prometheus alerts, and a Grafana dashboard, plus a day-2RUNBOOK.mdfor rolling upgrades, backup/restore, TLS rotation, and add/remove-peer flows. The reference image isdarkspar/meshdb-server:0.2.0. - Browse the source. MeshDB is MIT-licensed on GitHub. Issues and discussions are welcome.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.