Trust and verification
In a network you do not fully control, "did this result really come from that node?" has to be answerable. Gnarl answers it with signatures, and keeps the question separate from "should this node be allowed to ask me anything?", which is policy.
Identity
Every node is an ed25519 keypair. The node ID is derived from the public key, so an identity cannot be claimed — only proven.
gnarl identity show
▸ node id n1qh7f2m9x8p4vd6kkzq3r7wjn5tbc2a8
▸ algorithm ed25519
▸ created 2026-02-11T09:14:22Z
▸ rotations 0
What gets signed
| Artifact | Signed by | Protects against |
|---|---|---|
| Manifest | Publishing node | Forged claims about what a node holds |
| Query | Originating node | Unattributable or replayed queries |
| Result batch | Answering node | Tampering by a relay or coordinator |
Results carry the signature of the node that produced them, not the node that merged them. A coordinator that alters a hit invalidates the signature it was handed, and the client sees it.
{
"hits": [
{
"score": 12.4,
"node": "n1qh7f…c2a8",
"doc": {"title": "Puget Sound, 2024-06-11", "sensor": "sentinel-2"},
"signature": "ed25519:41ab…77c9"
}
],
"verified": true
}
Set "verify": false to skip verification when you already trust the transport
and want the throughput back.
Policy
Signatures establish who. Policy decides whether.
policy:
default: deny
allow:
- nodes: ["n1qh7f…c2a8", "n1m4kd…91b3"]
indexes: [imagery]
fields: [title, captured_at, sensor]
rate_limit: 40/s
- networks: [acme-internal]
indexes: [imagery, reports]
rate_limit: 200/s
require_signed_queries: true
redact:
- index: reports
fields: [author_email, internal_notes]
Policy is evaluated on the node that holds the data, every time, before the index is touched. There is no central policy service to bypass and no cached decision to go stale.
Trust models in practice
Closed network. A pre-shared key gates membership; every member is operated by one organization. Signatures are still checked, mostly to catch bugs and misconfiguration.
Federated. Several organizations, each running their own nodes, each publishing a node ID out of band. Peers pin the IDs they intend to trust.
gnarl peer trust n1m4kd…91b3 --label "partner-lab"
Open. Anyone may join and answer. Results are ranked with node reputation as an input, and clients can require that hits come only from pinned identities.
{"scope": {"trust": "pinned-only"}}
Key rotation
gnarl identity rotate --keep-old 30d
The node publishes a rotation record signed by the old key, so peers can follow the identity forward without re-establishing trust out of band. The old key stays valid for verification of already-published manifests for the grace period.
Threat model, briefly
Gnarl assumes peers may be slow, absent, buggy, or actively lying about their own content. It does not assume a peer can be forced to answer honestly — a node that holds a document may always choose to withhold it. What the protocol guarantees is attribution: whatever you do receive is provably from who it claims to be, unaltered, and everything else is visibly missing.