Skip to content

Firehose phase 1: validated profiles hose (kind 0) + extensible hose seam #7

Description

@melvincarvalho

First step of reworking the firehose into owned, extensible code. We do it in phases — this issue is profiles only — but lay down a seam later phases (follows, relay lists, relay health) plug into without rewrites.

Background

Today src/indexer.js opens one subscription on kinds [0,3,10002] and blindly raw-upserts every event via upsertEvent. Two problems for profiles specifically:

  1. No validation — a malicious relay can inject a forged kind-0 and we'd store it as a did:nostr identity. beacon is an identity/SSO substrate, so profile integrity matters.
  2. Indexes aren't owned by codeconnect() only creates {pubkey:1}. The content_text index that searchProfiles ($text) depends on exists only because it was created by hand; a fresh deploy silently breaks profile search.

(Legacy context: on nostr.social the old firehose.js/followshose.js/relayhose.js each do this per-kind and crash-loop; consolidating them is a later phase.)

Scope — phase 1 (profiles, kind 0)

  • Hose seam: introduce a small hose contract — { name, kinds, ensureIndexes(db), ingest(event, db) } — and a src/hoses/ dir. Later phases add sibling modules.
  • src/hoses/profiles.js: the first concrete hose.
    • Signature verification via @noble/curves (schnorr) + @noble/hashes (sha256): recompute the NIP-01 event id, reject id/sig mismatches. Keeps the repo's "no nostr-tools" approach.
    • Structural checks: kind 0, 64-hex pubkey, finite created_at, JSON-parseable content (empty allowed).
    • Owns its indexes: pubkey, created_at, and the content_text text index (so search works on a fresh deploy).
    • Latest-wins upsert into the beacon collection (unchanged shape).
  • Wire indexer.js: route kind-0 through the profiles hose; kinds 3 and 10002 keep the existing upsertEvent path unchanged (migrated in their own phases). Call ensureIndexes at startup.
  • Tests: sign events with a known key and assert accept/reject (valid, tampered content, bad sig, non-hex pubkey, wrong kind) + latest-wins.

Explicitly out of scope (later phases)

  • Follows hose (kind 3) + relay-URL harvesting — phase 2
  • Relay-lists hose (kind 10002) — phase 3
  • Active relay-health prober (unfreezes /relays) — phase 4
  • Deploying / retiring the legacy server hoses — phase 5

Acceptance

  • npm test green, including new profiles-hose tests.
  • Forged/tampered kind-0 events are rejected; valid ones upsert latest-wins.
  • A fresh runIndexer() creates the content_text index so /api/search works without manual setup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions