Skip to content

Merchant Fraud Consortium

**FinTech · Cross-organization data sharing · Relational trust**

Overview

Merchant Fraud Consortium

FinTech · Cross-organization data sharing · Relational trust

The problem

Merchants and payment processors collectively see billions of fraud signals per day. An attacker card-testing at Merchant A today hits Merchant B tomorrow. Current industry attempts at shared-fraud databases all founder on the same wall:

If I share my data, my competitors get value. If I don’t, we’re all worse off. And if I share bad data (either by accident or malice), I poison everyone else.

Concretely:

  • Visa/Mastercard fraud feeds exist but are reactive, aggregated, and delayed. Real-time cross-merchant signal isn’t there.
  • Third-party data consortiums (e.g., Sift, Signifyd networks) require every participant to trust the central operator, who now has the keys to a very valuable database.
  • Direct merchant-to-merchant sharing exists at the informal level (private Slack channels for security leads) and at the heavyweight level (ISACs) but with no structured trust model.

The fundamental issue is that trust in fraud reports isn’t universal. A 500-merchant consortium will have:

  • A few very-high-signal reporters (large merchants with sophisticated fraud teams).
  • Many medium-quality reporters.
  • Some noisy / adversarial / compromised accounts whose reports should be ignored or even reversed.

A single “is this reporter trusted” toggle doesn’t capture reality. Each consumer of a fraud signal should weight it differently.

Why Quidnug fits

This is relational trust’s natural shape. Each merchant computes their own trust in every other merchant, based on direct experience and transitive endorsements through mutual business partners.

ProblemQuidnug primitive
”Do I trust signals from merchant X?”Trust edge in fraud.signals.us-retail
”Did X actually sign this report?”ECDSA signature on event-stream entry
”Has X been compromised and is re-playing?”Monotonic anchor nonces
”How do I weigh a new merchant’s reports?”Transitive trust through mutual peers
”How do signals propagate in near-real-time?”Push gossip (QDP-0005)
“How do I bootstrap a new member?”K-of-K snapshot (QDP-0008)

High-level architecture

Consortium
─────────────
(members run their own
Quidnug nodes, no central
operator)
┌────────────────┐ gossip ┌────────────────┐
│ Acme Retail │ ◄────────► │ BigBox Inc │
│ (Quidnug node) │ │ (Quidnug node)│
└───────┬────────┘ └────────┬───────┘
│ │
│ gossip │ gossip
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Fin-Tech #1 │ ◄────────► │ Bank.com │
│ (Quidnug node) │ gossip │ (Quidnug node)│
└────────────────┘ └────────────────┘
Each member emits fraud signals as events
into a shared "fraud.signals.us-retail" domain.
Each member computes trust FROM THEIR OWN PERSPECTIVE
when evaluating a signal.

Data model

