CAIN-42 MCPGate

CAIN-42 four-server cluster: loss, jitter, duplication, reordering

Degraded-network test on the live CAIN-42 cluster cain-mr-02 (4 replicas on 4 servers in 4 regions): packet loss, delay with jitter, duplication and reordering on every replica's traffic at once. This page loads every quorum certificate all four replicas hold afterwards, and your browser re-checks them.

What happened

Impairment on all four replicas' mesh traffic: delay 120ms 40ms distribution normal loss 10% duplicate 5% reorder 10% 50% (all 4 hosts, cain-mr-02 overlay traffic only).

phaselengthwrites committedratep50p95
baseline45 s68/681.51/s596.4 ms1103.6 ms
impaired240 s44/620.18/s3880.6 ms6212.6 ms
recovered45 s89/891.98/s486.6 ms729.4 ms

Safety held: the identical decision chains below, on all four replicas, show there was no fork. Liveness degraded sharply: under the impairment the commit rate fell by about an order of magnitude and some writes did not commit within the client's 30 s timeout; it recovered fully once the impairment ended (all four agreed 0.4 s after). That is a measured limit, not a pass on performance.

Before and after the engine change

did engine 948b189 (view-change backoff resets on progress, fresh views not accused) improve liveness under this impairment?

Under the impairment: before (521f84c (bundle degraded-network-2026-09-27)) 39/61 committed, 0.16/s, p95 7173.9 ms; after (948b189 (this bundle)) 44/62 committed, 0.18/s, p95 6212.6 ms. View changes during the impairment: before 14 (view 8 -> 22); after at most 6 (view 22 at the 04:08 proof, before a rolling upgrade that itself changes view -> 28).

The view-change storm fell by more than half, but throughput under 10% loss did not improve beyond run-to-run noise (0.16 -> 0.18 commits/s). The storm was not the bottleneck; message delivery under loss is. Safety held in both runs.

Method: tc prio qdisc + u32 filter (destination = the cluster overlay subnet) -> netem band, removed by a timer on each host.

Verify (about 5 seconds)

What is checked

  1. The membership configuration hash is recomputed from the 4 member ids and Ed25519 public keys. Every node and every certificate must carry it.
  2. For every sequence on every node, the COMMIT_QC and the PREPARE_QC. Each vote must be an Ed25519 signature by a distinct member over SHA-256 of the canonical signed message. It must have the right type (a COMMIT vote never counts as a PREPARE vote) and match this cluster, epoch, view, sequence and digest. Each certificate needs at least 3 distinct signers. The leader's proposal must be signed by the primary of that view, and its digest must bind the proposed operation.
  3. The certificate hash and signature-bundle hash are recomputed from the content.
  4. Evolution 3 fast path: a FAST_COMMIT_QC (a decision taken without the COMMIT round) is accepted only if the published membership declares the fast path and all four members signed it. Three of four is never enough for a fast commit.
  5. The decision chain is folded from genesis: decision_hash(seq) = H(cluster, epoch, seq, digest, parent). It must be contiguous and identical on all four nodes, and the nodes must end with the same application-state hash.
  6. View-change quorum certificates, and one consensus-to-enforcement AuthorizationCertificate per node.
  7. Negative controls: a certificate is tampered with in seven ways, and every tampered copy must be rejected.

The same checks, without a browser: curl -so verify_pbft_qc_bundle.py verify_pbft_qc_bundle.py.txt && python3 verify_pbft_qc_bundle.py PBFT_QC_BUNDLE.json (needs pip install cryptography, no CAIN code).