Skip to content

fix: bunker replay protection + sync quota fallback (deferred audit items)#2

Open
vveerrgg wants to merge 1 commit into
fix/nsec-import-and-backupfrom
harden/sync-bunker-followups
Open

fix: bunker replay protection + sync quota fallback (deferred audit items)#2
vveerrgg wants to merge 1 commit into
fix/nsec-import-and-backupfrom
harden/sync-bunker-followups

Conversation

@vveerrgg

Copy link
Copy Markdown
Collaborator

Follow-up to #1 — the two contained, well-tested deferred items from the audit pass. Stacked on fix/nsec-import-and-backup; will retarget to main once #1 merges.

Bunker replay protection (bunker-server.js)

The NIP-46 server signs requests from authenticated clients. Without replay protection, a captured authenticated sign_event event could be re-published to the relay and re-signed.

  • _isFreshEvent() rejects request ids already processed (bounded seen-set, 500) and events whose created_at is outside a ±5min window.
  • State cleared on stop().

Sync quota fallback (sync-manager.js)

storage.sync.set is all-or-nothing: an over-quota batch was rejected whole and silently synced nothing — not even P1 profiles.

  • setSyncRespectingQuota() drops the lowest-priority entries and retries, guaranteeing P1 (profiles) syncs. Logs what it dropped (no silent truncation). Throws only if P1 + meta alone exceeds quota.

Tests

  • test/bunker-replay.test.js (7), test/sync-quota.test.js (4). Full suite: 181 passed, 2 skipped.

Deliberately NOT included — needs a design decision

Sync deletion tombstones. While scoping it I found vault docs live on Nostr relays (NIP-78) and vault.delete publishes a NIP-09 deletion to relays — storage.sync is only a mirror of a local cache. So deletion propagation is entangled with the relay layer, not a pure sync-schema addition. It needs a dedicated design pass to reconcile the relay deletion path with the sync mirror, rather than a half-correct tombstone bolted onto storage.

🤖 Generated with Claude Code

Two deferred items from the audit pass:

- Bunker replay protection: the NIP-46 server now rejects request events whose
  id was already processed and events whose created_at is outside a ±5min
  window. A captured authenticated sign_event could otherwise be re-published
  to the relay and re-signed. Seen-id set is bounded (500) and cleared on stop().

- Sync quota fallback: storage.sync.set is all-or-nothing, so an over-quota
  batch was rejected whole and silently synced nothing — not even P1 profiles.
  setSyncRespectingQuota now drops the lowest-priority entries and retries,
  guaranteeing high-priority data syncs, and logs what was dropped (no silent
  truncation). Throws only if even P1 + meta exceeds quota.

Tests: bunker-replay (7) and sync-quota (4) suites (+11).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant