DEV Community

Manuel Felipe Arias Pineda
Manuel Felipe Arias Pineda

Posted on

Building a Bot Reputation System with ZK Attestations and P2P Gossip

The problem

AI agents calling your APIs have no behavioral history. A spammer looks identical to a legitimate user.

Soulprint's bot reputation system gives every agent a persistent 0-20 point behavioral score.

Architecture

Total Score (0-100) = Identity (0-80) + Reputation (0-20)
Enter fullscreen mode Exit fullscreen mode

Reputation starts at 10 (neutral) and moves with attestations.

Attestations

An attestation is an Ed25519-signed statement from a verified service:

interface BotAttestation {
  issuer_did: string;  // service DID (score >= 60 required)
  target_did: string;  // bot being rated
  value:      1 | -1; // reward or punishment
  context:    string; // 'spam-detected', 'payment-completed'...
  timestamp:  number;
  sig:        string; // Ed25519 — bound to issuer_did
}
Enter fullscreen mode Exit fullscreen mode

Creating one:

import { createAttestation, verifyAttestation } from 'soulprint-core';

const att = createAttestation(serviceKeypair, botDid, +1, 'trip-completed');
console.log(verifyAttestation(att)); // true

// Tamper with it:
const fake = { ...att, value: -1 };
console.log(verifyAttestation(fake)); // false — signature invalid
Enter fullscreen mode Exit fullscreen mode

Only verified services can attest

The validator node enforces:

POST /reputation/attest

Requirements:
  service_spt.score >= 60
  service_spt.did == attestation.issuer_did
  attestation.timestamp age < 1 hour
  Ed25519 signature valid
Enter fullscreen mode Exit fullscreen mode

If any check fails: 403 Forbidden.

P2P gossip

Once a node accepts an attestation, it forwards to all peers:

function gossipAttestation(att: BotAttestation) {
  peers.forEach(peerUrl =>
    fetch(`${peerUrl}/reputation/attest`, {
      method: 'POST',
      headers: { 'X-Gossip': '1' },
      body: JSON.stringify({ attestation: att }),
      signal: AbortSignal.timeout(3_000),
    }).catch(() => {}) // fire-and-forget
  );
}
Enter fullscreen mode Exit fullscreen mode

Event eventual consistency — attestation propagates across the network within seconds.

Anti-replay

const isDuplicate = prevAtts.some(a =>
  a.issuer_did === att.issuer_did &&
  a.timestamp  === att.timestamp &&
  a.context    === att.context
);
if (isDuplicate) return getReputation(att.target_did); // ignore
Enter fullscreen mode Exit fullscreen mode

Security guarantees

Attack Defense
Fake attestation Ed25519 sig verification
Service impersonation issuer_did must match sig
Low-score service attesting Node rejects if score < 60
Old attestation replay Max age: 1 hour
Attestation flood Rate limit 10 req/min/IP

Real-world example

After 1 month using mcp-colombia normally:

Week 1: +1 'normal-usage-pattern' (score: 10 → 11)
Week 2: +1 'normal-usage-pattern' (score: 11 → 12)
Week 3: +1 'normal-usage-pattern' (score: 12 → 13)
Premium endpoint: +1 'premium-endpoint-used' (score: 13 → 14)
Enter fullscreen mode Exit fullscreen mode

Meanwhile, a spammer:

Day 1: -1 'spam-detected' (score: 10 → 9)
Day 2: -1 'spam-detected' (score: 9 → 8)
...
Day 10: score 0 — effectively banned from reputation-gated services
Enter fullscreen mode Exit fullscreen mode

Querying reputation

curl http://node.example.com/reputation/did:key:z6Mk...

# Response:
{
  "score": 14,
  "attestations": 4,
  "last_updated": 1740360000
}
Enter fullscreen mode Exit fullscreen mode

npm

npm install soulprint-core     # createAttestation, computeReputation
npm install soulprint-network  # validator node + reputation endpoints
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/manuelariasfz/soulprint
Spec: https://github.com/manuelariasfz/soulprint/blob/main/specs/SIP-v0.1.md

Top comments (0)