docs: add TRUST_SCORE.md (trust-scoring review + plan)
Ideas-only review of a personalized web-of-trust approach: a spec-correct NIP-85 consumer (kind-10040 provider designation, house fallback, author + signature verification, the rank/followers/reporters/muters/hops tags, and the absence rules), a Verified/Unknown/Flagged display with a viewer mode, counterparty alerts, confidence-gated scoring, presets and negative signals. Notes what we already have, concrete file touch points, license and privacy caveats, and a recommended first slice. Links it from the parked identity/conflict section.
This commit is contained in:
parent
486ffdb000
commit
8833a15830
2 changed files with 229 additions and 0 deletions
|
|
@ -2512,6 +2512,12 @@ downstream of **centralized identity + centralized conflict resolution**; the fi
|
|||
is identity at a lower level + **plural, viewer-applied filtering**. A full idea
|
||||
list was discussed this session but is deliberately **NOT started**.
|
||||
|
||||
**Related, also ideas-only:** `TRUST_SCORE.md` collects the trust-scoring review
|
||||
(NIP-85 consumer with kind-10040 provider choice, a Verified/Unknown/Flagged
|
||||
display, confidence-gated scoring, counterparty alerts). It is the concrete form
|
||||
of the "labeler choice / plural filtering" thesis below. Not started; read it
|
||||
before touching `useTrustScore` / `useRelatrScore` / `trustFilter`.
|
||||
|
||||
**Keep the door open (cheap):** admin keys and relay sets must stay *data you can
|
||||
swap*, not constants. Hardcoded `ADMIN_PUBKEYS` is fine now — converting it later
|
||||
is a small refactor, not a trap.
|
||||
|
|
|
|||
223
TRUST_SCORE.md
Normal file
223
TRUST_SCORE.md
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
# Trust scores — ideas and plan
|
||||
|
||||
Status: ideas / proposal. No code yet.
|
||||
Scope: better trust for people and content across the map, The Lab, the
|
||||
marketplace, and moderation.
|
||||
|
||||
Source material: the **GrapeRank** and **Trusted Assertions** specifications in
|
||||
the `NosFabrica/protocols` repository, plus a public reference
|
||||
implementation and client by the same group. NIP-85 (Trusted Assertions) is the
|
||||
published, stable contract; GrapeRank is one pre-NIP scoring model that produces
|
||||
the published scores. We should treat NIP-85 as the interface and GrapeRank as a
|
||||
model we may adopt or borrow from, not a hard dependency.
|
||||
|
||||
## Where we are today
|
||||
|
||||
Positive signals (per-viewer, client-side):
|
||||
|
||||
- `src/hooks/useTrustScore.ts` and `src/hooks/useBatchTrustScores.ts` compute a
|
||||
static ladder from the viewer's follow graph:
|
||||
`100` self, `90` direct follow, `60` friend-of-friend (2 hops), `30` has a
|
||||
NIP-05, `0` unknown. Signature-verified via `onlyVerified`.
|
||||
- `src/lib/trustFilter.ts` + `TrustFilter.tsx` expose the levels
|
||||
`all / verified (>=90) / trusted (>=60) / following`.
|
||||
- `useSpotCommunityScore` weights content signals by distinct accounts,
|
||||
web-of-trust and activity (per-account cap of 3); `useTradeIntegrity` adds
|
||||
ring detection. Reports drive `useAutoHiddenSpots` (>=3 distinct reporters,
|
||||
no community signals) and the review queue.
|
||||
|
||||
NIP-85 consumption is half-built:
|
||||
|
||||
- `src/hooks/useRelatrScore.ts` (and `useBatchRelatrScores`) query kind `30382`
|
||||
from one **hardcoded** provider pubkey + relay (auto-enabled), verify
|
||||
signatures, and read only the `rank` tag. It ignores provider designation,
|
||||
the `followers` / `reporters` / `muters` / `hops` tags, and the spec's
|
||||
absence rules.
|
||||
- `src/lib/relatr.ts` is a placeholder REST client, disabled (`RELATR_ENABLED =
|
||||
false`); it was never finished and is the wrong shape (REST, not events).
|
||||
|
||||
## Gaps versus the specs
|
||||
|
||||
1. No **provider designation**: we neither read the viewer's own kind `10040`
|
||||
nor let them choose providers, and we cannot show which provider a score
|
||||
came from.
|
||||
2. **Negative signals are ignored**: mutes (kind `10000`) and user-level reports
|
||||
(NIP-56 `1984`) are not inputs to an author's score.
|
||||
3. **No confidence gate**: a single follow instantly reads as `90`. A cluster of
|
||||
accounts that only follow each other is indistinguishable from a well-connected
|
||||
account. This is the biggest sybil hole in the current ladder.
|
||||
4. **No trust-driven alerts**: the app filters content, but never tells a user
|
||||
"someone you follow has been reported by trusted people."
|
||||
5. **Display is a bare ladder**: no viewer choice in how (or whether) trust is
|
||||
shown, and no distinct "flagged" state next to "verified" and "unknown".
|
||||
|
||||
## Ideas, highest value first
|
||||
|
||||
### 1. Make the NIP-85 consumer spec-correct
|
||||
|
||||
Replace the hardcoded single-provider lookup with a real consumer:
|
||||
|
||||
- Read the viewer's **kind 10040** designation (rows are
|
||||
`["30382:<tag>", <provider pubkey>, <relay>]`) and resolve the provider and
|
||||
relay per assertion type. Fall back to a configured "house" provider (the
|
||||
community default) when the viewer has no designation or is logged out.
|
||||
- Verify `event.pubkey == provider` **and** the signature before use (the
|
||||
signature check already exists; the author check is the trust boundary).
|
||||
- Read the normative tags: `rank` (0-100), `followers`, `reporters`, `muters`,
|
||||
`hops`. Treat producer extensions as optional.
|
||||
- Enforce the honesty rules the spec is emphatic about:
|
||||
- an absent `hops` means "no known path", never `0`;
|
||||
- absence of an assertion is **not** distrust;
|
||||
- ranks from different observers or providers are **not** comparable;
|
||||
- presence of an assertion is **not** endorsement (read the counts).
|
||||
|
||||
Sketch of the shape: `src/lib/nip85.ts` (designation parse, provider resolve,
|
||||
assertion parse, verification) + `src/hooks/useTrustAssertions.ts` (batch
|
||||
read via `querySkateContent`/relay hints, disabled when no provider).
|
||||
`useRelatrScore.ts` becomes one configured provider behind that interface
|
||||
rather than a special case. A Settings section ("Trust providers") shows the
|
||||
active provider + relay, allows choosing the house provider or a personal one,
|
||||
and can be switched off.
|
||||
|
||||
This alone turns trust from our fixed 0/30/60/90/100 into "whatever provider
|
||||
the viewer chooses", with no new infrastructure.
|
||||
|
||||
### 2. Three buckets and a viewer display mode
|
||||
|
||||
Adopt a simple, honest presentation on top of whatever score source is active:
|
||||
|
||||
- **Verified / Unknown / Flagged**, with color **and** a glyph so the state is
|
||||
legible without color. "Flashy intermediates" (highly verified vs verified)
|
||||
were deliberately rejected — actionable, not graded.
|
||||
- **Flagged = verified reporters >= threshold**, where "verified reporters" are
|
||||
reporters who themselves clear the trust bar. Not a raw report count.
|
||||
Show *who* reported, grouped as people you follow / verified / others, never
|
||||
what the account "is".
|
||||
- Let the viewer choose how it renders: number / level / word / **off**. "Off"
|
||||
is a legitimate choice for people who do not want others graded; safety
|
||||
warnings (flag/report) remain a separate channel and always show.
|
||||
- Reuse the same ladder in the trust filter, author badges on the map/Lab/
|
||||
marketplace, and the community-stats page, so a score reads identically
|
||||
everywhere.
|
||||
|
||||
Where it lands: a small `lib/trustLadder.ts` (bucket + ladder helpers), a viewer
|
||||
preference stored per account (the `useLocalStorage` pattern), and reuse of the
|
||||
existing `Filter` UI. Content that is `Flagged` can keep our current soft
|
||||
behaviour (deprioritize, badge, and feed the review queue) rather than hard
|
||||
delete.
|
||||
|
||||
### 3. Trust-driven alerts for counterparties
|
||||
|
||||
The alert pattern is a strong fit for a marketplace of strangers and paid Lab
|
||||
services:
|
||||
|
||||
- Compute "accounts in your network that trusted people report/mute" from the
|
||||
provider's `reporters` / `muters` counts (or locally, from reports weighted
|
||||
by reporter trust).
|
||||
- Surface it as a dismissible banner plus a page of alerts, with actions
|
||||
**ignore / unfollow / mute / report** and a short interaction history
|
||||
(your follows, prior trades, prior reports). One row per account; a "new
|
||||
since you last looked" marker.
|
||||
- The posture is "inform, then let the user decide" — never auto-hide a seller
|
||||
or coach on a single report. This complements the existing reputation cards
|
||||
and the >=3-distinct-reporter auto-hide, not replace them.
|
||||
|
||||
### 4. Confidence-gated scoring
|
||||
|
||||
The single most important algorithmic idea from the specs: trust is
|
||||
`influence = average x confidence`, with `confidence = 1 - rho^input` and each
|
||||
edge weighted by the rater's own influence and an attenuation factor. A lone
|
||||
endorsement scores near zero; breadth of already-trusted raters is what
|
||||
accumulates. Defaults: `rho 0.5`, `attenuation 0.85`, follows `+1`, mutes and
|
||||
reports negative.
|
||||
|
||||
Two ways to get it, in order of preference:
|
||||
|
||||
- **Consume it** from a provider (the house provider, or a personal one) — no
|
||||
client cost, and the score is already published as a signed assertion.
|
||||
- **Port it** into `cvm-server/` (it is ~a dozen parameters and a bounded
|
||||
iteration) and publish our own assertions only if we decide to run a
|
||||
provider. This keeps the heavy work off the phone while staying inside our
|
||||
existing service.
|
||||
|
||||
Either way, the win is: stop treating one follow as trust. Our existing
|
||||
per-account cap and activity weighting in `useSpotCommunityScore` are the same
|
||||
instinct applied to content; this applies it to people.
|
||||
|
||||
### 5. Presets and negative signals as policy
|
||||
|
||||
- Expose scoring policy as named **presets** (Relax / Default / Strict) that
|
||||
move the follower/muter/reporter cutoffs together, rather than hardcoded
|
||||
constants. If we ever run a provider, persist presets server-side with an
|
||||
append-only change history and show the active one in Settings.
|
||||
- Add **mutes (10000)** and **user-level reports (1984)** as negative edges —
|
||||
weighted, so a report from a trusted skater counts and a report from a
|
||||
brand-new account does not. We already gather reports; this is where they
|
||||
should feed a person-level signal, not only content moderation.
|
||||
|
||||
### 6. Vocabulary and honesty copy
|
||||
|
||||
Borrow the precise wording these specs insist on, because it matches our own
|
||||
convention ("Conditions · weather only", "attested, not verified"):
|
||||
|
||||
- counts are "verified followers / reporters / muters" (trusted raters only);
|
||||
- "No one in your network has vouched for this account yet." for Unknown;
|
||||
- "presence is not endorsement", "absence is not distrust";
|
||||
- "scores are point-of-view-relative" — always attribute a score to a provider
|
||||
and a perspective, never present it as an objective fact.
|
||||
|
||||
## What we are not doing
|
||||
|
||||
- Standing up the reference infrastructure (graph store + cache + search engine
|
||||
+ Java worker + Python orchestrator + bundled relays). It contradicts the
|
||||
project's zero-server stance for this feature and is unnecessary if we consume
|
||||
a provider. At most, a provider we operate would live in `cvm-server/`.
|
||||
- Adopting an external search relay as a dependency. The portable idea is
|
||||
ranking our own search and Lab results by the viewer's trust, which the
|
||||
`trustFilter` already gestures at.
|
||||
|
||||
## Caveats
|
||||
|
||||
- **Licensing.** The reference implementation, server and client are
|
||||
AGPL-3.0. Reading the specifications and reimplementing the algorithm is fine;
|
||||
copying their code into our shipped client would pull AGPL obligations onto
|
||||
SkateSpots. Prefer the specs and our own implementation.
|
||||
- **Spec status.** GrapeRank and Trusted Assertions are pre-NIP drafts; NIP-85
|
||||
itself is published. Code against NIP-85 (`10040` / `30382`) and treat
|
||||
GrapeRank as one producer.
|
||||
- **Privacy.** The current client-side ladder fetches only the viewer's own
|
||||
follows and keeps the graph local. Consuming a provider discloses "this
|
||||
viewer designates this provider, score this subject"; operating a provider
|
||||
means someone holds the whole graph. Whatever we pick must be disclosed on
|
||||
`/privacy`, the way geocoding and the AI proxy already are.
|
||||
- **Existing code.** `lib/relatr.ts` should be retired (REST, disabled,
|
||||
unconfirmed); `useRelatrScore.ts` is the seed to grow from.
|
||||
|
||||
## Recommended first slice
|
||||
|
||||
1. **Spec-correct NIP-85 consumer** (`src/lib/nip85.ts` +
|
||||
`src/hooks/useTrustAssertions.ts`), with kind-10040 resolution, a configured
|
||||
house provider, author + signature verification, and the four normative tags.
|
||||
Keep `useRelatrScore` working by expressing it as a provider.
|
||||
2. **A Flagged bucket** fed by verified reporter counts, shown with a glyph in
|
||||
the trust filter and on author badges, with a "who reported" breakdown.
|
||||
|
||||
This reuses code we already have, needs no new infrastructure, and improves the
|
||||
map, Lab, marketplace and moderation without changing the scoring model. The
|
||||
confidence model (idea 4) and alerts (idea 3) are the natural next steps.
|
||||
|
||||
## Touch points
|
||||
|
||||
| Concern | File(s) |
|
||||
| --- | --- |
|
||||
| NIP-85 designation + assertion parsing | new `src/lib/nip85.ts` |
|
||||
| Assertion read hook (batch) | new `src/hooks/useTrustAssertions.ts` |
|
||||
| Current single-provider score | `src/hooks/useRelatrScore.ts` |
|
||||
| Placeholder REST client (retire) | `src/lib/relatr.ts` |
|
||||
| Local ladder (keep, or blend) | `src/hooks/useTrustScore.ts`, `src/hooks/useBatchTrustScores.ts` |
|
||||
| Bucket/ladder helpers + viewer mode | new `src/lib/trustLadder.ts` |
|
||||
| Filter levels | `src/lib/trustFilter.ts`, `src/components/TrustFilter.tsx` |
|
||||
| Author badges | map / Lab / marketplace author surfaces |
|
||||
| Alerts | new alerts page + banner; report hooks already exist |
|
||||
| Policy presets | Settings; server-side only if we run a provider |
|
||||
| Privacy disclosure | `/privacy` |
|
||||
Loading…
Reference in a new issue