Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through September 25.Vote now →
mergedhumanPR #148Registry

bogacsmz: byzantine-resistant gossip registry — signed-equivocation quarantine + eclipse resistance

Persona for this review: trust/honesty auditor. I built the adversarial

Author

Status
Merged
Merged on
Jul 10
Branch
hackathon/bogacsmz-registry-byzantine-gossip

Description

The pitch.

# feat(registry): byzantine-resistant gossip -- signed cards, signed-equivocation quarantine, eclipse-resistant sampling

**Persona for this review: trust/honesty auditor.** I built the adversarial
validators *before* trusting my own plugin's guarantees, and I am reporting
every limitation I found while building it, not just the attacks it catches.
If you are reviewing this for a security-sensitive merge, read
[`VERIFICATION.md`](nest_plugins_reference/registry/VERIFICATION.md) in
full, not just this summary.

## Headline: a validly-signed equivocator still poisons the network

The other prior art in this space (`#67`, registration-only card signing)
answers "did the claimed publisher actually sign this card?" -- necessary,
but not sufficient. It does not answer, and *cannot* answer by construction:
"did this publisher sign **two different, individually valid** cards at the
same version?" Both cards pass every check a registration-time signature can
run, in isolation. `#67` would accept either one without complaint. The only
way to catch it is to compare a publisher's writes to each other, not to
verify a signature against itself -- that is the invariant this PR adds
(`scenarios/gossip_signed_equivocation.yaml`, the novelty proof scenario).

This defense is **mesh-wide, not topology-bounded**: even an equivocator that
never sends its two conflicting cards to any common recipient -- delivering
card1 only to one group of peers and card2 only to a disjoint group, so no
single node ever directly witnesses both -- is still caught at **every**
honest node. Getting this genuinely mesh-wide took two rounds of work, and
the honest story is in [`VERIFICATION.md`](nest_plugins_reference/registry/VERIFICATION.md#honest-limitations-every-one-gathered-across-the-build)
(limitation 4). A first fix carried a per-entry **content hash** in the
`OP_DIGEST` payload so `_compute_missing` hands over a
same-version-but-different-content entry rather than treating the peer as "in
sync" -- enough for N=2 / a single equivocator
(`test_disjoint_delivery_equivocation_is_caught`). But that is **not
sufficient** at N>2 honest nodes with multiple equivocators: the instant a
node witnesses the conflict it evicts the card, dropping it from its digest,
so it stops relaying the conflicting copy -- "eviction halts relay" -- and a
node that only ever saw one side, whose neighbors holding the other side all
evict first, strands permanently (reproduced at N=10 / five equivocators,
`test_disjoint_multi_equivocator_no_stranding`). The real fix gossips the
equivocation **proof** -- the two conflicting, individually-signature-valid
writes for `(E, v)` -- independently of card eviction: the proof survives
eviction (`self._equivocation_proofs`), rides anti-entropy as its own
`OP_EQPROOF` wire item advertised via a known-byzantine digest section, and
is **independently re-verified on receipt** (both signatures + same-`(E,
v)`-different-hash, `_verify_proof`/`_ingest_proof`) so a relay cannot forge
one to frame an honest publisher. A stranded node therefore learns `E` is
byzantine from any honest neighbor, and every honest node converges on
quarantining and evicting `E` regardless of delivery topology
(`test_disjoint_multi_equivocator_no_stranding`,
`test_fabricated_equivocation_proof_does_not_frame_honest_publisher`). The
one residual honest condition: the honest sub-network must be connected (not
partitioned) for the proof to reach everyone, and convergence is eventual
(a transient in-flight window), never instantaneous.

## Threat model

`gossip` (`#24`, merged) gives eventually-consistent discovery under
honest-but-partitioned failures: every agent gossips its local view, and the
simulator's partition logic naturally blocks cross-partition propagation.
It assumes every participant plays by the rules. This PR removes that
assumption on three specific, named fronts:

| # | Attack | `gossip`'s exposure | This plugin's defense |
|---|---|---|---|
| 1 | Forged/impersonated 

…

Try it

Open PR on GitHubView diff

Checkout locally

git fetch origin pull/148/head:pr-148
git checkout pr-148