Your graph in S3
The headline use case. Your graph database lives in your S3 bucket.
-
Install the client
Terminal window pip install namidbCargo.toml [dependencies]namidb = "0.3"tokio = { version = "1", features = ["full"] } -
Export AWS credentials (or rely on an EC2 / EKS / Lambda IAM role)
Terminal window export AWS_ACCESS_KEY_ID=AKIA...export AWS_SECRET_ACCESS_KEY=...export AWS_DEFAULT_REGION=us-east-1The only IAM permissions NamiDB needs on the bucket are
s3:GetObject,s3:PutObject,s3:DeleteObject,s3:ListBucket. That’s it. No DynamoDB lock table, no separate metadata service. -
Open (or bootstrap) a namespace
import namidb as tgclient = tg.Client("s3://my-bucket/data?ns=prod®ion=us-east-1")use namidb::storage::{parse_uri, WriterSession};let (store, paths) = parse_uri("s3://my-bucket/data?ns=prod®ion=us-east-1")?;let mut writer = WriterSession::open(store, paths).await?; -
Write Cypher
client.cypher("CREATE (a:Person {name: 'Alice', age: 30})")client.cypher("CREATE (b:Person {name: 'Bob', age: 25})")client.cypher("MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) ""CREATE (a)-[:KNOWS {since: 2020}]->(b)")result = client.cypher("MATCH (p:Person) WHERE p.age >= $min RETURN p.name AS name, p.age AS age",params={"min": 18},)print(result.to_pandas()) -
Restart the process. Open a notebook on another machine with the same URI.
The graph is still there. The bucket is the database.
Why this works
- Conditional writes (
If-Match/If-None-Match) on S3 replace external consensus. The manifest object is mutated by compare-and-swap, so two writers can race and only one wins — the loser retries against the new version. - Single-writer-per-namespace with epoch fencing. Multiple readers scale freely; only one mutator at a time per namespace.
- Durability is whatever S3 gives you: 99.999999999% (11 nines) of object durability, multi-AZ replication.
- Cost scales to zero when no client opens the namespace — no compute is running, no DynamoDB table is provisioned.
Use Cloudflare R2 for zero egress
R2 charges no egress and has full S3-compatible conditional writes.
Same scheme, with the R2 endpoint and region=auto:
import namidb as tg
client = tg.Client( "s3://my-bucket?ns=prod" "&endpoint=https://<ACCOUNT_ID>.r2.cloudflarestorage.com" "®ion=auto")If you’re running NamiDB outside AWS — on Cloudflare Workers, Fly.io, your VPS, your laptop — R2 is almost always the right call.
What about backups?
aws s3 sync s3://my-bucket/data/ ./backup-2026-05-19/That’s it. NamiDB never writes to anywhere else.
Multi-tenancy
Each namespace is a folder under your bucket prefix:
s3://my-bucket/data/├── tenant-acme/│ ├── manifest.json│ ├── wal/│ └── sst/├── tenant-globex/│ ├── manifest.json│ ├── wal/│ └── sst/└── tenant-initech/ ├── ...Each ?ns=… opens an isolated namespace. Operationally:
tenants are folders.
Next
- Cypher reference — what you can query today.
- Operations / URI grammar — every backend and flag.
- Self-host with Docker Compose — network-bounded REST API in front of the bucket.