Files
Artstr-Studio/docs/TRUST_AND_BLOCK_FEATURE.md
T

7.5 KiB
Raw Blame History

Trust, Mute, and Web-of-Trust (WoT) Plan

Goals

Build an abuse-resistant community feed that still feels open and discoverable, using only Nostr-native data and client-side logic.

Product goals

  1. Reduce spam and low-quality content quickly.
  2. Keep control in users’ hands (local + portable moderation).
  3. Preserve feed performance for both logged-out and logged-in users.
  4. Ship in small phases with measurable outcomes.

Non-goals (v1)

  • Centralized server moderation.
  • Perfect Sybil resistance.
  • Cross-relay consensus on reports.

Current architecture assumptions

  • Identity and signing via NIP-07 browser extension.
  • Community templates are already rendered from Nostr events.
  • App can query multiple relays and currently has a feed render pipeline.

Key Nostr primitives

NIP Kind Usage in this feature
NIP-02 3 Follow graph (hop-1, optional hop-2)
NIP-51 10000 User mute list (p tags, optional words/events)
NIP-56 1984 Abuse reports (advisory to relays/tools)

Phased delivery plan

Phase 1 — Local block list (works logged out)

Why first

Immediate abuse relief, zero protocol dependencies, and easy rollback.

Data model

  • localStorage['casewrap:block:list:v1'] = string[] (hex pubkeys)
  • Normalize pubkeys to lowercase hex before insert.
  • Keep a companion metadata object for analytics/debug (optional):
    • localStorage['casewrap:block:meta:v1'] = { [pubkey]: { createdAt, source } }

UX

  • Card menu action: Block author.
  • Settings section: list blocked pubkeys + unblock button.
  • Inline feed toast: “Author blocked. Undo”.
  • Badge in settings: “N authors hidden”.

Engineering notes

  • Block filter is applied as the first feed filter stage.
  • Use a Set in memory to avoid repeated array scans.
  • Add defensive guard for malformed pubkeys.

Acceptance criteria

  • Blocking hides author content immediately in all feed views.
  • Unblock restores content without full refresh.
  • Block state persists across refresh.

Phase 2 — Mute list sync (NIP-51)

Why second

Makes moderation portable across devices and compatible with other Nostr clients.

Sync strategy

On successful NIP-07 login:

  1. Fetch latest kind 10000 authored by viewer pubkey.
  2. Parse p tags into remoteMuted set.
  3. Read local block set localMuted.
  4. Merge union -> effectiveMuted.
  5. If union differs from remote, publish updated kind 10000.
  6. Cache effectiveMuted locally.

Conflict policy

  • Union wins by default (safer for abuse prevention).
  • Never silently remove remote mutes.
  • If publish fails, keep local behavior and surface non-blocking warning.

Event shape

{
  "kind": 10000,
  "tags": [["p", "<hex-pubkey>"]],
  "content": ""
}

Acceptance criteria

  • Existing remote mute list is respected on login.
  • New block actions publish updated mute list.
  • Local filter still works if relays/signing fail.

Phase 3 — WoT-aware ranking and filtering

Why third

Highest feed-quality impact for signed-in users.

Trust tiers

  • Blocked: in effective mute set -> hidden.
  • Trusted (hop-1): directly followed by viewer.
  • Extended (hop-2): followed by someone viewer follows.
  • Unknown: no graph evidence.

Modes

  • Default mode: rank by trust, don’t hard-hide unknown.
  • Optional strict mode toggle: show trusted only.
  • Optional toggle: enable/disable hop-2 expansion.

Ranking algorithm (v1)

Sort key tuple:

  1. Tier priority (trusted > extended > unknown)
  2. Optional social score (# of hop-1 followers who follow author)
  3. Existing recency score (keep current feed freshness behavior)

Performance constraints

  • Fetch hop-1 first; render immediately with partial trust state.
  • Compute hop-2 only if user enables it.
  • Cache graph in sessionStorage with TTL (e.g., 15 min).
  • Hard cap hop-2 expansion fanout (e.g., max 500 hop-1 nodes queried).
  • Use batched relay subscriptions; close on EOSE.

Acceptance criteria

  • Trust tiers display correctly for sampled known authors.
  • “Trusted only” mode hides extended + unknown.
  • Feed first paint is not blocked by hop-2 queries.

Phase 4 — Reporting (NIP-56)

Why fourth

Valuable ecosystem signal but lower immediate user impact than blocking/filtering.

UX

  • Card menu -> Report.
  • Reason required (spam, nudity, illegal, impersonation, other).
  • Optional note.
  • Post-submit option: “Also block this author”.

Event shape

{
  "kind": 1984,
  "tags": [
    ["e", "<template-event-id>", "spam"],
    ["p", "<author-pubkey>"]
  ],
  "content": "Optional detail"
}

Acceptance criteria

  • Signed event is published with valid target tags.
  • Failures are surfaced to user; no silent drop.

Cross-cutting concerns

Security & abuse hardening

  • Validate all incoming pubkeys/event ids before use.
  • Debounce block/report UI actions to avoid duplicate publishes.
  • Protect against relay poisoning by taking latest replaceable event only.

Privacy

  • WoT computation occurs client-side.
  • Do not log raw social graph by default.
  • If telemetry is added, aggregate counts only.

Track coarse metrics:

  • % feed hidden by block list
  • % users enabling trusted-only
  • median feed render time (logged in/out)
  • mute sync success rate

Failure handling

  • If NIP-07 unavailable: Phase 1 fully operational; hide sync/report controls.
  • If relay read fails: preserve local cache and degrade gracefully.
  • If relay publish fails: keep local state, show retry affordance.

Implementation slices (suggested task breakdown)

  1. Data layer: block list store + normalization + helpers.
  2. Feed pipeline: filter + tiering + ranking hooks.
  3. UI: card menu actions, settings controls, trust badges.
  4. Nostr sync: kind 10000 read/merge/publish.
  5. Reporting: kind 1984 modal and publish flow.
  6. Instrumentation: lightweight counters/timers.

Revised testing plan

Unit tests

  • Pubkey normalization and dedupe logic.
  • Union merge behavior for local vs remote mute lists.
  • Trust-tier classifier and rank sort stability.

Integration tests

  • Logged-out: block/unblock lifecycle across refresh.
  • Logged-in: mute sync read/merge/publish happy path.
  • Logged-in degraded: relay failure + signer rejection handling.
  • Trusted-only toggle behavior with mixed-tier fixture feed.

Performance tests

  • Feed render timing with:
    • 0 blocks
    • 500 blocked pubkeys
    • hop-1 only
    • hop-1 + capped hop-2

Manual QA checklist

  • Block author from card and confirm immediate hide.
  • Undo block toast restores card.
  • Login merges local + remote mute sets without loss.
  • Trust badges are accurate for controlled fixture accounts.
  • Trusted-only mode does not show unknown authors.
  • Report flow publishes kind 1984 and handles failure visibly.

Is the original plan good enough?

Short answer: good foundation, but not quite “as good as it could be” for production.

What it already did well

  • Correct NIP choices.
  • Practical phased rollout.
  • Good focus on no-backend architecture.

Biggest gaps this revision closes

  • Explicit conflict/merge policy for mute sync.
  • Concrete degradation behavior on relay/signing failure.
  • Performance guardrails for hop-2 fanout.
  • Better UX defaults (rank-first vs hide-first).
  • Test strategy split by unit/integration/perf.
  • Observability so you can prove impact after launch.