Architecture
Gnarl replaces the cluster with a network: a set of independent nodes that each own their data, their index, and their policy, and that cooperate only for the duration of a single query.
The pieces
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ node A │ │ node B │ │ node C │
│ ────────── │ │ ────────── │ │ ────────── │
│ local data │ │ local data │ │ local data │
│ local index │ │ local index │ │ local index │
│ policy │ │ policy │ │ policy │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ manifest │ manifest │ manifest
└───────────────┬───────┴───────────────┬───────┘
│ │
╔══════╧═══════════════════════╧══════╗
║ the network (gossip) ║
╚══════════════════╤══════════════════╝
│
query enters
at any node
There is no node in that diagram that is more important than the others. The node a query happens to arrive at becomes the coordinator for that query only, and stops being special the moment it returns results.
Query lifecycle
- Parse. The receiving node parses the query and resolves it against the fields it knows exist across the network.
- Plan. It reads cached manifests and eliminates every node that provably cannot contribute — wrong field, wrong time range, wrong geography, wrong policy. Planning is local and takes microseconds.
- Fan out. The query is signed and sent to the surviving candidates, with a deadline attached.
- Execute locally. Each node runs the query against its own index, applies its own policy, and returns hits with a signature.
- Merge. The coordinator normalizes scores, merges the ranked lists, and streams results back as they arrive.
- Verify. Signatures are checked against known node identities. Unverifiable results are dropped and reported, not silently included.
What is deliberately absent
| Absent | Why |
|---|---|
| Elected master | Nothing to elect, nothing to lose during an election |
| Shared cluster state | Nodes agree on nothing except the protocol |
| Global rebalancing | Data placement is whatever the operator already chose |
| Cross-node transactions | Queries are read-only and independent |
| Central registry | Manifests gossip; there is nobody to take offline |
Failure behavior
A node that is slow, unreachable, or refusing is not an outage — it is one fewer answer. Every response carries the roster of who was asked and who replied, so a partial result is always visibly partial:
{
"hits": [ /* ... */ ],
"coverage": { "asked": 41, "answered": 39, "timed_out": 2, "complete": false }
}
Applications that need completeness can require coverage.complete, retry the
stragglers, or degrade explicitly. What they cannot do is mistake a partial
answer for a whole one.
Consistency model
Gnarl is eventually consistent at the manifest layer and strongly consistent at the node layer. A node always answers from its own committed index. What varies is how quickly other nodes learn that the index changed — typically a gossip round, on the order of seconds.