Quids

  • One per member organization (Acme, BigBox, Fin-Tech #1, etc.).
  • Optionally, sub-quids per fraud-team analyst (so compromises are contained to one analyst’s key).

Domain

  • fraud.signals.us-retail (main).
  • Sub-domains by vertical: fraud.signals.us-retail.apparel, .electronics, .travel.
  • Members subscribe to subdomains relevant to them.

Signal as event stream entry

Every fraud signal is an event on a subject quid that represents the flagged entity (card fingerprint, IP, device ID, email pattern). Example:

{
"type": "EVENT",
"subjectId": "card-fp-8f3a9b...",
"subjectType": "QUID",
"eventType": "fraud.signal.card-testing",
"payload": {
"reporter": "acme-retail",
"severity": 0.9,
"evidence": {
"pattern": "multiple-CVV-retries",
"window": "5min",
"observedAt": 1713400000
},
"actionTaken": "decline",
"comment": "Testing stolen card numbers against $1 donations."
},
"signature": "<Acme's signature at its current epoch>"
}

Trust edges

Each member declares trust in peers they find reliable:

acme-retail ──0.9──► bigbox-inc (work with them for years)
acme-retail ──0.7──► fin-tech-1 (known good reputation)
acme-retail ──0.3──► newcomer-ltd (just joined, unproven)
bigbox-inc ──0.95──► acme-retail (mutual)
bigbox-inc ──0.8──► newcomer-ltd (BigBox did their DD)

Now when Newcomer submits a signal, Acme (direct trust 0.3) can also weigh through BigBox’s endorsement (0.95 × 0.8 = 0.76) and take the max, Newcomer’s signals get effective trust 0.76 from Acme’s perspective, not the pessimistic 0.3.

Signal consumption flow

1. Acme sees a card-testing pattern hit their checkout.
2. Acme submits a fraud signal event against the card fingerprint:
POST /api/v1/events
eventType: "fraud.signal.card-testing"
3. Push gossip (QDP-0005) propagates the event to peer nodes
within DomainGossipInterval * hops.
4. BigBox's node receives it. BigBox's fraud system polls for
new events in `fraud.signals.us-retail.*`.
5. For each received event, BigBox computes:
relTrust(bigbox-inc → reporter) in the signal's domain.
6. BigBox's decision engine blocks / flags / allows the card
based on severity × trust.
7. BigBox optionally emits their own corroborating event.
Now the card fingerprint's stream has TWO corroborating
signals, which raises the effective score for everyone
seeing both.

Counter-signals (“this wasn’t fraud”)

Recovery path for false positives. If BigBox discovers a flagged card was actually a legitimate customer, they emit:

{
"eventType": "fraud.signal.counter",
"payload": {
"reporter": "bigbox-inc",
"counters": ["<event-id of original signal>"],
"evidence": "customer verified; purchased $2000 of verified goods"
}
}

Consumers see both the original signal and the counter, and can make a better-informed decision. The original signal isn’t retracted (that would let malicious reporters hide their history); it’s complemented.

Reporter reputation management

A reporter whose signals are frequently countered by trusted peers sees their effective incoming trust decay naturally. Each consumer can apply their own heuristic:

effective_trust(reporter) = base_trust(reporter)
- 0.5 * fraction_countered

Publish this effective weighting via lower trust edges. Over time, a noisy reporter gets deprioritized by the network without any central operator making that call.

Compromised reporter detection

If Acme’s signing key is compromised and an attacker floods fake signals (“this legitimate card is fraud”), there are several responses:

  1. Rate limit on the gossip layer (QDP-0005 §7), per-producer message rate limit means one compromised member can only flood so fast.
  2. Counter-signal cascade, peers that see legit cards flagged emit counter-signals, degrading the attacker’s effective reputation.
  3. Emergency rotation via guardian recovery, Acme’s fraud team’s personal guardians (security team, CTO) can rotate Acme’s key, invalidating the attacker’s signing authority.

The critical point: no central operator can be coerced into kicking Acme off the consortium. Recovery is member-driven.

Key Quidnug features used

  • Relational trust in fraud.signals.us-retail, each member has their own view, so a bad actor can’t poison universally.
  • Push gossip (QDP-0005), signals propagate within ~30 seconds. Faster than polling a central feed.
  • Anchor nonces (QDP-0001), replay protection on signals. A signal can’t be re-submitted twice for double-counting.
  • Guardian recovery (QDP-0002), if a member’s signing key is compromised, their security team can rotate without disrupting the consortium’s operation.
  • Domain hierarchy, members subscribe to vertical-specific subdomains. An apparel retailer doesn’t need electronics- fraud signals.
  • K-of-K bootstrap (QDP-0008), a new member joins by getting snapshots of the trust graph from K existing trusted members.
  • Fork-block trigger (QDP-0009), when the consortium agrees to (e.g.) require signal severity ≥ 0.5 to propagate beyond one hop, a fork-block activates the change uniformly.

Scale estimates

Typical large consortium:

  • 500 member organizations
  • 100,000+ fraud signals/day across the network
  • ~2 MB/day of signal events per member on average
  • Trust graph: ~500 × ~50 trust edges each = 25,000 edges

This is well within a single Quidnug node’s comfort zone. Push gossip handles propagation; each member node processes only signals in their subscribed subdomains.

Value delivered

DimensionBeforeWith Quidnug
Real-time cross-merchant signalsDelayed batches or informal Slack~30-second propagation
Trust in a new member’s signalsBinary (in consortium or not)Relational, personalized, adapts over time
Compromised member recoveryCentral operator kicks them (if available)Member rotates their own key via guardian
False-positive correctionEmail / phone call to affected merchantOn-chain counter-signal visible to all
Data sovereigntyCentral DB owner sees everythingEach member holds their own node + data
Audit trail for a specific decisionMerchant’s internal logs onlySigned signal stream verifiable by anyone
New member onboardingManual vetting + central key provisioningK-of-K snapshot + existing members declare trust

What’s in this folder

Runnable POC

A working end-to-end demo lives at examples/merchant-fraud-consortium/:

  • fraud_weighting.py, standalone weighted-aggregation math (no SDK dependency).
  • fraud_weighting_test.py, pytest suite, 9 tests covering observer-relative correctness + decay.
  • demo.py, end-to-end flow against a live Quidnug node exercising all four actors (bootstrapping merchants, asymmetric trust edges, emitting signals, observer- relative aggregate scoring).

Running:

Terminal window
cd deploy/compose && docker compose up -d # start local node
cd examples/merchant-fraud-consortium
python demo.py

Implementation

Concrete API calls, pseudocode, signing shape.

Implementation: Merchant Fraud Consortium

Concrete Quidnug API calls to build a cross-merchant fraud-sharing network.

0. Configuration

Each member runs a Quidnug node with:

Terminal window
ENABLE_NONCE_LEDGER=true
ENABLE_PUSH_GOSSIP=true # real-time signal propagation
ENABLE_LAZY_EPOCH_PROBE=true
SUPPORTED_DOMAINS=fraud.signals.*,fraud.counter-signals.*
SEED_NODES='["peer1.consortium.example:8080","peer2.consortium.example:8080"]'

1. Register as a consortium member

Terminal window
# Bootstrap the member's root identity with home domain
curl -X POST http://localhost:8080/api/identities -d '{
"quidId": "acme-retail",
"name": "Acme Retail",
"creator": "acme-retail",
"updateNonce": 1,
"homeDomain": "fraud.signals.us-retail",
"attributes": {
"orgType": "retailer",
"vertical": "apparel",
"established": "2018",
"contact": "security@acme-retail.example"
}
}'

