Skip to content

feat(nip-01): add rate-limited backoff hint to OK messages - #807

Merged
chappie-daemon merged 3 commits into
mainfrom
feat/nip01-rate-limit-backoff
Oct 10, 2026
Merged

chappie-daemon merged 3 commits into
mainfrom
feat/nip01-rate-limit-backoff

Conversation

@Ferryx349

Copy link
Copy Markdown
Collaborator

Description

Implements the optional NIP-01 rate-limited backoff param (nostr-protocol/nips#2498).

When an event is rate-limited, the relay now sends:
["OK", , false, "rate-limited: slow down", ""]

  • The backoff is the period (ms) of the rate-limit window that tripped: a conservative, approximate hint as the NIP allows, so it works for both EWMA and sliding-window limiters with no limiter or config changes.
  • If the rate limiter backend is unavailable (fail-closed), the param is omitted.
  • createCommandResult / createEventCommandResult accept an optional trailing params; the 4-element form is unchanged for all other OK messages.
  • CLOSED is untouched, since nostream never sends rate-limited on it.

Related Issue

Closes :- #805

Motivation and Context

How Has This Been Tested?

  • Unit tests: 5-element OK message with backoff, 4-element fallback when no hint, isRateLimited returns the tripped period, createCommandResult with params.
  • pnpm run lint, pnpm run build:check, pnpm run test:unit all pass.

Screenshots (if appropriate):

Types of changes

  • Non-functional change (docs, style, minor refactor)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my code changes.
  • I added a changeset, or this is docs-only and I added an empty changeset.
  • All new and existing tests passed.

@changeset-bot

changeset-bot Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 618bfd9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
nostream Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@greptile-apps

greptile-apps Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

Adds an optional backoff hint to rate-limited OK messages.

  • Rate-limited event replies can include a wait hint.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Event reaches rate-limit check] --> B{Result}
  B -->|false| C[Continue event checks]
  B -->|Window period| D[Reject with five-element OK and backoff]
  B -->|Backend failure| E[Reject with four-element OK]
Loading

Reviews (1) · Last reviewed commit: "feat(nip-01): add rate-limited backoff h..." · Reviewed by Greptile

@coveralls

coveralls commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Coverage Status

coverage: 73.172% (+0.004%) from 73.168% — feat/nip01-rate-limit-backoff into main

@cameri

cameri commented Oct 10, 2026

Copy link
Copy Markdown
Owner

From Muse:

Independent check of the math: the EWMA decay (λ = ln(2)/period, R_new = R_old·e^(−λΔt) + step) is textbook-correct with period as the half-life, units consistent (ms throughout). Math.max(1, Math.ceil(period)) is a valid conservative hint per the NIP's "MAY be approximate" — it overestimates when barely over the limit and self-corrects via re-rejection when way over. One docs nit: #805 says the NIP specifies seconds, but the current NIP PR text specifies milliseconds — the PR matches the NIP, the issue text is stale.

@chappie-daemon chappie-daemon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed (chappie-daemon). Approved.

Verified against the proposal this implements (nostr-protocol/nips#2498, still OPEN): the fifth param is an optional zero-free decimal string of a positive integer in milliseconds, MAY be approximate, and its absence MUST NOT be read as an instruction to retry immediately. The implementation matches on every point I could test:

  • The hint is the tripped window's period (ms) via Math.max(1, Math.ceil(period)) — always a positive, zero-free integer, approximate by construction (a sliding window's true wait depends on when old events age out; EWMA decays continuously), which the proposal explicitly permits. Sent as a string (String(rateLimited)), matching the spec's decimal-string form.
  • Fail-closed path returns true rather than a number, so the param is omitted — and the NIP's 'absence MUST NOT be read as retry immediately' makes that safe.
  • Only one rate-limited OK sender exists (event-message-handler); the connection-level limiter in web-socket-server-adapter terminates the socket without a protocol message and is untouched, so the 'CLOSED is untouched' claim in the body is accurate.
  • Backward compatible: the 4-element form is unchanged when no hint exists; positional clients reading [0..3] see no difference.

Ran the branch (618bfd9): pnpm run test:unit 2116 passing / 0 failing, pnpm run lint clean (biome, 492 files), pnpm run build:check (tsc --noEmit) clean. Changeset is a correct minor.

One observation, not a finding: period has no >0 validation for limits.event.rateLimits in settings-config.ts (only pow.periodMs is validated), so a misconfigured period of 0 would surface as a 1ms hint after the Math.max(1, ...) clamp. The clamp keeps the protocol value valid regardless; noting it only because a settings guard could be a cheap follow-up.

@chappie-daemon
chappie-daemon merged commit f7d4b76 into main Oct 10, 2026
15 of 16 checks passed
@chappie-daemon
chappie-daemon deleted the feat/nip01-rate-limit-backoff branch October 10, 2026 12:36
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.

4 participants