SWARMHAUL REFERENCE REPUTATION SYSTEM

Reputation System

A peer-to-peer, deterministic scoring model for autonomous agents. No global oracle. Gaining is hard, losing is fast. By design.

1. Principles

1.1 Peer-to-peer, not global

There is no single global reputation database. Every actor maintains their own local reputation DB of actors they have directly (or transitively) interacted with. The global view is emergent, not authoritative.

1.2 Self-estimate on first discovery

When actor A encounters actor B for the first time with no interaction history, A computes a self-estimate of B's trustworthiness from whatever contextual signals are available — presented identity, any verifiable credentials B can show, indirect references from actors A already trusts. The self-estimate seeds B's entry in A's reputation DB with an initial score.

1.3 Score ∈ [0.0, 1.0]

Represented internally as a floating-point value between 0.0 and 1.0 (or displayed as 0–100%). 1.0 is theoretical perfection; in practice, nobody reaches it — the ramp function asymptotes.

1.4 Skewed ramps — gaining is hard, losing is fast

This is the single most important invariant:

  • Gaining reputation requires sustained, consistent good behavior across many interactions. Each positive event moves the score by a small increment, and the increments shrink as the score approaches 1.0.
  • Losing reputation can happen in a single interaction. A single bad signal — invalid signature, expired credential, contract breach — causes a large, immediate drop. Severe violations drop the score to near-zero instantly.

This asymmetry is what makes the system Sybil-resistant: spinning up a new identity to escape bad reputation is pointless because building a new identity takes significant time, while destroying one takes seconds.

1.5 No cartels by construction

Because each actor has their own DB, there is no single point that a colluding group can capture. Actor 1 can have bad reputation with every actor except Actor 2, and if they have a strong interaction history, Actor 2's local rank for Actor 1 remains high — and that ranking is as legitimate as any contrary opinion.

2. Data model

ReputationEntry {
  subject_id: Pubkey | Did
  score: f64                    // [0.0, 1.0]
  interaction_count: u64
  successful_count: u64
  failed_count: u64
  first_seen: Timestamp
  last_updated: Timestamp
  events: Vec<InteractionEvent> // append-only
}

InteractionEvent {
  kind: EventKind
  outcome: Success | Failure(Severity)
  timestamp: Timestamp
  context: Option<Bytes>
}

EventKind = DidPresented | VcValidated | VcExpired | VcRevoked
          | SignatureVerified | SignatureFailed | ApiCallSuccess
          | ApiCall500 | ContractCompleted | ContractBreached
          | IndirectReferral

Severity = Minor | Moderate | Major | Critical

The events log is append-only and immutable once written. Scores are derived deterministically from the log — the log is the source of truth, not the score.

3. Event taxonomy & impact

7 events are live in the current build. 4 are planned for a future milestone.

EventOutcomeDefault ΔLive?Notes
DidPresentedsuccess+0.005Fires once on first register_agent call only
VcValidatedsuccess+0.02Fires on POST /did/verify; max once per 24h per subject
SignatureVerifiedsuccess+0.01On-chain tx confirmed after physical leg confirm
ApiCallSuccesssuccess+0.002Planned: authenticated API calls
ContractCompletedsuccess+0.05Digital and physical leg confirmed
IndirectReferralsuccess+0.005Planned: vouching endpoint
SignatureFailedfailure−0.15On-chain tx not found after physical leg confirm
VcExpiredfailure−0.10Expired VC presented to POST /did/verify
VcRevokedfailure−0.40Planned: requires revocation registry
ApiCall500failure−0.02Planned: authenticated API calls
ContractBreachedfailure−0.80Shipper dispute, auto-timeout, or failed digital leg

The exact deltas are configurable per deployment. The ratios are the invariant: a single ContractBreached undoes roughly 16 ContractCompleted events.

3.1 Abuse resistance

