A did:nostr social-graph indexer — it indexes Nostr profiles (kind 0), follows (kind 3, the social graph), and relay lists (kind 10002) for pubkeys into MongoDB, and serves them over a small read API.
Useful as the identity substrate for SSO: "sign in with did:nostr", plus trust / discovery over the follow graph.
This is a clean reimplementation of nostr-labs/nostr-beacon, keeping the same Mongo schema to start (for regression). Per the RFC, DID-document generation will delegate to jss's buildDidDocument rather than maintaining a second, divergent generator (the source of the bugs in nostr-labs/nostr-beacon#3).
Each collection stores the raw Nostr event, keyed by pubkey, latest-wins by created_at:
| data | kind | collection |
|---|---|---|
| profiles | 0 | beacon |
| follows (social graph) | 3 | follows |
| relay lists | 10002 | relay_lists |
(Collection names are configurable via env so the same Mongo can be read/written interchangeably.)
npm install
cp .env.example .env # set MONGODB_URI and relays
npm start # indexer + read API
# or run separately:
npm run index # indexer only
npm run serve # read API only
# indexer flags (override the matching env var; see --help):
node index.js --hoses profiles --no-legacy # run a single hose, no legacy fallback
node src/indexer.js --help
GET /api/profiles— recent profilesGET /api/profile/:pubkeyGET /api/follows/:pubkeyGET /api/relays/:pubkey— that pubkey's kind-10002 relay listGET /api/relays-directory— relay-health directory (?online=1,?sort=quality|latency|recent,?limit=)
v0 — MVP: indexer + read API + the regression-compatible schema, plus the DID-document resolution endpoint (/.well-known/did/nostr/:pubkey.json via jss's buildDidDocument) and a /relays health directory.
The indexer is being reworked into composable hoses (src/hoses/), each owning a set of event kinds, all sharing one schnorr signature check (src/hoses/event.js) so a relay can't inject forged data. Which hoses run is a switch — the HOSES env (comma list of names; default: all).
- Profiles (kind 0) — verifies + stores the raw event latest-wins, owns its indexes incl. the
content_textsearch index. - Follows (kind 3) — verifies + stores the derived social-graph shape
{ pubkey, follows:[hex…], count }(so in-degree works), and harvests relay URLs from legacy kind-3 content into therelaysdirectory. - Relay lists (kind 10002) — verifies + stores the raw event latest-wins (the shape the DID document's
serviceentries are built from), and harvests ther-tag URLs into therelaysdirectory (the canonical relay-URL source).
Every kind is now hose-owned; the legacy raw-upsert fallback is empty.
Separately, an active relay-health prober (node probe.js / npm run probe) keeps the relays directory fresh: it dials every known relay for reachability + latency, reads the NIP-11 info doc for auth/payment requirements, and rolls up uptime. It self-schedules (PROBE_INTERVAL, default hourly) or runs a single sweep with --once. It's not a hose — it's active, not a subscription.
MIT