2. Install a guardian set for key recovery

Terminal window
# Build a GuardianSet for the member's own recovery path.
# (Use the same flow as the interbank case, see
# ../interbank-wire-authorization/implementation.md §2).
# Members pick their own internal security team as guardians.
curl -X POST http://localhost:8080/api/v2/guardian/set-update -d '{...}'

3. Declare trust in peers

Each member issues trust edges to peers they’ve vetted. Trust edges use the standard TRUST transaction:

Terminal window
curl -X POST http://localhost:8080/api/trust -d '{
"type": "TRUST",
"truster": "acme-retail",
"trustee": "bigbox-inc",
"trustLevel": 0.9,
"domain": "fraud.signals.us-retail",
"nonce": 1,
"description": "Longstanding partner; strong fraud team",
"validUntil": 1776844800
}'

Repeat for each peer. Trust decays naturally if validUntil is set, forces periodic re-attestation.

4. Emit a fraud signal

When Acme’s fraud system detects a card-testing pattern:

Terminal window
curl -X POST http://localhost:8080/api/v1/events -d '{
"type": "EVENT",
"subjectId": "card-fp-8f3a9b4e5d2c", /* hash of card identifiers */
"subjectType": "QUID",
"eventType": "fraud.signal.card-testing",
"payload": {
"reporter": "acme-retail",
"severity": 0.9,
"patternType": "multiple-CVV-retries",
"observedAt": 1713400000,
"evidenceHash": "<sha256 of internal evidence blob>",
"ipGeolocation": "US-CA",
"actionTaken": "decline"
},
"signature": "<ECDSA sig over canonical event bytes>"
}'

Note: the raw evidence stays in Acme’s internal systems for privacy. Only a hash and structured metadata go on-chain.

5. Consume signals for decision-making

A merchant’s fraud engine polls for new events in subscribed subdomains:

package fraud
import (
"context"
"net/http"
"time"
)
// SignalDecision incorporates both the raw severity and the
// consumer's trust in the reporter.
type SignalDecision struct {
SignalID string
Reporter string
RawSeverity float64
ConsumerTrust float64
EffectiveScore float64
}
func (f *FraudEngine) EvaluateSignal(ctx context.Context, signal Event) SignalDecision {
reporter := signal.Payload["reporter"].(string)
domain := extractDomain(signal.EventType)
// Query Quidnug for relational trust in the reporter.
trust, err := f.quidnugClient.GetTrust(ctx, f.selfQuid, reporter, domain,
&GetTrustOptions{MaxDepth: 4, IncludeUnverified: false})
if err != nil {
// Unknown reporter; treat as untrusted for safety.
return SignalDecision{
Reporter: reporter,
RawSeverity: signal.Payload["severity"].(float64),
ConsumerTrust: 0,
EffectiveScore: 0,
}
}
rawSev := signal.Payload["severity"].(float64)
return SignalDecision{
SignalID: signal.ID,
Reporter: reporter,
RawSeverity: rawSev,
ConsumerTrust: trust.TrustLevel,
EffectiveScore: rawSev * trust.TrustLevel,
}
}
// Apply:
// - effective >= 0.7 → block transaction
// - 0.4 <= effective < 0.7 → step up (3DS, manual review)
// - effective < 0.4 → allow but monitor
func (f *FraudEngine) Apply(d SignalDecision) Action {
switch {
case d.EffectiveScore >= 0.7:
return ActionBlock
case d.EffectiveScore >= 0.4:
return ActionStepUp
default:
return ActionAllow
}
}

6. Emit a counter-signal when a signal turns out to be wrong

BigBox finds that a card flagged by Newcomer was actually a legitimate repeat customer:

Terminal window
curl -X POST http://localhost:8080/api/v1/events -d '{
"type": "EVENT",
"subjectId": "card-fp-8f3a9b4e5d2c",
"subjectType": "QUID",
"eventType": "fraud.signal.counter",
"payload": {
"reporter": "bigbox-inc",
"countersEventId": "<event-id of the original>",
"evidence": "Customer verified via 3DS; completed $2000 purchase",
"severity": 0.9
},
"signature": "<BigBox sig>"
}'

Consumers processing signals should look for counter-signals before acting on the original:

func (f *FraudEngine) ActiveCounter(signalID string) bool {
events, _ := f.quidnugClient.GetSubjectEvents(signalID, "QUID")
for _, ev := range events {
if ev.EventType != "fraud.signal.counter" {
continue
}
if ev.Payload["countersEventId"] == signalID {
// Trust this counter?
counterTrust, _ := f.quidnugClient.GetTrust(
ctx, f.selfQuid,
ev.Payload["reporter"].(string),
"fraud.signals.us-retail", nil)
if counterTrust.TrustLevel >= 0.5 {
return true
}
}
}
return false
}

7. Key compromise emergency

Acme’s security team finds evidence their signing key is compromised. Emergency rotation via guardians:

Terminal window
curl -X POST http://localhost:8080/api/v2/guardian/recovery/init -d '{
"kind": "guardian_recovery_init",
"subjectQuid": "acme-retail",
"fromEpoch": 0,
"toEpoch": 1,
"newPublicKey": "<hex of new signing key>",
"minNextNonce": 1,
"maxAcceptedOldNonce": 0,
"anchorNonce": <next>,
"validFrom": <now>,
"guardianSigs": [
{ "guardianQuid": "acme-ciso", "keyEpoch": 0, "signature": "..." },
{ "guardianQuid": "acme-security-lead", "keyEpoch": 0, "signature": "..." }
]
}'

During the time-lock window (1h default), consortium peers can see the rotation is pending. Their push gossip catches the eventual commit; within minutes of commit, Acme’s old key is no longer valid for new signals.

8. Joining as a new member

A new merchant wants to join. Four steps:

8a. Self-register identity (off-network)

Generate a keypair, build a Quidnug node, create root identity.

8b. K-of-K bootstrap from existing members

cfg := core.DefaultBootstrapConfig()
cfg.Quorum = 3
cfg.TrustedPeer = "" // no operator override, strict K-of-K
// Before bootstrap, seed the trust list with public keys of
// known-good existing members (obtained out-of-band, email,
// website, trade association).
node.SeedBootstrapTrustList([]BootstrapTrustEntry{
{Quid: "acme-retail", PublicKey: "<hex>"},
{Quid: "bigbox-inc", PublicKey: "<hex>"},
{Quid: "fin-tech-1", PublicKey: "<hex>"},
}, 3)
sess, err := node.BootstrapFromPeers(context.Background(),
"fraud.signals.us-retail", cfg)
if err != nil || sess.State != core.BootstrapQuorumMet {
// Bootstrap failed, operator investigates.
panic(err)
}
node.ApplyBootstrapSnapshot(cfg.ShadowBlocks)

8c. Request inclusion from existing members

Send each member’s security team a signed self-introduction (off-band). Members review the newcomer’s reputation, certifications, and emit trust edges:

Terminal window
# Acme (having vetted the newcomer)
curl -X POST http://localhost:8080/api/trust -d '{
"truster": "acme-retail",
"trustee": "newcomer-ltd",
"trustLevel": 0.3, /* low starting trust; grows with performance */
"domain": "fraud.signals.us-retail",
"nonce": 47,
"validUntil": <now + 90d>
}'

8d. Start emitting signals

After bootstrap and at least one inbound trust edge, the newcomer can emit signals that will be weighted at whatever level each peer has assigned.

9. Compliance reporting

