Skip to content

feat: add NIP-91 AND filter support - #44

Merged
erskingardner merged 1 commit into
erskingardner:masterfrom
hzrd149:feature/nip-91
Aug 18, 2026
Merged

feat: add NIP-91 AND filter support#44
erskingardner merged 1 commit into
erskingardner:masterfrom
hzrd149:feature/nip-91

Conversation

@hzrd149

@hzrd149 hzrd149 commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • implement draft NIP-91 &x tag filters with all-values-required semantics
  • normalize compatibility #x filters so AND values do not satisfy the residual OR clause
  • use one required tag as the historical/live index seed, then verify the complete packed event
  • support NIP-91 across REQ, live delivery, search, COUNT, router, sync, and negentropy
  • advertise NIP-91 and configurable max_and_entries/max_tags_per_filter limits through NIP-11

The implementation follows nostr-protocol/nips#2252 at b93bda29d45998866e81c65e0693616294a78672 and takes the overlap-normalization approach from hoytech/strfry#162.

Safety and compatibility

  • reject malformed, empty, oversized, and invalid hex AND filters
  • force full-event verification rather than unsafe index-only delivery
  • omit NIP-45 HLL sketches for ambiguous AND filter shapes
  • preserve restricted COUNT privacy rules
  • post-filter results received from relays that only understand the compatibility # clause
  • keep persisted negentropy trees usable if the live AND-entry limit is lowered

Testing

  • cargo fmt --all --check
  • cargo check --workspace --all-targets --locked
  • cargo clippy --workspace --all-targets --locked -- -D warnings
  • cargo test -p wok-query --locked
  • cargo test -p wok-compat --test nip_conformance --locked (27 passed)
  • cargo test -p wok-compat --test e2e_transports --locked -- --test-threads=1 (12 passed)
  • workspace tests excluding wok-bench passed; one parallel LMDB allocation failure was confirmed transient by the serial E2E run

Includes unit, property, historical scan, live monitor, exact COUNT, router/sync fallback, negentropy, conformance, E2E, and fuzz coverage.

@erskingardner erskingardner left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The NIP-91 matcher, overlap normalization, forced full-event verification, HLL omission, and limit plumbing look correct and well covered. Requesting changes for a compile break in the separate fuzz workspace; the other notes are interop/cost follow-ups.

Policy question, not a code defect: this advertises draft NIP-91 from nips#2252, which is not in the conformance pin 656cecc7…. That is a deliberate exception to “canonical NIPs it advertises.” Fine if maintainers want it, but it should be an explicit call rather than landing as if 91 were already in the pin.

Comment thread fuzz/fuzz_targets/ingress.rs
Comment thread crates/wok-cli/src/router.rs
Comment thread crates/wok-query/src/filter.rs Outdated
Comment thread crates/wok-query/src/scan.rs Outdated
@hzrd149

hzrd149 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Pushed follow-up commit 50227d2 addressing the review feedback:

  • added the missing wok-query dependency to the standalone fuzz workspace
  • aligned query admission cost with the tag/AND index actually selected
  • select the smallest AND tag set as the historical scan seed
  • added capability-aware forwarding for router REQs: fetch/cache upstream NIP-11 and preserve &* only when NIP 91 is explicitly advertised; otherwise send the # compatibility superset and retain exact local post-filtering
  • applied the same NIP-91 filter handling to sync NEG-OPEN
  • require upstream NIP 77 advertisement before sending NEG-OPEN
  • coalesce concurrent NIP-11 lookups per normalized relay URL, with bounded process-local caching (1h valid results, 5m failures, 256 relays)
  • retained Wok conditional NIP-91 advertisement when AND filtering is enabled

Validation completed:

  • cargo fmt --all --check
  • cargo check --workspace --all-targets --locked
  • cargo test -p wok-query --locked
  • relay capability tests
  • 9 mesh/NIP-11 cache and forwarding tests
  • cargo check --manifest-path fuzz/Cargo.toml --bin ingress --locked

The policy question about advertising draft NIP-91 remains an explicit maintainer decision; the implementation continues the PR behavior and advertises it only when max_tags_per_filter > 0 and max_and_entries > 0.

@erskingardner erskingardner left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Previous findings are fixed: fuzz depends on wok-query, query cost follows the tag/AND index, AND seed picks the smallest AND set, and outbound filters are capability-aware.

The NIP-91 matcher is still in good shape, and advertising draft 91 is an accepted maintainer call.

Requesting changes for two follow-up issues in the new NIP-11 forwarding path. After those, this looks mergeable (CI has not reported on 50227d2 yet).

Comment thread crates/wok-cli/src/mesh.rs Outdated
Comment thread crates/wok-cli/src/mesh.rs Outdated
Comment thread crates/wok-query/src/scan.rs Outdated

@erskingardner erskingardner left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Previous request-changes items are fixed on f17c044.

  • Compatibility path folds each &x set into #x instead of dropping keys, so an &-only filter cannot become {} / a remote firehose. Malformed & clauses still fail before the NIP-11 lookup, and filter arrays are rewritten element-wise.
  • NIP-11 is used only to decide NIP-91; NEG-OPEN is left to prove 77. Unreachable or malformed documents degrade to the compatibility filter.
  • Historical scans seed one cursor from the smallest AND set; estimated_cost charges that seed. nip91_and_tag_seeds_a_single_cursor covers the cursor-count regression.

The matcher, overlap normalization, forced full-event verification, HLL omission, restricted COUNT scoping, live monitors, search does_match_without_search, and limit plumbing still look correct. Draft NIP-91 advertisement remains the accepted maintainer exception, gated on both tag-filter limits being nonzero.

GitHub has not reported checks on this head; I reviewed the diff and tests as written, not a CI run of f17c044.

Implement draft NIP-91 AND tag filters across historical queries, live subscriptions, COUNT, router forwarding, and negentropy. Add capability-aware compatibility forwarding, preserve exact local post-filtering, align query cost and cursor selection, and document and configure the filter limits.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@erskingardner
erskingardner merged commit 43b0555 into erskingardner:master Aug 18, 2026
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.

2 participants