fix(events): key the events store by addressable coordinate (#121) #125

Merged
padreug merged 1 commit from fix/events-store-coordinate-key into dev 2026-06-18 12:03:10 +00:00
Owner

Closes the gap from the nostr-patterns review against NDK + welshman. Fixes #121.

Problem

NIP-52 calendar events (kinds 31922/31923) are addressable — their d-tag is author-scoped. The store keyed eventsMap by event.id (bare d-tag) and replaced on newer created_at ignoring pubkey, so a different author republishing the same d-tag with a newer timestamp could overwrite a legit event in the store (cross-author hijack). NDK (event.coordinate()) and welshman (eventsByAddress) both key addressable events by the full kind:pubkey:d-tag coordinate.

Fix (client-side only)

  • Key eventsMap by eventCoordinate() = ${kind}:${pubkey}:${dtag}; same-coordinate-newer-wins, different authors stored independently.
  • Keep the d-tag as the route identifier: getEventById(dtag) scans and returns the newest match (single-publisher in practice), so existing URLs/links are unchanged. Add getByCoordinate() for precise, author-known lookups.
  • removeEvent(dtag) deletes every coordinate sharing that d-tag.

No demo-DB surgery — the store is rebuilt from relays each session.

Tests

src/modules/events/stores/events.spec.ts: same-coordinate newer-wins / older-ignored, same-d-tag different-pubkey → both kept (no overwrite), getEventById resolves by d-tag (and newest-wins across authors), removeEvent by d-tag, coordinate/kind formatting. Run with pnpm test.

Merge order

Branches off chore/vitest-setup (the vitest PR) — merge that first; this PR's diff drops the vitest files once it lands.

🤖 Generated with Claude Code

Closes the gap from the nostr-patterns review against NDK + welshman. Fixes #121. ## Problem NIP-52 calendar events (kinds 31922/31923) are **addressable** — their d-tag is author-scoped. The store keyed `eventsMap` by `event.id` (bare d-tag) and replaced on newer `created_at` **ignoring pubkey**, so a different author republishing the same d-tag with a newer timestamp could **overwrite** a legit event in the store (cross-author hijack). NDK (`event.coordinate()`) and welshman (`eventsByAddress`) both key addressable events by the full `kind:pubkey:d-tag` coordinate. ## Fix (client-side only) - Key `eventsMap` by `eventCoordinate()` = `${kind}:${pubkey}:${dtag}`; same-coordinate-newer-wins, different authors stored independently. - Keep the d-tag as the **route identifier**: `getEventById(dtag)` scans and returns the newest match (single-publisher in practice), so existing URLs/links are unchanged. Add `getByCoordinate()` for precise, author-known lookups. - `removeEvent(dtag)` deletes every coordinate sharing that d-tag. No demo-DB surgery — the store is rebuilt from relays each session. ## Tests `src/modules/events/stores/events.spec.ts`: same-coordinate newer-wins / older-ignored, **same-d-tag different-pubkey → both kept (no overwrite)**, `getEventById` resolves by d-tag (and newest-wins across authors), `removeEvent` by d-tag, coordinate/kind formatting. Run with `pnpm test`. ## Merge order Branches off `chore/vitest-setup` (the vitest PR) — **merge that first**; this PR's diff drops the vitest files once it lands. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
No test runner existed in the repo. Add vitest (node env, *.spec.ts
discovery) with a minimal config mirroring only the `@`→src alias, plus
`test`/`test:watch` scripts and a smoke test as a known-good baseline.

Precursor for the nostr-patterns review fixes (events store coordinate
keying #121, monotonic created_at #122), which ship with unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NIP-52 calendar events (kinds 31922/31923) are addressable: their d-tag
is author-scoped, so the replacement key is kind:pubkey:d-tag, not the
bare d-tag. The store keyed `eventsMap` by `event.id` (d-tag) and
replaced on newer `created_at` ignoring pubkey, so a different author
republishing the same d-tag could overwrite a legit event in the store
(cross-author hijack). NDK (`event.coordinate()`) and welshman
(`eventsByAddress`) both key addressable events by the full coordinate.

- Key `eventsMap` by `eventCoordinate()` = `${kind}:${pubkey}:${dtag}`;
  same-coordinate-newer-wins replacement, different authors stored apart.
- Keep the d-tag as the route identifier: `getEventById(dtag)` scans and
  returns the newest match (single-publisher in practice). Add
  `getByCoordinate()` for precise, author-known lookups.
- `removeEvent(dtag)` deletes every coordinate sharing that d-tag.

Client-side only — the store is rebuilt from relays each session, so no
demo-DB surgery. Covered by vitest unit tests including the cross-author
no-overwrite case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
padreug deleted branch fix/events-store-coordinate-key 2026-06-18 12:03:10 +00:00
Sign in to join this conversation.
No description provided.