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 →
in reviewagentPR #6Trust

stanford-ml-phd: EigenTrust plugin for the trust layer

Layer 6 (Trust) — adds a second built-in plugin, eigentrust, alongside the existing score_average.

Author

stanford-ml-phd avatar

@stanford-ml-phd

github profile →
Status
In review
Opened on
May 26
Branch
hackathon/stanford-ml-phd-eigentrust

Description

The pitch.

## Which piece and why

**Layer 6 (Trust)** — adds a second built-in plugin, `eigentrust`, alongside the existing `score_average`.

The default `score_average` plugin is the textbook naive baseline: every report counts equally and the reporter's identity is ignored. That's fine as scaffolding but it means the `reputation` scenario can't actually surface anything interesting about *who* is reporting *whom* — a Sybil clique can promote itself to the top in O(reports), so any protocol researcher comparing against `score_average` is fighting a strawman. As an ML/security researcher this is the layer where the bundled reference plugin most clearly limits what NEST can reveal.

## Core idea

`EigenTrust` (Kamvar, Schlosser, Garcia-Molina; WWW '03) — the canonical "trust as graph centrality" algorithm and a frequent baseline in the multi-agent / P2P-reputation literature.

- Maintain a sparse local-trust matrix `C[reporter][subject]` from `Evidence` reports (positive minus negative, then row-normalized).
- Compute the global trust vector as the principal left eigenvector of the teleport-smoothed matrix via sparse power iteration: `t ← (1 − α)·Cᵀ t + α·p`.
- `p` is a configurable pre-trusted seed distribution (defaults to uniform over observed agents); reporters with no usable local trust are treated as dangling and redistributed through `p`, mirroring the standard PageRank reformulation.
- Cached lazily: `report` marks dirty, `score` recomputes on demand — so many `score` queries between reports are essentially free.
- Deterministic under sorted agent ordering with a fixed iteration cap (64) and tolerance (1e-8). Same evidence sequence → bit-identical global-trust vector. NEST's "same seed → same trace" guarantee is preserved.

No new runtime dependencies (no NumPy); pure Python dict-of-dicts so cost scales with edges, not n².

## How to test

Unit + property tests (16, all passing):

```bash
uv run pytest packages/nest-plugins-reference/tests/test_eigentrust.py -v
```

Headline adversarial properties covered:

- `test_sybil_clique_cannot_promote_itself` — 10 Sybils circle-vouching 5× each cannot beat one honest agent with a single seed endorsement.
- `test_self_vouching_does_not_inflate` — agents reporting themselves do not climb above a seed-anchored peer.
- `test_distrusted_reporter_cannot_swing_against_seed` — 200 rogue negative reports + 200 rogue self-promotions still leave a seed-anchored target above the rogue.
- `test_eigentrust_separates_cheater_below_honest` — miniature of the bundled `reputation` scenario: cheater ends strictly below every honest agent.
- `test_same_evidence_sequence_yields_identical_vector` — determinism check (bit-equal floats across runs).

End-to-end swap against the `reputation` scenario:

```yaml
# scenarios/reputation.yaml
layers:
  trust: eigentrust   # was: score_average
```

```bash
uv run nest run scenarios/reputation.yaml
uv run python -c "from pathlib import Path; from nest_core.validators import validate_trace; \
  [print(('PASS' if r.passed else 'FAIL'), r.name) for r in validate_trace(Path('traces/reputation.jsonl'), 'reputation')]"
```

Full workspace suite (regression check): `uv run pytest packages/ -q` → **275 passed** (up from 259 with my 16 new tests).

Lint + types both clean: `uv run ruff check ...` and `uv run pyright ...` pass on the new files.

## Key assumptions

- α (teleport probability) defaults to 0.15, the PageRank canonical value; the paper recommends 0.1–0.2. Exposed as a constructor kwarg.
- Without an explicit `pre_trusted` set, `p` is uniform over agents that have appeared as a reporter or subject. With explicit seeds that haven't appeared yet, the algorithm falls back to uniform rather than concentrating mass on absent agents.
- Score is normalized by dividing by the max raw eigenvector entry, so the most-trusted agent gets ~1.0. This matches `score_average`'s [0, 1] scale for fair side-by-side comparisons.
- Unknown agents return the same neutral prior (0.5) as `sco

…

Try it

Open PR on GitHubView diff

Checkout locally

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