Use monotonic created_at when publishing replaceable events #122

Closed
opened 2026-06-18 11:20:56 +00:00 by padreug · 0 comments
Owner

From the nostr-patterns review — and directly the lesson from the ticket-count live-update debugging.

Problem

Relays only push a replaceable update to open subscriptions when created_at is strictly newer (verified against our relay). Publishers that stamp created_at = floor(Date.now()/1000) (second resolution) can emit two versions in the same second — the relay treats the second as not-newer and won't propagate it to live subscribers (it only shows on a reload/re-query).

useBookmarks (NIP-51 kind-10003) republishes the whole list on every toggle with second-resolution created_at — rapid toggles can collide. (The aiolabs/events extension's nostr_publisher uses int(time.time()) too — tracked separately.)

Proposed fix

When publishing a replaceable event, use a monotonic timestamp: created_at = max(floor(Date.now()/1000), lastKnownCreatedAt + 1). Apply in useBookmarks (webapp); file/flag the same for the events extension.

Testing

Unit test the timestamp helper: two publishes "in the same second" produce strictly increasing created_at; a publish after a future-dated prior bumps past it.

To be done as a PR (gap #2 of the review).

From the nostr-patterns review — and directly the lesson from the ticket-count live-update debugging. ## Problem Relays only **push a replaceable update to open subscriptions when `created_at` is strictly newer** (verified against our relay). Publishers that stamp `created_at = floor(Date.now()/1000)` (second resolution) can emit two versions in the **same second** — the relay treats the second as not-newer and won't propagate it to live subscribers (it only shows on a reload/re-query). `useBookmarks` (NIP-51 kind-10003) republishes the whole list on every toggle with second-resolution `created_at` — rapid toggles can collide. (The `aiolabs/events` extension's `nostr_publisher` uses `int(time.time())` too — tracked separately.) ## Proposed fix When publishing a replaceable event, use a **monotonic** timestamp: `created_at = max(floor(Date.now()/1000), lastKnownCreatedAt + 1)`. Apply in `useBookmarks` (webapp); file/flag the same for the events extension. ## Testing Unit test the timestamp helper: two publishes "in the same second" produce strictly increasing `created_at`; a publish after a future-dated prior bumps past it. _To be done as a PR (gap #2 of the review)._
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/webapp#122
No description provided.