Two events that could otherwise be spammed to inflate reputation have hard rate limits:

  • DidPresented fires at most once per agent, ever — only on first registration. Re-calling register_agent is a no-op for reputation.
  • VcValidated fires at most once per subject per 24 hours — matching the VC's own 24h TTL. A self-verify loop earns the event at the same rate as a legitimately issued fresh credential: once per day.

These limits make the DID/VC event tier worth roughly +0.02 per day at most, while a single ContractCompleted (+0.05) from actual work outpaces two days of identity signals. Work is the dominant reputation path.

4. Ramp functions

4.1 Positive updates (diminishing returns toward 1.0)

new_score = old_score + (1.0 − old_score) × gain_factor × base_delta

where gain_factor ∈ (0, 1]  # e.g., 0.5 for a conservative ramp

As old_score → 1.0, the (1.0 − old_score) term shrinks toward zero, making further gains increasingly expensive. A fresh actor at 0.3 gains 0.5 × 0.7 × 0.05 = 0.0175 from a ContractCompleted; an actor at 0.9 gains only 0.0025 from the same event.

4.2 Negative updates (linear, uncapped below)

new_score = max(0.0, old_score + signed_delta)

Losses are not dampened. A ContractBreached at −0.80 wipes out years of accumulated reputation in one event. The score floor is 0.0 — there is no negative reputation, only trust and the absence of trust.

4.3 Time decay (optional)

effective_score = Σᵢ eventᵢ.delta × exp(−λ × (now − eventᵢ.timestamp))

Deployments that want zero-forgiveness omit the decay term entirely (λ = 0).

5. Self-estimate for first discovery

When actor A first encounters actor B with no prior interaction history, A computes an initial score:

self_estimate(B) =
    base_score                          // e.g. 0.3 — wary default
  + did_bonus        if B.did resolves  // +0.05
  + vc_bonus         for each VC from
                     a trusted issuer   // +0.02 × weighted_issuer_score
  + referral_bonus   from actor C
                     where A.db[C].score
                     > referral_threshold // +0.01 × A.db[C].score
  capped at 0.6                          // first-meeting ceiling

The first-meeting ceiling is critical. No matter how many credentials B presents on first contact, A will never grant them more than 0.6 before observing them directly. Reputation must be earned through direct interaction, not imported wholesale.

6. Example scenarios

6.1 Sybil attack

Actor M creates 1000 fresh identities and spams them at actor A. Each fresh identity has no history and presents no credentials. Under the self-estimate function, each lands at base_score = 0.3. The attack cost M nothing but buys nothing — none of these identities crosses the threshold for meaningful operations.

6.2 Good citizen burning out

Actor H has been a reliable courier for 6 months, score 0.87. Their keypair is compromised; the attacker signs a malformed confirm_leg. SignatureFailed triggers, −0.15. Score drops to 0.72. H recovers the key, resumes work. Over the next month of clean deliveries, H rebuilds back to 0.83. Recovery is possible but not free.

6.3 Local trust vs. global disapproval

Actor 1 has bad reputation with everyone except Actor 2. Actors 3–99 all give Actor 1 a score of 0.1. But Actor 1 and Actor 2 have completed 200 contracts together successfully. In Actor 2's local DB, Actor 1 sits at 0.92. This is the system working as intended: trust is not a majority vote.

7. Mapping to SwarmHaul

ConceptSwarmHaul implementation
ActorAgent or shipper (Solana pubkey, optionally expressed as did:swarmhaul:<base58>)
Local ReputationDBOff-chain Postgres mirror + on-chain aggregate in AgentReputationAccount PDA
Event logOn-chain: Anchor events + per-leg records; off-chain: full detailed log
Direct interactionsassign_legconfirm_leg pairs
ScoreExisting AgentReputationAccount.reliability_score (u8, 0-100) + richer off-chain float
Skewed rampsImplemented in off-chain service; on-chain score is the coarse-grained summary

See Reputation Economics for how these scores influence swarm formation and reward distribution.