grange — a document database
your agent can pay for
Agents don't want a database console — they want JSON in, JSON out, and a bill they can settle themselves.
French for "barn" — a place where documents live.
grange is a document DB written in machin: crash-safe WAL, indexes with O(1) aggregates, range queries. Embed it, run it, or use this hosted instance — signup is one curl with a peage wallet.
# get an isolated, metered namespace (the wallet IS the signup) curl -s -X POST https://grange.intrane.fr/tenants -H 'X-Peage-Wallet: pw_...' -d '{"name":"my agent"}' # -> {"tenant":"t...","token":"gt_..."} curl -s -X POST https://grange.intrane.fr/put -H 'Authorization: Bearer gt_...' \ -d '{"coll":"leads","doc":{"co":"acme","score":9}}' curl -s 'https://grange.intrane.fr/agg?coll=leads&group-by=co&sum=score' -H 'Authorization: Bearer gt_...'
faster than SQLite where it matters
100k docs, both engines indexed on the same fields, same box (bench ships in the repo — make bench):
| workload | grange | SQLite |
|---|---|---|
| bulk insert, 2 indexes maintained | 278k docs/s | 25k rows/s |
| indexed count × 1000 | <1 ms | 1.5 s |
| group-by count/sum/avg × 1000 | <1 ms (write-time registers) | 49 s |
| range count × 1000 | <1 ms | 257 ms (indexed) |
Crash-safe by construction
Every commit is one immutable, checksummed WAL chunk. kill -9 leaves exactly the committed prefix — the repo ships the harness that proves it.
Race-free, provably
The server is a single actor with zero goroutines; machin's inferred data-race analysis verifies the whole engine on every build. No Send/Sync, no annotations.
RBAC
POST /tokens issues read-only (ro) or read-write (rw) tokens per tenant. Existing tokens default to rw.
Concurrent reads
GRANGE_CONCURRENT_READS=1 auto-spawns a read replica on port+1 and proxies GETs to it — real read concurrency without touching the write path.
Automated failover
POST /promote (admin) flips a --follow follower to primary in one request. The data is already local.
Memory introspection
GET /memory reports RSS, limit, arena resets, loaded collections, and watchdog config. GRANGE_RESET_EVERY reclaims the arena between requests.
Pay-as-you-go
Storage: €0.15/GB/month, first 50 MB free. Queries free. Accrued continuously, charged via peage — no subscription, no card on file.
No lock-in
The engine is MIT OSS. Export with one curl; self-host the same binary when you outgrow the hosted rail.
first 5 minutes
Hosted — signup to first query in 5 curls (get a wallet at peage.intrane.fr):
# 1. signup — the wallet IS the credential, no card, no form curl -s -X POST https://grange.intrane.fr/tenants \ -H 'X-Peage-Wallet: pw_...' -d '{"name":"my agent"}' # > {"tenant":"t...","token":"gt_..."} # 2. put — store JSON documents, auto-id or your own curl -s -X POST https://grange.intrane.fr/put \ -H 'Authorization: Bearer gt_...' \ -d '{"coll":"leads","doc":{"co":"acme","score":9}}' # 3. index — equality for agg O(1), range for ordering curl -s -X POST https://grange.intrane.fr/index \ -H 'Authorization: Bearer gt_...' \ -d '{"coll":"leads","field":"co","sums":"score"}' curl -s -X POST https://grange.intrane.fr/index \ -H 'Authorization: Bearer gt_...' \ -d '{"coll":"leads","field":"score","kind":"range"}' # 4. find — equality, range, ordered, paginated curl -s 'https://grange.intrane.fr/find?coll=leads&where=score>=5&order=score&desc=true' \ -H 'Authorization: Bearer gt_...' # 5. aggregate — group-by count/sum/avg, O(1) at write time curl -s 'https://grange.intrane.fr/agg?coll=leads&group-by=co&sum=score' \ -H 'Authorization: Bearer gt_...'
Self-hosted — same engine, no signup:
curl -sSL -o grange https://github.com/javimosch/grange/releases/latest/download/grange-linux-x86_64 chmod +x grange ./grange put --db ./data --coll leads --doc '{"co":"acme","score":9}' ./grange index --db ./data --coll leads --field co --sums score ./grange index --db ./data --coll leads --field score --range ./grange find --db ./data --coll leads --where 'score>=5' --order score --desc ./grange agg --db ./data --coll leads --group-by co --sum score
dogfooding: this page runs on grange
The subscribe form below is not a demo — it is a paying tenant of this hosted instance. The flow: landing form → grange-subscribe (a 130-line machin app) → machin client SDK → hosted grange. Every subscriber email is a document in the subscribers collection, stored and queried like any customer's data. The count is live:
curl -s https://subscribe.grange.intrane.fr/count # > {"ok":true,"data":{"subscribers":4}}
The subscribe service is a separate process (a grange actor calling its own HTTP API would deadlock), which is also the honest shape of a customer. Source: apps/subscribe/subscribe.src
dogfooding: vigie-sync — a live analytics mirror
vigie is a real analytics product. vigie-sync mirrors its SQLite database into grange as a cold collection — disk-resident, not held in memory. It runs continuously on dk1: a systemd timer syncs every 10 minutes, a daily one re-checks every aggregate against SQLite as the oracle.
# sync new rows from vigie's SQLite (read-only sidecar) vigie-sync sync --sqlite /opt/vigie/vigie.db --token gt_... # compare: every aggregate must match SQLite exactly vigie-sync compare --sqlite /opt/vigie/vigie.db --token gt_... # > {"match":true,"checks":14,"last_24h_query_ms":3}
14/14 checks match SQLite: total count, per-site counts, per-kind counts, and a 24-hour window. The 24h query — the one every analytics dashboard runs — was a full scan before the cold range index; it now answers in 3 ms on 604 events. The sync cursor itself lives in grange (one more dogfood). SQLite stays the source of truth, so a live product carries none of the risk of the experiment.
get launch + milestone notes
One mail when something ships (new SDKs, replication, pricing changes). Stored — of course — in grange itself: this form's backend is a machin app using the machin client SDK against the hosted service, as a paying tenant.
the interface is the docs
curl -s https://grange.intrane.fr/llms.txt # the full contract, written for agents