filter: add per-account filters_enabled, default off #389

Open
opened 2026-08-10 19:28:10 +00:00 by KofTwentyTwo · 0 comments
KofTwentyTwo commented 2026-08-10 19:28:10 +00:00 (Migrated from github.com)

Stops an active duplication defect. Ships independently of everything else.

Every engine independently classifies the same message as new (existing_message_ids is per-engine), and pending_remote_mutations has no uniqueness constraint. State-mutating actions converge — ten MOVEs produce one net effect. FORWARD and CALL WEBHOOK do not. Ten engines send ten forwards and ten signed webhook POSTs per matched message; an on-call engineer is paged ten times per alert.

generate_rule_id uses OsRng, so the "same" rule has a different id on every engine and no receiver can dedup from rule identity even in principle.

Scope

  • Per-account filters_enabled, default off — exactly one engine owns filter execution per account.
  • Make generate_rule_id a content hash of normalized NSQL.
  • Send Idempotency-Key: hash(rule_key, message_key) on webhooks and a deterministic dedup header on forwards, so receivers can dedup what we cannot make once-only.
  • Document the semantics: filter execution is at-least-once per engine; MOVE/COPY/FLAG/MARK READ converge under it, FORWARD/CALL WEBHOOK/DELETE do not.

Note

Default-off is a production behavior change — filters stop running until explicitly enabled. That is deliberate: it converts a silent duplication bug into an explicit, documented limitation.

A server-hosted claim (+FLAGS.SILENT (UNCHANGEDSINCE n) (\$NuncioFiltered)) is the eventual answer where CONDSTORE and custom keywords exist; it depends on the conditional-mutation work and is tracked separately.

Design: ADR 0002 · review evidence (PR #385)

**Stops an active duplication defect. Ships independently of everything else.** Every engine independently classifies the same message as new (`existing_message_ids` is per-engine), and `pending_remote_mutations` has no uniqueness constraint. State-mutating actions converge — ten `MOVE`s produce one net effect. **`FORWARD` and `CALL WEBHOOK` do not.** Ten engines send ten forwards and ten signed webhook POSTs per matched message; an on-call engineer is paged ten times per alert. `generate_rule_id` uses `OsRng`, so the "same" rule has a different id on every engine and no receiver can dedup from rule identity even in principle. ### Scope - Per-account `filters_enabled`, **default off** — exactly one engine owns filter execution per account. - Make `generate_rule_id` a content hash of normalized NSQL. - Send `Idempotency-Key: hash(rule_key, message_key)` on webhooks and a deterministic dedup header on forwards, so receivers *can* dedup what we cannot make once-only. - Document the semantics: filter execution is at-least-once per engine; MOVE/COPY/FLAG/MARK READ converge under it, FORWARD/CALL WEBHOOK/DELETE do not. ### Note Default-off is a **production behavior change** — filters stop running until explicitly enabled. That is deliberate: it converts a silent duplication bug into an explicit, documented limitation. A server-hosted claim (`+FLAGS.SILENT (UNCHANGEDSINCE n) (\$NuncioFiltered)`) is the eventual answer where CONDSTORE and custom keywords exist; it depends on the conditional-mutation work and is tracked separately. Design: [ADR 0002](https://github.com/KofTwentyTwo/nuncio/blob/dev/docs/adr/0002-multi-engine-sync-model.md) · [review evidence](https://github.com/KofTwentyTwo/nuncio/blob/dev/docs/reviews/2026-08-10-multi-engine-design-review.md) (PR #385)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/nuncio#389