{"id":50658305,"url":"https://github.com/namidb/namidb","last_synced_at":"2026-06-13T19:01:07.681Z","repository":{"id":358722862,"uuid":"1242792159","full_name":"namidb/namidb","owner":"namidb","description":"Graph database native to the cloud. Embedded, multi-tenant, built on object storage.","archived":false,"fork":false,"pushed_at":"2026-06-07T23:55:24.000Z","size":2872,"stargazers_count":104,"open_issues_count":8,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-06-08T01:08:58.025Z","etag":null,"topics":["agent-memory","columnar","cypher","gql","graph-database","graphrag","lsm-tree","object-storage","rust","s3"],"latest_commit_sha":null,"homepage":"https://namidb.com","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/namidb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T19:00:51.000Z","updated_at":"2026-06-07T23:51:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/namidb/namidb","commit_stats":null,"previous_names":["namidb/namidb"],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/namidb/namidb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namidb%2Fnamidb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namidb%2Fnamidb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namidb%2Fnamidb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namidb%2Fnamidb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/namidb","download_url":"https://codeload.github.com/namidb/namidb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namidb%2Fnamidb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34296383,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-memory","columnar","cypher","gql","graph-database","graphrag","lsm-tree","object-storage","rust","s3"],"created_at":"2026-06-08T01:04:34.820Z","updated_at":"2026-06-13T19:01:07.666Z","avatar_url":"https://github.com/namidb.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cp\u003e\n  \u003cimg src=\".assets/namidb-logo.jpeg\" alt=\"NamiDB\" width=\"640\" /\u003e\n\u003c/p\u003e\n\n# NamiDB\n\n### A graph database that lives in your S3 bucket.\n\nIt embeds like DuckDB, runs as a standalone HTTP server, or sits on our hosted cloud. Same engine in all three, and the bucket is always the source of truth.\n\n[![License: BSL 1.1](https://img.shields.io/badge/License-BSL%201.1-1f6feb.svg)](LICENSE)\n[![Rust](https://img.shields.io/badge/Rust-1.85%2B-dea584.svg?logo=rust\u0026logoColor=white)](https://www.rust-lang.org)\n[![PyPI](https://img.shields.io/badge/PyPI-namidb-3776ab.svg?logo=pypi\u0026logoColor=white)](https://pypi.org/project/namidb/)\n[![Docker](https://img.shields.io/badge/Docker-namidb--server-2496ed.svg?logo=docker\u0026logoColor=white)](crates/namidb-server/Dockerfile)\n[![Website](https://img.shields.io/badge/Website-namidb.com-0a7ea4.svg)](https://namidb.com)\n[![Docs](https://img.shields.io/badge/Docs-docs.namidb.com-0a7ea4.svg)](https://docs.namidb.com)\n\n[**Website**](https://namidb.com) · [**Documentation**](https://docs.namidb.com) · [**RFCs**](./docs/rfc/) · [**Request early access**](https://namidb.com)\n\n\u003c/div\u003e\n\n---\n\nNamiDB is a graph database engine built around object storage. You write Cypher, it lays your nodes and edges out as columnar files in a bucket, and that bucket is the only source of truth. There's nothing else to run and nothing to coordinate outside the bucket itself. The same engine ships three ways: embedded as a library, as an HTTP server, or on our hosted cloud.\n\n\u003cbr /\u003e\n\n## Why now\n\nA few things had to line up before this made sense.\n\n**S3 finally got conditional writes.** In 2024, S3 shipped `If-Match` and `If-None-Match`, which was the last primitive we were missing. With compare-and-swap on the bucket you can build a coordinated, durable system where object storage *is* the database. There's no Raft, no ZooKeeper, no etcd in the picture. People had already pulled this off for vectors, for queues, for analytics. Nobody had done it for graphs.\n\n**The best columnar graph engine left the field.** Apple bought Kùzu in October 2025 and archived the repo. It was the most carefully thought-out columnar graph engine anyone had published, and it just went quiet. That left a hole.\n\n**Agents need graphs.** Vector search is necessary but it isn't enough. Knowledge graphs are what agent memory, deep retrieval, and reasoning under uncertainty actually sit on once you're past the demo. A lot of the interesting AI work this decade is going to be about relationships, not just embeddings.\n\nSo that's what we're building.\n\n\u003cbr /\u003e\n\n## Where this came from\n\nNamiDB started inside LESAI as the graph database behind a hosted product we're building. We've been at it for about a year now, and every Cypher query, every manifest CAS, every CSR adjacency table in here has been run against real workloads, not just unit tests.\n\nWe're open-sourcing the engine now because two things finally lined up:\n\n1. Apple archived Kùzu in October 2025, so the columnar property-graph space lost its one maintained option. We'd independently landed on more or less the design Kùzu pioneered, so putting NamiDB out there felt like the most useful thing we could do about that gap.\n2. Our own roadmap moved to a hosted product, [NamiDB Cloud](https://namidb.com), which is multi-tenant and scales to zero per namespace. The engine doesn't need to be a competitive secret anymore. The engine is open, the cloud is the business.\n\n\u003cbr /\u003e\n\n## The shape\n\n**NamiDB writes Cypher to your S3 bucket.**\n\nThere's no control plane to provision, no Raft to tune, no etcd to babysit. Conditional writes (`If-Match` / `If-None-Match`) on the bucket take the place of a consensus tier, so the bucket itself holds the truth. Your graph database is just files: durability is whatever S3, R2, GCS or Azure already give you, cost drops to zero when nobody is querying, a backup is `aws s3 sync`, and a tenant is a folder.\n\nThe engine is the same whether you run it as a library inside your app, as a Rust daemon over HTTP, or on our hosted cloud. It works just as well against AWS S3, Cloudflare R2, GCS, Azure Blob, MinIO, or your local disk.\n\n\u003cbr /\u003e\n\n## Three deployments, one engine\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\".assets/namidb-deployments-dark.svg\" /\u003e\n    \u003cimg src=\".assets/namidb-deployments.svg\" alt=\"NamiDB deployments: Server, Embedded, Cloud, converging on a single object-storage bucket\" width=\"900\" /\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n| Mode | Status | Best for | How it ships |\n|---|---|---|---|\n| **Server** | 🧪 alpha (v0.1) | Self-hosted over your own S3 / R2 / GCS / Azure bucket | `namidb-server` binary + `Dockerfile` |\n| **Embedded** | 🧪 alpha (v0.1) | Notebooks, single-process apps, local dev, CI fixtures | `pip install namidb`, talks to a bucket from inside your process |\n| **Cloud** | 🔒 closed beta | Multi-tenant SaaS, agent memory, scale-to-zero per namespace | Managed by LESAI on namidb.com, [request access](https://namidb.com) |\n\nIt's the same engine across all three. Server and Embedded write to an identical bucket layout, so you can open an embedded notebook against the exact `s3://...` URI a `namidb-server` daemon is serving.\n\nNamiDB is pre-1.0 and alpha: the engine has run inside LESAI for about a year, but it has no external production users yet, several production concerns are still in progress (broad authz, backup/restore), and it is not yet a drop-in for critical data. Run it over a bucket you own, keep backups (`aws s3 sync`), and treat 0.x as the moving target it is.\n\n\u003cbr /\u003e\n\n## What's in the engine today\n\n- **Cypher and GQL parsing.** A strict subset of GQL (ISO/IEC 39075:2024) plus openCypher 9. All 12 in-scope LDBC SNB Interactive Complex Read queries (IC01 through IC12) parse, plan and run end to end.\n- **Writes through Cypher.** `CREATE`, `MERGE`, `SET`, `DELETE`, `DETACH DELETE`, `REMOVE`. Durable on `commit_batch` (WAL append plus manifest CAS).\n- **Cost-based optimizer.** Predicate pushdown, projection pushdown, join reorder, hash-join conversion, hash semi-join (`EXISTS` decorrelation), Parquet row-group pruning. `EXPLAIN VERBOSE` prints the chosen plan with selectivity and cost annotations.\n- **Vectorized execution.** A morsel-driven executor with an optional factorized intermediate representation (RFC-017) for path-heavy queries.\n- **Columnar storage on object storage.** Parquet node SSTs, a custom edge-SST format with CSR adjacency (RFC-002), zstd compression, bloom filters, fence-pointer indices.\n- **Coordination-free correctness.** One writer per namespace, with epoch fencing via manifest CAS. Conditional writes (`If-Match`, `If-None-Match`) stand in for external consensus.\n- **Tiered caches.** A process-wide `AdjacencyCache` (CSR), a `NodeViewCache`, and an `SstCache` (decoded body, edge property streams, and the reader). Cross-snapshot reuse, `Arc`-shared and byte-budgeted.\n- **Six storage backends.** `memory://`, `file://` (with `flock`-based CAS), `s3://` (AWS S3, R2, MinIO, Tigris, LocalStack), `gs://`, `az://`.\n- **Python bindings.** `pip install namidb`. abi3 wheels for Linux (x86_64 and aarch64), macOS (arm64) and Windows (x86_64), with an sdist fallback everywhere else. Sync and async (`acypher`). Arrow, pandas and polars output.\n- **CLI.** `namidb parse`, `namidb explain --verbose`, `namidb run --store \u003curi\u003e` for ad-hoc query work against any backend.\n- **HTTP server.** The `namidb-server` binary, with bearer-token auth, a periodic flush loop, a lock-free `/v0/livez` liveness probe, Prometheus metrics at `/v0/metrics` plus a slow-query log, and a small REST API (`/v0/cypher`, `/v0/health`, `/v0/admin/flush`). Optional TLS on both the HTTP and Bolt listeners via `--tls-cert` / `--tls-key` (rustls).\n- **Bolt protocol.** Same `namidb-server` binary speaks Bolt 4.4 / 5.0 / 5.4 on an opt-in TCP listener (default 7687). Neo4j drivers connect over `bolt://host:7687` and run Cypher, verified end-to-end with the Python driver. The other language drivers (Java, JavaScript, .NET, Go, Rust) speak the same protocol but are not all exercised yet. The Cypher parser does not implement `CALL` / `SHOW` as general clauses, but the server answers the specific schema-introspection procedures Neo4j and Memgraph GUIs fire on connect (`db.labels`, `db.relationshipTypes`, `apoc.meta.*`, `meta_util.schema`, and the rest) over Bolt, so their schema panels populate, verified with G.V()/gdotv. Running those procedures over the HTTP or embedded APIs still hits the parser limitation. See [RFC-022](./docs/rfc/022-bolt-protocol.md).\n- **Vector search and embeddings.** `cosine_similarity`, `dot_product` and `euclidean_distance` builtins rank stored f32 vectors through the normal scan + `ORDER BY` + `LIMIT` path, so K-nearest-neighbour search is just Cypher. Loading a markdown vault (`namidb load-vault --embed`, or the MCP server) embeds each note so semantic search works over it. The default embedder is **local and lexical**: a dependency-free hashing embedder that matches shared vocabulary, not meaning. For meaning-level search, build with `--features remote-embedder` and set the `NAMIDB_EMBED_*` variables to use OpenAI, Voyage, Cohere, Gemini or Jina. A namespace must be embedded consistently; switching embedders means a re-embed. The scan is still flat (no ANN index yet) and vectors are stored uncompressed.\n- **Bench harness.** A synthetic, deterministic LDBC SNB Interactive harness under [`bench/`](./bench/).\n\n\u003cbr /\u003e\n\n## Quickstart\n\nTwo ways in. Same engine behind both.\n\n### Door 1: a real graph database in your S3 bucket\n\nThis is the headline use case. Point it at a bucket, write Cypher, and durability is whatever S3 already gives you.\n\n```bash\npip install namidb\nexport AWS_ACCESS_KEY_ID=AKIA...\nexport AWS_SECRET_ACCESS_KEY=...\n```\n\n```python\nimport namidb\n\n# Open (or bootstrap) the `prod` namespace on your bucket.\nclient = namidb.Client(\"s3://my-bucket/data?ns=prod\u0026region=us-east-1\")\n\nclient.cypher(\"CREATE (a:Person {name: 'Alice', age: 30})\")\nclient.cypher(\"CREATE (b:Person {name: 'Bob',   age: 25})\")\nclient.cypher(\n    \"MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) \"\n    \"CREATE (a)-[:KNOWS {since: 2020}]-\u003e(b)\"\n)\n\nresult = client.cypher(\n    \"MATCH (p:Person) WHERE p.age \u003e= $min RETURN p.name AS name, p.age AS age\",\n    params={\"min\": 18},\n)\nprint(result.to_pandas())\n```\n\nKill the process and start it again. Open a notebook on another machine pointed at the same URI. The graph is still there, because the bucket is the database.\n\n### Door 2: a 30-second taste, no credentials\n\nFor when you just want to poke at the engine before wiring up a bucket. In-process, ephemeral, zero setup:\n\n```python\nimport namidb\nclient = namidb.Client(\"memory://acme\")\nclient.cypher(\"CREATE (a:Person {name: 'Alice'})\")\nprint(client.cypher(\"MATCH (p:Person) RETURN p.name\").rows())\n```\n\nThe same handful of lines work against `file://`, `gs://`, `az://`, or any S3-compatible endpoint. Only the URI changes.\n\n\u003cbr /\u003e\n\n## Pick your storage backend\n\nThe URI tells the client which bucket and which namespace to use.\n\n| Scheme | Backend |\n|---|---|\n| `s3://\u003cbucket\u003e[/\u003cprefix\u003e]?ns=\u003cns\u003e` | **AWS S3, Cloudflare R2, MinIO, Tigris, LocalStack, anything S3-compatible** |\n| `gs://\u003cbucket\u003e?ns=\u003cns\u003e` | Google Cloud Storage |\n| `az://\u003caccount\u003e/\u003ccontainer\u003e?ns=\u003cns\u003e` | Azure Blob Storage |\n| `file:///abs/dir?ns=\u003cns\u003e` | Local filesystem (CAS via `flock` + atomic rename) |\n| `memory://\u003cns\u003e` | In-process and ephemeral, for testing only |\n\nEvery backend speaks the same Cypher, exposes the same Python, Rust and HTTP APIs, and gives you the same snapshot-isolated reads.\n\n**Durability.** A write is acknowledged once `commit_batch` has written its WAL segment and swung the manifest pointer on the backend. On the object stores (`s3://`, `gs://`, `az://`, and S3-compatibles like R2 or MinIO) that inherits the backend's own durability: once the PUT is acked, the write is on durable storage. The `file://` backend writes through the OS page cache and does **not** `fsync`, so a committed write survives a process crash but a kernel panic or power loss can lose the most recent un-flushed writes. Treat `file://` as a development and single-node store, not as the only copy of data you cannot lose; point production at an object store. `memory://` is not durable at all. Backup and restore a consistent snapshot of any namespace with `namidb backup` / `namidb restore`.\n\n### AWS S3 (the primary path)\n\n```python\nimport os\nos.environ[\"AWS_ACCESS_KEY_ID\"]     = \"AKIA...\"\nos.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"...\"\n\nclient = namidb.Client(\n    \"s3://my-bucket/data?ns=prod\"\n    \"\u0026region=us-west-2\"\n)\n```\n\nCredentials come from the standard AWS env vars (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, `AWS_DEFAULT_REGION`). IAM roles on EC2, EKS, Lambda and ECS just work, with no NamiDB-specific auth to wire up.\n\nThe only IAM permissions NamiDB needs on the bucket are `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` and `s3:ListBucket`. That's it. No DynamoDB lock table, no separate metadata service.\n\n### Cloudflare R2 (no egress fees)\n\nR2 charges nothing for egress and has full S3-compatible conditional writes. Same scheme, just point at the R2 endpoint with `region=auto`:\n\n```python\nimport os\nos.environ[\"AWS_ACCESS_KEY_ID\"]     = \"\u003cR2 access key\u003e\"\nos.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"\u003cR2 secret\u003e\"\n\nclient = namidb.Client(\n    \"s3://my-bucket?ns=prod\"\n    \"\u0026endpoint=https://\u003cACCOUNT_ID\u003e.r2.cloudflarestorage.com\"\n    \"\u0026region=auto\"\n)\n```\n\nIf you're running NamiDB anywhere outside AWS (Cloudflare Workers, Fly.io, your own VPS, your laptop), R2 is almost always the right call.\n\n### Other backends\n\nSame `namidb.Client(...)` call, just a different URI. Expand for the copy-paste credentials.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eGoogle Cloud Storage\u003c/strong\u003e (\u003ccode\u003egs://\u003c/code\u003e)\u003c/summary\u003e\n\n```python\nimport os\nos.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"] = \"/etc/gcs-key.json\"\nclient = namidb.Client(\"gs://my-bucket/data?ns=prod\")\n```\n\nYou can also pass the service-account path in the URI:\n`gs://my-bucket?ns=prod\u0026service_account=/etc/gcs-key.json`.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAzure Blob Storage\u003c/strong\u003e (\u003ccode\u003eaz://\u003c/code\u003e)\u003c/summary\u003e\n\n```python\nimport os\nos.environ[\"AZURE_STORAGE_ACCOUNT_NAME\"] = \"myacct\"\nos.environ[\"AZURE_STORAGE_ACCESS_KEY\"]   = \"...\"\nclient = namidb.Client(\"az://myacct/mycontainer?ns=prod\")\n```\n\nFor Azurite (the local emulator) tack on `\u0026use_emulator=true`.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eMinIO\u003c/strong\u003e (self-hosted S3), \u003ccode\u003es3://\u003c/code\u003e with an \u003ccode\u003eendpoint=...\u003c/code\u003e\u003c/summary\u003e\n\n```bash\ndocker run -d --rm -p 9000:9000 -p 9001:9001 \\\n  -e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin \\\n  --name minio minio/minio server /data --console-address \":9001\"\ndocker exec minio mc alias set local http://127.0.0.1:9000 minioadmin minioadmin\ndocker exec minio mc mb local/namidb\n```\n\n```python\nimport os\nos.environ[\"AWS_ACCESS_KEY_ID\"]     = \"minioadmin\"\nos.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"minioadmin\"\nclient = namidb.Client(\n    \"s3://namidb?ns=dev\"\n    \"\u0026endpoint=http://127.0.0.1:9000\"\n    \"\u0026region=us-east-1\"\n    \"\u0026allow_http=true\"\n)\n```\n\nFor the production-style MinIO plus `namidb-server` plus docker-compose stack,\nsee [Self-host as a database](#self-host-as-a-database) below.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eLocalStack\u003c/strong\u003e (S3 mock for tests), \u003ccode\u003es3://\u003c/code\u003e with an \u003ccode\u003eendpoint=...\u003c/code\u003e\u003c/summary\u003e\n\n```bash\ndocker run -p 4566:4566 -e SERVICES=s3 localstack/localstack\naws --endpoint-url=http://localhost:4566 s3 mb s3://namidb-dev\nexport AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test\n```\n\n```python\nclient = namidb.Client(\n    \"s3://namidb-dev?ns=local\"\n    \"\u0026endpoint=http://localhost:4566\"\n    \"\u0026allow_http=true\"\n    \"\u0026region=us-east-1\"\n)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eLocal filesystem\u003c/strong\u003e (\u003ccode\u003efile://\u003c/code\u003e)\u003c/summary\u003e\n\nFor CI fixtures or single-machine dev when you want durability without\na bucket. Full manifest CAS via per-namespace `flock` plus atomic\n`rename(2)`.\n\n```python\nclient = namidb.Client(\"file:///var/lib/namidb?ns=prod\")\n# relative paths work too:\nclient = namidb.Client(\"file://./data?ns=dev\")\n```\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n## Self-host as a database\n\nThere are two ways to run NamiDB as a database you fully own. Pick whichever matches how your app wants to talk to it.\n\n### Option A: embedded library plus your bucket\n\nYour app (Python or Rust) imports NamiDB directly and points at a bucket you control. Lowest latency, no extra hop, no network boundary, nothing to authenticate against. This is the \"DuckDB for graphs\" mode.\n\n```python\n# Python service\nimport namidb\nclient = namidb.Client(\"s3://your-bucket/data?ns=prod\u0026region=us-east-1\")\nresult = client.cypher(\"MATCH (n:Person) RETURN count(n) AS n\")\n```\n\n```rust\n// Rust service\nuse namidb::{\n    core::id::NamespaceId,\n    storage::{parse_uri, WriterSession},\n};\n\nlet (store, paths) = parse_uri(\"s3://your-bucket/data?ns=prod\")?;\nlet mut writer = WriterSession::open(store, paths).await?;\n// upserts, commit_batch, snapshot reads...\n```\n\nReach for this when your read fan-out fits in a single process and you don't want any network overhead. Because object storage is the source of truth, two replicas of your service can open the same namespace independently, and NamiDB's epoch-CAS protocol fences out the stale writer for you.\n\n### Option B: the `namidb-server` daemon over REST\n\nA single Rust binary (or container image) opens a namespace and serves it over HTTP. This is the one for when the database lives on a different machine than the app, or when you want a network boundary with bearer-token auth.\n\n```bash\n# Install from source\ncargo install --path crates/namidb-server\n\n# Or build the Docker image (from the repo root)\ndocker build -t namidb-server:0.1 -f crates/namidb-server/Dockerfile .\n```\n\n```bash\nnamidb-server \\\n  --store \"s3://your-bucket/data?ns=prod\u0026region=us-east-1\" \\\n  --listen 0.0.0.0:8080 \\\n  --auth-token \"$NAMIDB_AUTH_TOKEN\"\n```\n\n```bash\ncurl -X POST http://your-host:8080/v0/cypher \\\n  -H \"Authorization: Bearer $NAMIDB_AUTH_TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"query\": \"MATCH (n:Person) RETURN count(n) AS n\"}'\n# {\"columns\":[\"n\"],\"rows\":[{\"n\": 42}]}\n```\n\nSee [`crates/namidb-server/README.md`](./crates/namidb-server/README.md)\nfor the full route reference (`/v0/cypher`, `/v0/health`,\n`/v0/version`, `/v0/metrics`, `/v0/admin/flush`), the metrics and\nslow-query log, the JSON to Cypher type mapping, and the concurrency\nmodel.\n\n### Recipe: docker-compose with MinIO plus namidb-server\n\nA complete self-hosted database in one file. Bring your own auth token,\neverything else is wired up:\n\n```yaml\n# docker-compose.yml\nservices:\n  minio:\n    image: minio/minio\n    command: server /data --console-address \":9001\"\n    environment:\n      MINIO_ROOT_USER: minioadmin\n      MINIO_ROOT_PASSWORD: minioadmin\n    volumes:\n      - minio-data:/data\n    healthcheck:\n      test: [\"CMD\", \"mc\", \"ready\", \"local\"]\n      interval: 3s\n      retries: 30\n\n  bucket-init:\n    image: minio/mc\n    depends_on:\n      minio:\n        condition: service_healthy\n    entrypoint: \u003e\n      sh -c \"\n        mc alias set local http://minio:9000 minioadmin minioadmin \u0026\u0026\n        mc mb --ignore-existing local/namidb\n      \"\n\n  namidb-server:\n    image: namidb-server:0.1   # built from crates/namidb-server/Dockerfile\n    depends_on:\n      bucket-init:\n        condition: service_completed_successfully\n    environment:\n      NAMIDB_STORE: \"s3://namidb?ns=prod\u0026endpoint=http://minio:9000\u0026region=us-east-1\u0026allow_http=true\"\n      NAMIDB_LISTEN: \"0.0.0.0:8080\"\n      NAMIDB_AUTH_TOKEN: \"${NAMIDB_AUTH_TOKEN:?set NAMIDB_AUTH_TOKEN in your env}\"\n      NAMIDB_FLUSH_INTERVAL: \"30s\"\n      AWS_ACCESS_KEY_ID: \"minioadmin\"\n      AWS_SECRET_ACCESS_KEY: \"minioadmin\"\n    ports:\n      - \"8080:8080\"\n\nvolumes:\n  minio-data: {}\n```\n\n```bash\nexport NAMIDB_AUTH_TOKEN=$(openssl rand -hex 32)\ndocker compose up -d\ncurl -s http://localhost:8080/v0/health | jq .\n```\n\nThat's it. A graph database, your data sitting in MinIO, and an\nauthenticated REST API on `:8080`. Swap the `NAMIDB_STORE` URI and the\nsame setup moves to AWS S3, R2, GCS or Azure without touching anything\nelse.\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n## CLI\n\n\n```bash\n# Ephemeral in-memory namespace, same as the quickstart.\nnamidb run \"CREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})\"\nnamidb run \"MATCH (p:Person) RETURN p.name\"\n\n# Persistent. Any URI scheme works.\nnamidb run --store \"file:///var/lib/namidb?ns=prod\" \\\n  \"CREATE (a:Person {name: 'Alice'})\"\nnamidb run --store \"file:///var/lib/namidb?ns=prod\" \\\n  \"MATCH (p:Person) RETURN p.name\"\n\nnamidb run --store \"s3://my-bucket/data?ns=prod\u0026region=us-west-2\" \\\n  \"MATCH (p:Person) RETURN count(*) AS n\"\n\n# Plan inspection. Doesn't touch storage.\nnamidb explain --verbose \\\n  \"MATCH (a:Person)-[:KNOWS]-\u003e(b) RETURN b ORDER BY b.id LIMIT 20\"\n```\n\nSee [`crates/namidb-cli/README.md`](./crates/namidb-cli/README.md)\nfor every subcommand.\n\n\u003cbr /\u003e\n\n## Rust (embedded)\n\n```rust\nuse std::sync::Arc;\n\nuse namidb_core::id::NamespaceId;\nuse namidb_query::{execute, lower, parse, Params};\nuse namidb_storage::{parse_uri, WriterSession};\n\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    // Any supported URI scheme: memory://, file://, s3://, gs://, az://.\n    let (store, paths) = parse_uri(\"memory://demo\")?;\n    let mut writer = WriterSession::open(store, paths).await?;\n\n    // ... upsert nodes / edges, then commit_batch + flush ...\n\n    let snap = writer.snapshot();\n    let query = parse(\"MATCH (a:Person) RETURN count(*) AS n\")?;\n    let plan  = lower(\u0026query)?;\n    let rows  = execute(\u0026plan, \u0026snap, \u0026Params::new()).await?;\n\n    println!(\"{rows:?}\");\n    Ok(())\n}\n```\n\nThe umbrella crate ([`crates/namidb/`](./crates/namidb/)) re-exports\nthe stable surface, so a downstream `Cargo.toml` only needs the one\nline.\n\n\u003cbr /\u003e\n\n## Architecture\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\".assets/namidb-architecture-dark.svg\" /\u003e\n    \u003cimg src=\".assets/namidb-architecture.svg\" alt=\"NamiDB architecture: Query / Graph / Storage (LSM) / Object store layers, with a cross-snapshot caches side-car\" width=\"900\" /\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│  Cypher · GQL (ISO/IEC 39075:2024)                                  │\n│  Cost-based optimizer · Morsel-driven executor · Factorization      │\n├─────────────────────────────────────────────────────────────────────┤\n│  Property graph · CSR adjacency · Columnar SSTs                     │\n├─────────────────────────────────────────────────────────────────────┤\n│  LSM tree · WAL · Memtable · SST · Manifest CAS                     │\n│  Hybrid buffer pool (memory + NVMe)                                 │\n├─────────────────────────────────────────────────────────────────────┤\n│  S3 · R2 · GCS · Azure Blob · MinIO · Tigris · Local FS             │\n└─────────────────────────────────────────────────────────────────────┘\n```\n\nDesign proposals live in [`docs/rfc/`](./docs/rfc/). Start with\n[RFC-001](./docs/rfc/001-storage-engine.md) for the storage engine and\n[RFC-002](./docs/rfc/002-sst-format.md) for the SST format.\n\n\u003cbr /\u003e\n\n## Configuration\n\nA handful of env vars you can tune. The defaults are fine for almost\neverything; you mostly reach for these when you're chasing down a\nperformance or memory problem.\n\n| Env var | Default | What it does |\n|---|---|---|\n| `NAMIDB_ADJACENCY` | ON | CSR adjacency in RAM, shared across snapshots (RFC-018). |\n| `NAMIDB_NODE_CACHE` | ON | Cross-snapshot `NodeView` lookup cache (RFC-019). |\n| `NAMIDB_SST_CACHE` | ON | SST body, decoded edge property streams, and the parsed `EdgeSstReader` (RFC-020). |\n| `NAMIDB_FACTORIZE` | OFF | Factorized intermediate results in the executor (RFC-017). |\n| `NAMIDB_PROFILE_DUMP` | OFF | Dump per-stage profile counters to stderr after each query. |\n\n`namidb-server` adds a few of its own:\n\n| Env var | Default | What it does |\n|---|---|---|\n| `NAMIDB_STORE` | (required) | Storage URI, e.g. `s3://bucket?ns=prod`. |\n| `NAMIDB_LISTEN` | `0.0.0.0:8080` | TCP bind address. |\n| `NAMIDB_AUTH_TOKEN` | unset (open) | Bearer token. When it's unset the server warns and accepts every request. |\n| `NAMIDB_FLUSH_INTERVAL` | `30s` | Background memtable -\u003e L0 flush cadence. `0s` disables it. |\n| `NAMIDB_SLOW_QUERY_THRESHOLD` | `1s` | Log any query at or above this wall-clock at WARN. `0s` disables the slow-query log. |\n\n\u003cbr /\u003e\n\n## Repository layout\n\n```\n.\n├── Cargo.toml              # Workspace manifest\n├── rust-toolchain.toml     # Pinned toolchain\n├── LICENSE                 # BSL 1.1 (auto-converts to Apache 2.0)\n├── README.md\n├── CONTRIBUTING.md\n├── docs/\n│   └── rfc/                # Design proposals (RFC-001 to RFC-020)\n├── crates/\n│   ├── namidb-core/        # Common types, errors, schema\n│   ├── namidb-storage/     # LSM, WAL, manifest, SST, memtable, URI parser, file:// CAS\n│   ├── namidb-graph/       # Property columns + CSR adjacency\n│   ├── namidb-query/       # Cypher / GQL parser, optimizer, executor\n│   ├── namidb-cli/         # `namidb` command-line tool\n│   ├── namidb-py/          # Python bindings (PyO3 + maturin)\n│   ├── namidb-server/      # `namidb-server` HTTP daemon + Dockerfile\n│   ├── namidb-bench/       # LDBC-shaped synthetic bench harness\n│   └── namidb/             # Public façade crate\n├── bench/                  # LDBC SNB Interactive bench harness\n└── tests/                  # Integration helpers (LocalStack, R2 wrapper)\n```\n\n\u003cbr /\u003e\n\n## Documentation\n\n| Resource | Where |\n|---|---|\n| **Website** | [namidb.com](https://namidb.com) |\n| **Reference docs \u0026 guides** | [docs.namidb.com](https://docs.namidb.com) |\n| **Design RFCs** | [`docs/rfc/`](./docs/rfc/) |\n| **Python bindings** | [`crates/namidb-py/README.md`](./crates/namidb-py/README.md) |\n| **HTTP server** | [`crates/namidb-server/README.md`](./crates/namidb-server/README.md) |\n| **CLI** | [`crates/namidb-cli/README.md`](./crates/namidb-cli/README.md) |\n| **Benchmark harness** | [`bench/README.md`](./bench/README.md) |\n\n\u003cbr /\u003e\n\n## Roadmap\n\n- **Cloud (closed beta).** Multi-tenant SaaS on namidb.com with\n  per-namespace scale-to-zero, encrypted-at-rest tenants, and a hosted\n  control plane. [Request access](https://namidb.com).\n- **Streaming responses.** `/v0/cypher/stream` (NDJSON) and\n  `/v0/cypher/arrow` (Arrow IPC) for zero-copy DataFrame ingestion.\n- **Concurrent reads.** RFC-021 takes the single-writer mutex off the\n  read path so a `namidb-server` can fan reads out across every core.\n\n\u003cbr /\u003e\n\n## Contributing\n\nWe develop in the open. Have a look at [`CONTRIBUTING.md`](./CONTRIBUTING.md)\nand the RFCs in [`docs/rfc/`](./docs/rfc/) before you start. Anything\nnon-trivial goes through an RFC first.\n\n\u003cbr /\u003e\n\n## License\n\nNamiDB is licensed under the [**Business Source License 1.1**](LICENSE).\n\n- Free for development, testing, internal production use, and anything\n  that doesn't compete with a hosted NamiDB offering from the Licensor.\n- Converts automatically to the **Apache License 2.0** three years\n  after each release.\n- A separate commercial license is available if you need to embed or\n  redistribute NamiDB outside what BSL allows, including running it as\n  a hosted database service. Reach us at\n  [`info@namidb.com`](mailto:info@namidb.com).\n\n\u003cbr /\u003e\n\n## Acknowledgements\n\nA few projects this leans on, directly or for ideas:\n\n- **Kùzu**, for showing that columnar storage, CSR adjacency and\n  factorization are the right model for property graphs.\n- **SlateDB**, for the canonical recipe for LSM trees on object\n  storage.\n- **turbopuffer**, for proving that namespace-per-tenant on S3 is a\n  viable SaaS architecture.\n- **Apache Arrow, Parquet and DataFusion**, for the columnar\n  foundation.\n- **foyer-rs**, for the hybrid memory and disk cache.\n\n\u003cbr /\u003e\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n### The bucket is the database.\n\n\u003csub\u003eNamiDB is a product of \u003ca href=\"https://namidb.com\"\u003e\u003cb\u003eLESAI, Corp.\u003c/b\u003e\u003c/a\u003e, Delaware, USA.\u003c/sub\u003e\u003cbr /\u003e\n\u003csub\u003e© 2026 LESAI, Corp. All rights reserved.\u003c/sub\u003e\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnamidb%2Fnamidb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnamidb%2Fnamidb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnamidb%2Fnamidb/lists"}