At quarter-end, produce a report showing:

  • Signals emitted by this member
  • Signals countered against this member (FP rate)
  • Signals received and acted on
// Using the event stream API:
mySignals := client.GetEventsByReporter("acme-retail",
"fraud.signals.us-retail", startDate, endDate)
countersAgainstUs := client.GetEventsByType(
"fraud.signal.counter",
startDate, endDate)
falsePositiveRate := float64(countersAgainstUs) / float64(len(mySignals))

10. Testing

func TestFraudConsortium_SignalWithTrust(t *testing.T) {
// Set up 3-node consortium.
h := newConsortiumHarness(t, 3)
defer h.close()
acme := h.nodes[0]
bigbox := h.nodes[1]
// BigBox trusts Acme at 0.9.
submitTrust(t, bigbox, "bigbox-inc", "acme-retail", 0.9, "fraud.signals.us-retail")
// Acme emits a signal.
submitSignal(t, acme, "card-fp-1", "acme-retail", 0.8)
// BigBox evaluates.
decision := evaluateFor(bigbox, "card-fp-1")
assert.InDelta(t, 0.72, decision.EffectiveScore, 0.01) // 0.8 × 0.9
assert.Equal(t, ActionBlock, apply(decision))
}
func TestFraudConsortium_CounterSignalDegrades(t *testing.T) {
// Verify that a trusted counter-signal causes consumers to
// de-act on the original.
}
func TestFraudConsortium_ReplayRejected(t *testing.T) {
// Same signal re-submitted rejected by monotonic nonce.
}

Where to go next

Threat model

Adversaries, assumed capabilities, mitigations.

Threat Model: Merchant Fraud Consortium

Assets

  1. Signal accuracy, the consortium’s collective ability to detect fraud depends on signals being truthful.
  2. Member reputation, a member wrongly accused of a false positive loses commercial value.
  3. Cardholder privacy, the underlying PAN data is not on-chain, but patterns of flagging are.
  4. Cross-member trust graph, who trusts whom is market- sensitive information.

Attackers

AttackerCapabilityGoal
Compromised member keyValid signing key of one memberFlood false signals / reverse reals
Malicious joinerLegitimate new member, bad intentPoison the network
CompetitorLegitimate member, mixed motivesHurt a competitor’s reputation
FraudsterNo consortium membershipManipulate signal network
Central authority demandLaw enforcement / regulatorAccess member data

Threats

T1. Compromised member emits false signals

Attack. Attacker gains control of one member’s signing key and emits thousands of fake signals flagging legitimate cards.

Mitigation.

  • Relational trust, other members weigh this reporter’s signals through their own lens. The more signals they emit, the more are countered by trusted peers, the lower their effective weight becomes.
  • Push gossip rate limit (QDP-0005 §7), per-producer token bucket means the attacker can only emit so many signals per minute before their forwarded traffic is rate-limited.
  • Counter-signal cascade, trusted peers seeing obvious false positives emit counters, which degrade the attacker’s effective trust.
  • Emergency rotation, member’s security team rotates their signing key via guardian recovery. Old key becomes invalid within the delay window.

Residual risk. Window of attack between compromise and detection. Mitigated by monitoring (signal-rate anomalies, counter-signal-volume alerts).

T2. Malicious new member

Attack. Attacker establishes a shell merchant, gets minimal vetting, joins the consortium, and emits malicious signals.

Mitigation.

  • Starting trust is low, new members typically get trust 0.1–0.3 from existing members. Their signals have low effective weight until they build reputation.
  • Transitive trust degrades, if the malicious member can’t get a trusted peer to endorse them, their signals stay low-weight.
  • Any member who vouched can revoke, trust edges are time-limited; validUntil enforces periodic re-affirmation.

Residual risk. If the malicious actor successfully compromises or social-engineers several existing high-trust members into vouching, they can accumulate reputation. This is a long-term con, detectable by auditing trust-graph changes.

T3. False-positive weaponization

Attack. A legitimate but aggressive fraud team at Merchant A regularly emits signals that turn out to be false positives affecting Merchant B’s customer base.

Mitigation.

  • Per-consumer reputation tracking, each member can internally compute “how often does Acme’s signals get countered by my trusted peers?” and lower their local trust in Acme accordingly.
  • Public counter-signal trail, A’s false positives accumulate visibly in A’s signal history.

Residual risk. Not a cryptographic problem; a market- incentive one. The transparency makes it manageable.

T4. Signal-flow correlation attack

