Routing and manifests
The hard problem in a network without a coordinator is not executing a query. It is deciding who to ask. Gnarl solves it with manifests.
What a manifest is
A manifest is a small, signed document that describes what a node can answer — never what it holds. It is the only thing that crosses the network before a query does.
{
"index": "imagery",
"node": "n1qh7f2m9x8p4vd6kkzq3r7wjn5tbc2a8",
"version": 7,
"doc_count": 48219,
"fields": ["title", "captured_at", "sensor"],
"summary": {
"time_range": ["2019-03-01T00:00:00Z", "2026-07-28T00:00:00Z"],
"bbox": [-124.7, 24.5, -66.9, 49.4],
"sensor": ["landsat-8", "sentinel-2", "planetscope"],
"terms": "bloom:8kb:base64…"
},
"signature": "ed25519:9c41…2f7e"
}
Typical size is a few kilobytes regardless of corpus size — a 50-million-document node advertises no more than a 50-thousand-document one.
Elimination, not lookup
Planning is a process of provable elimination. The planner never asks "who has this document?" It asks "who can I rule out?"
| Predicate in query | Manifest field consulted | Eliminates |
|---|---|---|
captured_at > 2024 | summary.time_range | Nodes whose newest document predates 2024 |
within(bbox) | summary.bbox | Nodes with no overlapping footprint |
sensor = "landsat-8" | summary.sensor | Nodes that have never seen that sensor |
match("glacier") | summary.terms (Bloom filter) | Nodes whose vocabulary lacks the term |
| any | policy | Nodes that would refuse you anyway |
Whatever survives elimination gets asked. False positives cost one wasted request; false negatives are impossible, because every summary is conservative by construction — a Bloom filter may say "maybe" but never a wrongful "no".
Fan-out control
Asking everyone is correct but not always affordable. Queries carry a budget.
{
"scope": {
"max_nodes": 64,
"deadline_ms": 750,
"min_coverage": 0.9
}
}
When more nodes survive elimination than max_nodes allows, the planner ranks
candidates by expected contribution — manifest match strength, recent latency,
and recent success rate — and asks the best ones first. The response always
reports what fraction of eligible nodes actually answered.
Manifest propagation
Manifests gossip. A node pushes a new manifest version to a random subset of peers, which forward it onward until the network converges — typically within a few seconds on networks of hundreds of nodes.
Versions are monotonic and signed, so a stale manifest can never overwrite a fresh one, and a peer cannot forge a manifest on someone else's behalf.
Tuning
routing:
gossip_interval: 5s
manifest_ttl: 15m # re-ask a peer whose manifest went stale
candidate_cap: 128 # hard ceiling on fan-out per query
latency_window: 5m # how much history feeds candidate ranking
bloom_bits_per_doc: 10 # larger = fewer false positives, bigger manifest