updateRules skips upstream changes to existing official spam-filter objects

Issue Description

In Stalwart 0.16.22, updateRules uses RegistryWrite::insert for all imported object types: SpamRule, SpamTag, SpamDnsblServer, HttpLookup, MemoryLookupKey and SpamFileExtension. PrimaryKeyConflict only increments already_exists; existing objects are not updated.

The official v3.0.0 and v3.0.1 artifacts provide a minimal example: their only difference is the DBL_SPAM score, changed from 6.5 to 4.5. No objects were added or removed. The importer therefore skips the sole change in this release.

This contradicts the documented update behavior for core rules and core DNSBL definitions, which says their contents are overwritten while the local enable setting is preserved.

Verified by source inspection and comparison of the official artifacts. No end-to-end reproduction of the unmodified native update path was performed for this report.

Expected Behavior

Updating the unmodified official ruleset from v3.0.0 to v3.0.1 should change DBL_SPAM from 6.5 to 4.5.

Upstream changes to existing official objects should be applied. For core SpamRule and SpamDnsblServer objects, local enable settings should remain preserved as documented.

Actual Behavior

The importer attempts an insertion, receives PrimaryKeyConflict for the existing DBL_SPAM tag, and counts it as already_exists without updating its score.

The code path therefore leaves the stored score at 6.5 instead of the candidate artifact’s 4.5. Reloading settings cannot apply the intended value because the stored object was never updated.

The same insert-and-skip behavior prevents changes to existing rule conditions, DNSBL definitions and HTTP lookup configurations from being applied.

Reproduction Steps

  1. Import the official v3.0.0 ruleset into an instance without existing spam-filter ruleset objects:
    https://github.com/stalwartlabs/spam-filter/releases/download/v3.0.0/spam-filter-rules.json.gz

  2. Read the SpamTag whose tag is DBL_SPAM and confirm score=6.5.

  3. Set SpamSettings.spamFilterRulesUrl to:
    https://github.com/stalwartlabs/spam-filter/releases/download/v3.0.1/spam-filter-rules.json.gz
    Apply ReloadSettings so the importer uses the new URL.

  4. Run SpamFilterMaintenance with maintenanceType=“updateRules” and wait for completion. Do not delete or manually modify existing objects beforehand.

  5. Read DBL_SPAM again.

Expected: score=4.5.
The source code predicts: score remains 6.5 because the existing object is skipped.

Relevant Log Output

No runtime logs are attached. The evidence is the importer’s conflict-handling code and comparison of the official v3.0.0 and v3.0.1 artifacts.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

Importer:

Documented update behavior:

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

This confirms the reported behavior gap against the documented contract. This is a legitimate bug report, not a support question I can resolve for the user — it needs a maintainer/engineering follow-up rather than a documentation answer.

Thanks for the detailed writeup, including the exact source lines and the DBL_SPAM 6.5→4.5 example. This looks like a genuine mismatch between the documented behavior for core rules/DNSBL entries (“contents are overwritten, local enable preserved”) and the current updateRules implementation in spam_classifier.rs, which uses RegistryWrite::insert for every object type and simply counts PrimaryKeyConflict as already_exists without ever applying an update/upsert. As you noted, this affects SpamRule, SpamDnsblServer, SpamTag, HttpLookup, MemoryLookupKey, and SpamFileExtension alike, so any in-place score/config change in an upstream release (not just additions/removals) would silently be skipped.

I can’t fix or triage the code myself here, but this report has enough detail (exact file/line references, the specific artifact diff, and reproduction steps) for the team to act on. A staff member will need to confirm intended behavior and follow up.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

This has been fixed in v0.16.24 (yet to be released).