Attack. Observer of gossip traffic tries to correlate which members are sharing signals with which others, inferring commercial relationships.

Mitigation.

  • Push gossip fanout means every member receives every signal, not just “interested” subsets, no pairwise correlation from gossip patterns.
  • Content-level correlation (who emits signals about which cards) is inherent to the domain; consortium membership is public by design.
  • Sub-domains let members scope what they emit publicly (vertical-specific subdomains).

Residual risk. An observer with gossip-level network view can see who emits and when. If that’s a concern, Tor-like anonymizing relays are future work beyond Quidnug.

T5. Replay attack

Attack. Attacker captures a legit signal from Acme and re-submits it to have signals double-counted.

Mitigation.

  • Dedup-first processing (QDP-0005 §6.5), message ID dedup rejects replays in O(1) before even checking signatures.
  • Per-signer anchor nonce (QDP-0001), each signal is bound to an anchor-nonce; a replay has the same nonce, immediately rejected.

Residual risk. None.

T6. Sybil, one person, many “members”

Attack. Attacker registers 50 shell merchants, each with a quid, and has them mutually endorse each other to build fake-trust graph.

Mitigation.

  • Out-of-band vetting, existing members don’t issue trust edges to shell entities they don’t know. Sybils can endorse each other all they want; without an edge from a real member, they remain unreachable in the trust graph.
  • Trust edges are public, a mutual-endorsement ring is visually obvious in the graph.
  • Periodic trust expiration (validUntil) forces re-attestation, raising the ongoing cost of maintaining sybils.

Residual risk. If a single legitimate member is compromised and starts endorsing sybils, the ring gets a foothold. See T1.

Attack. Government serves a subpoena on the central operator.

Mitigation.

  • There is no central operator. Each member holds their own data. Subpoenas go to individual members, who can decide with counsel.

Residual risk. Individual members can still be compelled. But the consortium isn’t a single target.

T8. Denial of service on one member

Attack. Flood one member’s node with traffic to prevent them from emitting / receiving signals.

Mitigation.

  • IP-level rate limiting on HTTP surface.
  • Push gossip is resilient to one-peer outage (messages propagate through other peers).
  • Member’s signal consumption is not latency-sensitive (seconds to minutes is fine).

Residual risk. Standard DoS mitigations (CDN, WAF) apply.

T9. Data poisoning via severity inflation

Attack. Attacker submits every signal with severity=1.0 to maximize disruption.

Mitigation.

  • Consumer-side normalization, consumers don’t have to trust reporter-declared severity. Severity × reporter-trust is the effective weight.
  • Counter-signals include a rebuttal severity, so a counter can match and neutralize.

Residual risk. Mostly structural, not a cryptographic problem.

T10. Consortium governance capture

Attack. A coalition of members passes a fork-block that changes the signal-validity rules in their favor (e.g., lowering the counter-signal weight).

Mitigation.

  • 2/3 quorum required for fork-block transactions (QDP-0009 §7). Capturing 2/3 of the validator set is itself a massive Sybil attack.
  • MinForkNoticeBlocks = 1440 ~= 24h. Every operator sees the change 24h before activation. Smart operators halt and investigate suspicious forks.
  • Members can operator-override their node’s flag settings, refusing to honor a controversial fork locally.

Residual risk. Majority-validator compromise is the prerequisite.

Not defended against

  1. Raw PII leaks, the protocol stores hashes and patterns, but if a member wraps a full PAN in an event payload, it’s on-chain. This is a member-application-layer responsibility.

  2. Collusion between multiple members, the protocol assumes each member is their own principal. Explicit collusion is out of scope.

  3. Gaming the trust algorithm, the BFS-pathing with max aggregation can be gamed by carefully structuring endorsements. Mitigations are part of the trust-algorithm design, not this use case.

Monitoring

Critical Prometheus metrics:

  • quidnug_gossip_rate_limited_total{producer=...}, a member being rate-limited may be under attack or may be compromised.
  • quidnug_nonce_replay_rejections_total, rising replays indicate replay attempts.
  • Application-layer: counter-signal ratio per reporter, FP rate, trust edge churn.

Operator playbook

  1. Unusual signal rate from a member. Emit an alert to that member’s security team. They have options:
    • Confirm expected burst (nothing to do).
    • Rotate their key via guardian recovery (emergency path).
  2. Counter-signal rate spiking. One reporter’s signals are being contested. Reduce local trust in that reporter.
  3. Suspicious fork-block transaction. Review signatures, communicate with other members, use operator-override if needed.

References