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
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
…