]> git.ipfire.org Git - thirdparty/rspamd.git/log
thirdparty/rspamd.git
2 months agoMerge pull request #5908 from dragoangel/fix-bayes-expiry
Vsevolod Stakhov [Wed, 25 Feb 2026 10:28:35 +0000 (10:28 +0000)] 
Merge pull request #5908 from dragoangel/fix-bayes-expiry

fix: properly set bayes class labels

2 months agoMerge pull request #5907 from moisseev/senderscore
Vsevolod Stakhov [Wed, 25 Feb 2026 09:53:18 +0000 (09:53 +0000)] 
Merge pull request #5907 from moisseev/senderscore

[Conf] Disable Validity SenderScore RBLs by default

2 months agoMerge branch 'master' into fix-bayes-expiry 5908/head
Dmitriy Alekseev [Tue, 24 Feb 2026 21:58:27 +0000 (22:58 +0100)] 
Merge branch 'master' into fix-bayes-expiry

2 months ago[Fix] Propagate source/classification URL flags to query-extracted URLs
Vsevolod Stakhov [Tue, 24 Feb 2026 21:42:03 +0000 (21:42 +0000)] 
[Fix] Propagate source/classification URL flags to query-extracted URLs

When a URL is found inside the query string of another URL (e.g.
http://redir.com/?q=http://target.com), the inner URL now inherits
source/classification flags (FROM_TEXT, CONTENT, SUBJECT, INVISIBLE)
from the outer URL via RSPAMD_URL_FLAG_PROPAGATE_MASK.

Previously, inner URLs only received the QUERY flag, losing all context
about where the parent URL was found. This caused inconsistencies in
plugins that filter URLs by source flags (e.g. RBL content URL filtering).

Also fixes two bugs in the subject path (rspamd_url_task_subject_callback):
- hostlen check used outer URL instead of inner query URL
- QUERY flag was not set on URLs extracted from subject URL queries

2 months agofix: properly set bayes class labels to S and H for class spam and ham class names...
Dmitriy Alekseev [Tue, 24 Feb 2026 17:19:40 +0000 (18:19 +0100)] 
fix: properly set bayes class labels to S and H for class spam and ham class names, adjust bayes expiry to write occurrences as spam and ham instead of S and H

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
2 months ago[Conf] Disable Validity SenderScore RBLs by default 5907/head
Alexander Moisseev [Tue, 24 Feb 2026 14:45:55 +0000 (17:45 +0300)] 
[Conf] Disable Validity SenderScore RBLs by default

Both bl.score.senderscore.com and score.senderscore.com require
a registered MyValidity account to function. Unregistered IPs
receive 127.255.255.255 (blocked) for all queries, making the
RBLs non-functional without prior account setup regardless of
query volume.

Disable senderscore_reputation (score.senderscore.com) by default
and update the senderscore (bl.score.senderscore.com) comment to
reflect the actual registration requirement. Users must register
their querying IPs at https://my.validity.com before enabling
either RBL.

2 months ago[Fix] Move binary data from KEYS to ARGV in fuzzy and neural scripts
Vsevolod Stakhov [Tue, 24 Feb 2026 14:39:49 +0000 (14:39 +0000)] 
[Fix] Move binary data from KEYS to ARGV in fuzzy and neural scripts

Same expand_keys corruption issue as the Bayes fix: binary data passed
as KEYS gets template-expanded, stripping 0x24 bytes.

fuzzy_update: move operation parameters and raw digest to ARGV, keep
only hash_key, count_key, and shingle keys as KEYS.

neural_save_unlock: move compressed ANN weights, PCA matrix, and all
non-key parameters to ARGV, keep only Redis key names as KEYS.

2 months ago[Fix] Move binary msgpack data from KEYS to ARGV in Bayes Redis scripts
Vsevolod Stakhov [Tue, 24 Feb 2026 13:38:31 +0000 (13:38 +0000)] 
[Fix] Move binary msgpack data from KEYS to ARGV in Bayes Redis scripts

When expand_keys is enabled, lutil.template() is applied to all KEYS
arguments of EVALSHA commands. This corrupts binary msgpack blobs by
stripping 0x24 ('$') bytes, breaking str8 headers where the length
byte equals 36. Move non-key arguments (msgpack tokens, config, labels)
to ARGV which is not subject to key expansion.

Also fix msgpack_str_len off-by-one for str32 (4+len -> 5+len).

2 months ago[Fix] Fix subprocess cleanup race in spawn_process SIGCHLD handler
Vsevolod Stakhov [Tue, 24 Feb 2026 11:37:27 +0000 (11:37 +0000)] 
[Fix] Fix subprocess cleanup race in spawn_process SIGCHLD handler

When the I/O handler finishes reading the subprocess reply before
SIGCHLD arrives, neither handler would call rspamd_lua_cbdata_free:
the I/O handler skips cleanup because dead=FALSE, and the SIGCHLD
handler skips cleanup because replied=TRUE. This leaves the subprocess
in the main process workers table, causing shutdown to wait for a
child that has already exited.

Fix by always calling rspamd_lua_cbdata_free in the SIGCHLD handler
when replied=TRUE, since the I/O handler has already finished and
deferred cleanup to us.

2 months ago[Fix] Use main Lua state in config object destructors
Vsevolod Stakhov [Tue, 24 Feb 2026 11:29:54 +0000 (11:29 +0000)] 
[Fix] Use main Lua state in config object destructors

The periodic, cached config, and symbol callback destructors stored a
raw lua_State pointer captured at registration time. If this was a
thread/coroutine state from the thread pool, it could be garbage
collected before the destructor runs during config cleanup, causing a
use-after-free crash in luaL_unref.

Use RSPAMD_LUA_CFG_STATE(cfg) instead, which is the main Lua state
that remains valid throughout config_free until rspamd_lua_close.

2 months ago[Fix] Prevent LuaJIT GC stalls after neural training
Vsevolod Stakhov [Tue, 24 Feb 2026 11:07:50 +0000 (11:07 +0000)] 
[Fix] Prevent LuaJIT GC stalls after neural training

The LuaJIT GC atomic phase is non-incremental and processes the entire
gray/grayagain object graph in one uninterruptible pass. After neural
training completes, the controller's Lua heap is bloated with training
temporaries, causing the next GC cycle's atomic phase to stall the
event loop at 100% CPU for an extended period.

Two fixes:
- Force collectgarbage('collect') in ann_trained callback to clean up
  training temporaries before they accumulate
- Stop/restart GC around fork() in spawn_process to prevent the child
  from inheriting a mid-cycle GC state that triggers thrashing

2 months agoMerge pull request #5903 from rspamd/vstakhov-embeddings-pool
Vsevolod Stakhov [Tue, 24 Feb 2026 08:48:12 +0000 (08:48 +0000)] 
Merge pull request #5903 from rspamd/vstakhov-embeddings-pool

Fasttext embed: SIF weighting and conv1d pooling

2 months agoMerge pull request #5894 from rspamd/vstakhov-multi-fuzzy-flag
Vsevolod Stakhov [Tue, 24 Feb 2026 08:47:56 +0000 (08:47 +0000)] 
Merge pull request #5894 from rspamd/vstakhov-multi-fuzzy-flag

[Feature] Multi-flag fuzzy hash support

2 months agoMerge pull request #5898 from dragoangel/fix-multiclass-bayes
Vsevolod Stakhov [Tue, 24 Feb 2026 08:31:44 +0000 (08:31 +0000)] 
Merge pull request #5898 from dragoangel/fix-multiclass-bayes

[Fix] Fix multiclass Bayes learn cache and classifier isolation

2 months ago[Fix] Use backward compatible learned_ids for spam and ham class names 5898/head
Dmitriy Alekseev [Mon, 23 Feb 2026 23:28:09 +0000 (00:28 +0100)] 
[Fix] Use backward compatible learned_ids for spam and ham class names

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
2 months agoMerge branch 'master' into fix-multiclass-bayes
Dmitriy Alekseev [Mon, 23 Feb 2026 17:34:20 +0000 (18:34 +0100)] 
Merge branch 'master' into fix-multiclass-bayes

2 months agoMerge pull request #5900 from moisseev/classifiers
Vsevolod Stakhov [Mon, 23 Feb 2026 09:25:51 +0000 (09:25 +0000)] 
Merge pull request #5900 from moisseev/classifiers

[Feature] Extend /bayes/classifiers endpoint with metadata

2 months ago[Feature] Fasttext embed: multi-scale conv1d pooling for text features 5903/head
Vsevolod Stakhov [Mon, 23 Feb 2026 09:14:35 +0000 (09:14 +0000)] 
[Feature] Fasttext embed: multi-scale conv1d pooling for text features

Add conv1d output mode to the fasttext_embed provider that applies
multi-scale max-over-time pooling over sliding word windows in Lua,
producing compact feature vectors for the neural plugin's dense ANN.

For each kernel size (default {1, 3, 5}), word vectors are averaged
within sliding windows, then max-pooled across positions per channel.
Each scale's features are L2-normalized independently for balanced
contribution. This replaces the previous approach of feeding raw NCW
matrices into KANN conv1d layers.

Also adds max1d and input3d layer bindings to the KANN Lua API, and
includes conv1d settings (kernel_sizes, conv_pooling, max_words) in
the providers_config_digest for automatic retraining on config change.

2 months ago[Feature] Fasttext embed: SIF word weighting for sentence vectors
Vsevolod Stakhov [Sun, 22 Feb 2026 18:11:44 +0000 (18:11 +0000)] 
[Feature] Fasttext embed: SIF word weighting for sentence vectors

Add Smooth Inverse Frequency (SIF) weighting to the fasttext embedding
provider. Common words (the, is, a) get near-zero weight while
distinctive words (viagra, invoice) get high weight, significantly
improving embedding quality without changing dimensionality.

Expose get_word_frequency() from the fasttext shim C++ API and Lua
bindings, returning p(word) = count/ntokens from the model vocabulary.

SIF is enabled by default (sif_weight=true, sif_a=1e-3). Combined with
multi-model mean+max pooling, improves F1 from 0.87 to 0.90 in testing.

2 months agoMerge pull request #5897 from rspamd/vstakhov-more-embeddings
Vsevolod Stakhov [Sun, 22 Feb 2026 17:34:43 +0000 (17:34 +0000)] 
Merge pull request #5897 from rspamd/vstakhov-more-embeddings

Replace libfasttext with mmap-based built-in shim

2 months agoMerge branch 'master' into fix-multiclass-bayes
Dmitriy Alekseev [Sun, 22 Feb 2026 17:31:30 +0000 (18:31 +0100)] 
Merge branch 'master' into fix-multiclass-bayes

2 months agoMerge pull request #5902 from dragoangel/patch-16
Vsevolod Stakhov [Sun, 22 Feb 2026 17:08:57 +0000 (17:08 +0000)] 
Merge pull request #5902 from dragoangel/patch-16

[Fix] Properly count rspamd-proxy-milter conns count

2 months ago[Minor] Replace hash table with linear search for class deduplication 5900/head
Alexander Moisseev [Sun, 22 Feb 2026 15:45:03 +0000 (18:45 +0300)] 
[Minor] Replace hash table with linear search for class deduplication

Number of classes per classifier is always small (N < 10 in practice),
so hash table overhead outweighs its O(1) lookup benefit. Linear search
over the already-built UCL array is simpler and faster here.

2 months agoMerge branch 'master' into patch-16 5902/head
Dmitriy Alekseev [Sun, 22 Feb 2026 15:25:46 +0000 (16:25 +0100)] 
Merge branch 'master' into patch-16

2 months agoRefactor nconns decrement handling in milter sessions
Dmitriy Alekseev [Sun, 22 Feb 2026 13:46:51 +0000 (14:46 +0100)] 
Refactor nconns decrement handling in milter sessions

Update nconns decrement logic for milter sessions to ensure accurate connection tracking during session lifecycle without memory leaks.

2 months ago[Feature] Fasttext embed: multi-model and mean+max pooling 5897/head
Vsevolod Stakhov [Sun, 22 Feb 2026 11:07:49 +0000 (11:07 +0000)] 
[Feature] Fasttext embed: multi-model and mean+max pooling

Use all configured language_models for every message by default
(multi_model=true), concatenating vectors from each model for
richer cross-lingual representations.

Add mean+max pooling (pooling="mean_max" default) which concatenates
the average word vector with element-wise max pooling, capturing both
typical and prominent semantic features.

With 2 quantized 50-dim models this produces 200-dim vectors instead
of 50, significantly improving classification (F1 0.51 -> 0.87 in
testing).

2 months ago[Minor] Rename llm_embeddings classifier type to neural in classifier_test
Vsevolod Stakhov [Sun, 22 Feb 2026 11:00:56 +0000 (11:00 +0000)] 
[Minor] Rename llm_embeddings classifier type to neural in classifier_test

Accept 'llm_embeddings' as a legacy alias for backward compatibility.

2 months agoMerge pull request #5901 from dragoangel/patch-15
Vsevolod Stakhov [Sat, 21 Feb 2026 19:14:44 +0000 (19:14 +0000)] 
Merge pull request #5901 from dragoangel/patch-15

[Fix] Actualize links in README.md

2 months ago[Fix] Fasttext shim: fix quantized model support and add hierarchical softmax
Vsevolod Stakhov [Sat, 21 Feb 2026 18:54:10 +0000 (18:54 +0000)] 
[Fix] Fasttext shim: fix quantized model support and add hierarchical softmax

- Add pruneidx support in dictionary subword computation: pruned models
  (like lid.176.ftz) remap subword hash IDs through the prune index table,
  matching FastText's pushHash() behavior
- Apply per-row norm from quantized norm PQ (NPQ) when decoding QMatrix
  rows, fixing vector magnitude for quantized models
- Implement hierarchical softmax prediction via Huffman tree DFS,
  matching FastText's HierarchicalSoftmaxLoss for models with loss=hs
- Add predict() method to Lua rspamd_fasttext bindings

2 months ago[Fix] Fasttext shim: fix binary format parsing and harden against corrupt models
Vsevolod Stakhov [Sat, 21 Feb 2026 17:27:42 +0000 (17:27 +0000)] 
[Fix] Fasttext shim: fix binary format parsing and harden against corrupt models

- Fix QMatrix load order: read codesize+codes before PQ (not after)
- Fix PQ centroid count: use dim*ksub (not nsubq*ksub*dsub)
- Fix PQ centroid addressing: match FastText's get_centroids() for last sub-quantizer
- Fix dictionary load: read size_ field before nwords/nlabels
- Fix output matrix: always read qout bool between input and output matrices
- Fix subword n-gram skip: only skip single-char BOW/EOW, not full wrapped word

Add comprehensive sanity checks for all untrusted values from model files:
- Validate dimensions, entry counts, matrix sizes against sane upper bounds
- Overflow-safe multiplication for matrix element counts
- Bounds checks on centroid, codes, and dense matrix data access
- Null pointer guards on all matrix operations
- Replace throwing .at() with bounds-checked pointer return
- Limit string reads to 1024 bytes to prevent runaway allocation
- Return nullptr/false from loaders on validation failure
- Guard Lua bindings against empty/short vectors from get_word_vector

2 months ago[Feature] WebUI: Add multi-class classifier support to learning UI
Alexander Moisseev [Sat, 21 Feb 2026 15:56:04 +0000 (18:56 +0300)] 
[Feature] WebUI: Add multi-class classifier support to learning UI

 - Support /learnclass endpoint with Classifier and Class headers
   for multi-class learning.

 - Handle both old (array) and new (metadata object) /bayes/classifiers
   response formats for backward compatibility.

 - Add dynamic UI switching based on classifier type:
   * Binary classifiers: show HAM/SPAM upload buttons
   * Multi-class classifiers: show class dropdown + Learn button

 - Display classifier metadata as text badges in dropdown:
   [multi-class] [per-user]

 - Hide "All classifiers" option when multi-class classifiers present
   (different classifiers may have different class sets).

2 months ago[Minor] Fasttext shim: addressing review comments
Vsevolod Stakhov [Sat, 21 Feb 2026 16:46:19 +0000 (16:46 +0000)] 
[Minor] Fasttext shim: addressing review comments

- Use ICU U8_NEXT for UTF-8 iteration instead of handcrafted code
- Replace exception-based error handling with fail-bit pattern in
  binary_reader, propagating errors via tl::expected
- Replace std::sort with std::reverse after min-heap extraction

2 months ago[Feature] Replace libfasttext with mmap-based built-in shim
Vsevolod Stakhov [Fri, 20 Feb 2026 11:21:38 +0000 (11:21 +0000)] 
[Feature] Replace libfasttext with mmap-based built-in shim

Replace the external libfasttext shared library dependency with a
zero-dependency C++20 shim that reads .bin/.ftz models directly.
The large input matrix is mmap'd with MAP_SHARED/PROT_READ so all
worker processes share the same physical pages after fork.

This eliminates:
- C++ exception ABI issues across shared library boundaries
  (no more fork-probe hack in lua_fasttext)
- The ENABLE_FASTTEXT cmake option (always compiled in now)
- Per-worker heap copies of the model (~500MB-7GB savings)

New files:
- contrib/fasttext_shim/fasttext_shim.{h,cxx} - built-in shim
- contrib/fasttext_shim/CMakeLists.txt
- lualib/plugins/neural/providers/ - embedding providers
- src/lua/lua_fasttext.cxx - rewritten Lua bindings

2 months agoMerge pull request #5899 from moisseev/multi-class
Vsevolod Stakhov [Sat, 21 Feb 2026 16:37:56 +0000 (16:37 +0000)] 
Merge pull request #5899 from moisseev/multi-class

[Minor] Use explicit class for colorizing ham/spam in Bayesian statis…

2 months ago[Fix] Properly count rspamd-proxy-milter conns count
Dmitriy Alekseev [Sat, 21 Feb 2026 14:51:42 +0000 (15:51 +0100)] 
[Fix] Properly count rspamd-proxy-milter conns count

2 months agoUpdate README links to point to new documentation site 5901/head
Dmitriy Alekseev [Sat, 21 Feb 2026 13:39:28 +0000 (14:39 +0100)] 
Update README links to point to new documentation site

2 months agoUpdate logo image source in README.md
Dmitriy Alekseev [Sat, 21 Feb 2026 13:24:01 +0000 (14:24 +0100)] 
Update logo image source in README.md

2 months ago[Feature] Extend /bayes/classifiers endpoint with metadata
Alexander Moisseev [Sat, 21 Feb 2026 06:43:54 +0000 (09:43 +0300)] 
[Feature] Extend /bayes/classifiers endpoint with metadata

- Return classifier metadata (type, per_user, classes) instead of just names array.

Old format: ["bayes", "bayes_multi"]
New format: {classifiers: [{name, type, per_user, classes: [...]}]}

- Export rspamd_classifier_is_per_user() and rspamd_classifier_type()
helper functions to stat_api.h for reuse.

2 months ago[Minor] Use explicit class for colorizing ham/spam in Bayesian statistics table 5899/head
Alexander Moisseev [Fri, 20 Feb 2026 19:18:37 +0000 (22:18 +0300)] 
[Minor] Use explicit class for colorizing ham/spam in Bayesian statistics table

Use statfile.class field from /stat response to determine CSS class
for table cells instead of guessing from symbol name. Falls back to
symbol name matching for backward compatibility with older Rspamd.

2 months ago[Fix] Fix multiclass Bayes learn cache and classifier isolation
Dmitriy Alekseev [Fri, 20 Feb 2026 16:31:33 +0000 (17:31 +0100)] 
[Fix] Fix multiclass Bayes learn cache and classifier isolation

Propagate task->classifier from the HTTP header so class-targeted
learning skips unrelated classifiers early. Validate the requested
class exists in a statfile before tokenisation to fail fast with a
clear 404 error on misconfigured setups.

Replace numeric class_id hash cache keys in learned_ids with direct class
name strings throughout the Redis cache layer (C, Lua, Redis scripts) to
fix uint64_t precision loss through Lua's 53-bit doubles, which caused
equality checks to always fail for arbitrary multiclass class names.

Add RSPAMD_FLAG_CLASSIFIER_MULTICLASS at config load time
to route probability lookups to the correct result type per classifier.

Store multiclass and binary Bayes results under per-classifier mempool
keys (multiclass_result:<name>, bayes_prob:<name>) and update the
set/get API to take a classifier_name parameter, preventing
cross-contamination when multiple classifiers are configured.

Switch multiclass result allocation from heap (g_new0/g_new) to task
memory pool, eliminating the need for rspamd_multiclass_result_free.

Inject can_learn_prob and can_learn_class into the mempool before
invoking Lua learn conditions, replacing the legacy asymmetric
spam_min/ham_max pair with a unified min_prob threshold.

Remove the cl_skipped gate in rspamd_stat_cache_check so the Redis
learned_ids cache is always consulted before learn conditions run for
proper logging and response handling on relearning same emails.

2 months agoMerge pull request #5893 from moisseev/multi-class
Vsevolod Stakhov [Fri, 20 Feb 2026 10:38:19 +0000 (10:38 +0000)] 
Merge pull request #5893 from moisseev/multi-class

[Feature] WebUI: Display classifier metadata in Bayesian statistics table

2 months ago[Minor] Remove unnecessary ucl_object_insert_key return check 5893/head
Alexander Moisseev [Fri, 20 Feb 2026 08:52:20 +0000 (11:52 +0300)] 
[Minor] Remove unnecessary ucl_object_insert_key return check

No other place in the codebase checks ucl_object_insert_key return
value. Remove the check to match Rspamd coding convention.

2 months ago[Fix] Backward-compatible version negotiation for multi-flag fuzzy 5894/head
Vsevolod Stakhov [Thu, 19 Feb 2026 17:32:10 +0000 (17:32 +0000)] 
[Fix] Backward-compatible version negotiation for multi-flag fuzzy

Use an unused bit (bit 4) in the version byte as a v2 capability flag.
New clients send version 4|0x10 initially; old servers mask to 4 and
work normally, new servers see the cap bit and send v2 replies. Once
a v2 reply is received, subsequent requests use native version 5.

2 months agoMerge branch 'master' into vstakhov-multi-fuzzy-flag
Vsevolod Stakhov [Thu, 19 Feb 2026 16:52:19 +0000 (16:52 +0000)] 
Merge branch 'master' into vstakhov-multi-fuzzy-flag

2 months ago[Fix] Store small PDF objects without counting toward limit
Vsevolod Stakhov [Thu, 19 Feb 2026 16:16:18 +0000 (16:16 +0000)] 
[Fix] Store small PDF objects without counting toward limit

Small objects are now stored but don't count toward max_pdf_objects,
so padding evasion objects can't exhaust the budget while legitimate
small references (like JavaScript dictionaries) are preserved.

2 months agoMerge branch 'master' into vstakhov-multi-fuzzy-flag
Vsevolod Stakhov [Thu, 19 Feb 2026 15:01:48 +0000 (15:01 +0000)] 
Merge branch 'master' into vstakhov-multi-fuzzy-flag

2 months ago[Fix] Defeat PDF object padding evasion in extract_outer_objects
Vsevolod Stakhov [Thu, 19 Feb 2026 14:59:58 +0000 (14:59 +0000)] 
[Fix] Defeat PDF object padding evasion in extract_outer_objects

Decouple iteration limit from storage limit so that thousands of tiny
dummy obj/endobj pairs no longer consume all max_pdf_objects slots.

- Add min_obj_content_size (default 32) config: objects smaller than
  this are skipped during extraction (they carry no useful content)
- extract_outer_objects now iterates ALL start/end positions but only
  stores objects that pass the size filter, up to max_pdf_objects
- attach_pdf_streams similarly iterates all stream positions
- Add timeout checks in both loops to stay within pdf_process_timeout

2 months ago[Fix] Adjust positions in URL matcher properly
Vsevolod Stakhov [Thu, 19 Feb 2026 14:17:26 +0000 (14:17 +0000)] 
[Fix] Adjust positions in URL matcher properly

2 months ago[Fix] Refine structural alternative check to leaf text/* parts
Vsevolod Stakhov [Thu, 19 Feb 2026 10:58:41 +0000 (10:58 +0000)] 
[Fix] Refine structural alternative check to leaf text/* parts

The previous check counted any sibling in multipart/alternative,
but multipart/related with only images or malformed empty related
parts are not real alternatives. Now only leaf text/* non-html
siblings count (e.g. text/calendar, text/enriched).

2 months ago[Fix] Check MIME structure for alternatives in has_only_html_part
Vsevolod Stakhov [Thu, 19 Feb 2026 10:31:07 +0000 (10:31 +0000)] 
[Fix] Check MIME structure for alternatives in has_only_html_part

has_only_html_part() relied solely on alt_text_part to find alternatives,
but parts like text/calendar (detected as ics with no_text=true) are not
in text_parts and thus invisible to the search. Now we also walk the MIME
tree: if the HTML part sits inside a multipart/alternative with other
siblings, it has a structural alternative and MIME_HTML_ONLY should not
fire.

2 months ago[Fix] Fix XML detection overriding HTML in content type detection
Vsevolod Stakhov [Wed, 18 Feb 2026 19:01:23 +0000 (19:01 +0000)] 
[Fix] Fix XML detection overriding HTML in content type detection

HTML parts containing an embedded <?xml?> prolog (e.g. inside the body)
were misdetected as application/xml. Since xml type has no_text=true,
this excluded the HTML part from text_parts entirely, causing
has_only_html_part() to return false and MIME_HTML_ONLY to not fire.

- Remove xml binary pattern from patterns.lua (it preempted the text
  heuristic which correctly prioritises HTML)
- Guard xml text pattern so it cannot override HTML when HTML markers
  are also present
- Raise xml text pattern weight to 40 to preserve pure XML detection

2 months ago[Fix] Filter forbidden_ids from multi-flag extra flags in v2 reply
Vsevolod Stakhov [Wed, 18 Feb 2026 15:33:02 +0000 (15:33 +0000)] 
[Fix] Filter forbidden_ids from multi-flag extra flags in v2 reply

Extract forbidden flag checks into rspamd_fuzzy_filter_forbidden_v2()
which filters both primary and extra flags. If primary is forbidden
but valid extras remain, promotes first valid extra to primary.

2 months ago[Test] Update fuzzy tests for multi-flag support
Vsevolod Stakhov [Wed, 18 Feb 2026 12:09:47 +0000 (12:09 +0000)] 
[Test] Update fuzzy tests for multi-flag support

Rename Overwrite tests to Multi Flag tests since multiple flags
now coexist instead of overwriting. Add Multi Flag Delete test.

2 months ago[Feature] Add multi-flag fuzzy hash support with Lua-based Redis update path
Vsevolod Stakhov [Wed, 18 Feb 2026 11:23:31 +0000 (11:23 +0000)] 
[Feature] Add multi-flag fuzzy hash support with Lua-based Redis update path

Support up to 8 flag slots per fuzzy hash (primary + 7 extra), enabling
multiple rules to match against the same digest with independent flag/value
pairs. The highest-value flag is always promoted to the primary slot for
backward compatibility.

The Redis update path (ADD/DEL/REFRESH) is moved from a C MULTI/EXEC
pipeline with an embedded EVAL string to a proper Lua architecture
following the Bayes pattern: a Redis script in lualib/redis_scripts/
loaded via lua_redis.load_redis_script_from_file(), with a Lua module
(lua_fuzzy_redis.lua) providing an async update functor called from C
via lua_pcall. This gives automatic EVALSHA + NOSCRIPT recovery and
keeps the multi-flag merge logic in a maintainable .lua file.

Wire protocol extended with epoch 12 (RSPAMD_FUZZY_EPOCH12) carrying
rspamd_fuzzy_reply_v2 with n_extra_flags and up to 7 extra flag entries.

2 months ago[Fix] Distinguish explicit multi-class from legacy binary configs
Alexander Moisseev [Tue, 17 Feb 2026 13:13:48 +0000 (16:13 +0300)] 
[Fix] Distinguish explicit multi-class from legacy binary configs

Check is_spam_converted flag to differentiate between:
- Explicit class declaration (new format) -> multi-class
- Converted is_spam=true/false (legacy) -> binary

This fixes incorrect "binary" detection for explicit multi-class
configurations using class="spam" and class="ham".

2 months ago[Fix] Fix JPEG dimension parsing - use 256 instead of 0xff
Vsevolod Stakhov [Tue, 17 Feb 2026 10:29:17 +0000 (10:29 +0000)] 
[Fix] Fix JPEG dimension parsing - use 256 instead of 0xff

2 months ago[Fix] Fix JSON emission for implicit arrays in libucl
Vsevolod Stakhov [Mon, 16 Feb 2026 19:08:55 +0000 (19:08 +0000)] 
[Fix] Fix JSON emission for implicit arrays in libucl

When emitting UCL objects with implicit arrays (multiple values for the
same key) as JSON, the emitter was producing invalid output by:
- Not printing the key before the array for non-first elements
- Missing comma separators between elements

This caused rspamadm configdump -j to output malformed JSON like:
  "actions": {...}[
instead of:
  "actions": {...},
  "group": [

2 months ago[Feature] WebUI: Add classifier metadata to Bayes statistics table
Alexander Moisseev [Mon, 16 Feb 2026 18:34:29 +0000 (21:34 +0300)] 
[Feature] WebUI: Add classifier metadata to Bayes statistics table

- Display `classifier name` with type badges (`multi-class`, `per-user`)
and `class` field for each statfile with fallback for old server responses
- Group statfiles by classifier name with rowspan (similar to server grouping)
- Update hover handler to support nested rowspan cells
- Highlight all parent rowspan cells when hovering classifier or statfile

2 months ago[Feature] Extend /stat endpoint with classifier metadata
Alexander Moisseev [Mon, 16 Feb 2026 08:04:20 +0000 (11:04 +0300)] 
[Feature] Extend /stat endpoint with classifier metadata

Add classifier object (name, type, per_user) and class field to each
statfile in /stat response.

2 months ago[Minor] Fix compile warnings
Vsevolod Stakhov [Mon, 16 Feb 2026 16:00:52 +0000 (16:00 +0000)] 
[Minor] Fix compile warnings

- Fix hexdigests array size (use empty brackets for compiler to determine)
- Fix char8_t to char32_t implicit conversion in replxx terminal
- Fix hex format specifier in hyperscan debug logging (%xd not %x)
- Add forward declaration for struct rspamd_external_libs_ctx
- Remove unused is_msgpack variable in protocol.c
- Fix logic bug: c >= 'a' || c <= 'f' should be && in hex parsing

2 months ago[Fix] Catch all exceptions when loading fasttext model to avoid crash
Vsevolod Stakhov [Mon, 16 Feb 2026 15:36:19 +0000 (15:36 +0000)] 
[Fix] Catch all exceptions when loading fasttext model to avoid crash

When the fasttext language detection model file doesn't exist or is
corrupted, fasttext library throws an exception. Add a catch-all handler
to prevent crashes when the model path is invalid or the file is missing.

Also improved error messages to include the model path for debugging.

2 months ago[Minor] Backport ucl_object_iterate_end() from libucl to fix iterator leak
Vsevolod Stakhov [Mon, 16 Feb 2026 15:36:16 +0000 (15:36 +0000)] 
[Minor] Backport ucl_object_iterate_end() from libucl to fix iterator leak

Add ucl_object_iterate_end() function to free resources associated with
an inline iterator when iteration is abandoned before completion. This
fixes a potential memory leak when iterating over UCL_OBJECT types where
internal heap state is allocated.

Changes backported from libucl 0.9.3:
- Add ucl_hash_iterate_free() to ucl_hash.c/h
- Add ucl_object_iterate_end() to ucl_util.c and ucl.h
- Add ucl_iterate_object_end macro alias

Note: Rspamd already had the security fixes for:
- heap-buffer-overflow in ucl_maybe_parse_number
- heap-buffer-overflow in ucl_parse_multiline_string
- Use-After-Free in ucl_hash_insert
- Invalid JSON emission when merging objects

2 months ago[Feature] Add rspamd_text:normalize_newlines() for CRLF/LF conversion
Vsevolod Stakhov [Mon, 16 Feb 2026 15:00:03 +0000 (15:00 +0000)] 
[Feature] Add rspamd_text:normalize_newlines() for CRLF/LF conversion

Add fast C API and Lua binding to normalize line endings in rspamd_text:
- Two-pass algorithm using rspamd_memcspn for efficient scanning
- LF to CRLF conversion (for SMTP compliance, fixes issue #5888)
- CRLF to LF conversion (for Unix compatibility)
- Supports mempool for memory allocation
- Proper ownership handling: frees old memory if owned, sets OWN flag if using g_malloc

Update lua_smtp.lua to normalize messages to CRLF before SMTP DATA,
ensuring compatibility with strict servers like Exchange.

Includes comprehensive unit tests covering:
- No-op cases (already normalized, no newlines)
- Basic conversions in both directions
- Mixed/inconsistent line endings
- Weird line endings (lone CR, multiple CR)
- Edge cases (empty, single char, large text, null bytes)
- Mode parameter variations (crlf/windows/lf/unix, case insensitive)

2 months agoMerge pull request #5892 from fatalbanana/structured_meta
Vsevolod Stakhov [Mon, 16 Feb 2026 12:40:40 +0000 (12:40 +0000)] 
Merge pull request #5892 from fatalbanana/structured_meta

[Test] Don't install python libraries at runtime

2 months ago[Test] Don't install python libraries at runtime 5892/head
Andrew Lewis [Mon, 16 Feb 2026 11:24:25 +0000 (13:24 +0200)] 
[Test] Don't install python libraries at runtime

2 months agoMerge pull request #5890 from rspamd/vstakhov-uuid-v7-per-task
Vsevolod Stakhov [Sun, 15 Feb 2026 21:47:48 +0000 (21:47 +0000)] 
Merge pull request #5890 from rspamd/vstakhov-uuid-v7-per-task

Add structured metadata exporter with native UUID v7 per task

2 months ago[Feature] Add UUID v7 column support to ClickHouse plugin 5890/head
Vsevolod Stakhov [Sun, 15 Feb 2026 10:03:00 +0000 (10:03 +0000)] 
[Feature] Add UUID v7 column support to ClickHouse plugin

Add TaskUUID column with Delta compression codec for efficient storage
of time-ordered UUID v7 values generated natively by Rspamd.

- Add enable_uuid setting (default: false)
- Add TaskUUID UUID CODEC(Delta, LZ4) column to schema
- Add migration from schema version 9 to 10
- Collect UUID via task:get_uuid() when enabled

Delta compression is effective for UUIDv7 due to its 48-bit millisecond
timestamp prefix ensuring monotonic ordering.

2 months ago[Test] Add functional tests for structured metadata exporter
Vsevolod Stakhov [Sat, 14 Feb 2026 22:47:30 +0000 (22:47 +0000)] 
[Test] Add functional tests for structured metadata exporter

2 months ago[Test] Add unit tests for structured formatter features
Vsevolod Stakhov [Sat, 14 Feb 2026 21:43:22 +0000 (21:43 +0000)] 
[Test] Add unit tests for structured formatter features

2 months ago[Feature] Add zstd compression option to structured formatter
Vsevolod Stakhov [Sat, 14 Feb 2026 21:26:24 +0000 (21:26 +0000)] 
[Feature] Add zstd compression option to structured formatter

2 months ago[Feature] Use detected MIME types for attachments in structured formatter
Vsevolod Stakhov [Sat, 14 Feb 2026 21:22:52 +0000 (21:22 +0000)] 
[Feature] Use detected MIME types for attachments in structured formatter

2 months ago[Feature] Sync UUID v7 random portion with Log-Tag header
Vsevolod Stakhov [Sat, 14 Feb 2026 19:17:22 +0000 (19:17 +0000)] 
[Feature] Sync UUID v7 random portion with Log-Tag header

When an MTA provides a Log-Tag header that overrides the pool UID,
patch the task UUID's random portion (bytes 8-15) to stay in sync.
Uses a fast hash of the tag to derive the bytes while preserving
the UUID v7 variant bits.

2 months ago[Feature] Add structured formatter to metadata_exporter
Vsevolod Stakhov [Sat, 14 Feb 2026 15:35:59 +0000 (15:35 +0000)] 
[Feature] Add structured formatter to metadata_exporter

Add a 'structured' output format that emits rich msgpack metadata
including UUID correlation, extracted text, base64-encoded attachment
and image content, URLs, and reply detection. Uses task:get_uuid()
for cross-system correlation instead of generating UUIDs in Lua.

2 months ago[Feature] Add native UUID v7 per task with Lua binding
Vsevolod Stakhov [Sat, 14 Feb 2026 15:35:51 +0000 (15:35 +0000)] 
[Feature] Add native UUID v7 per task with Lua binding

Generate a UUID v7 (time-ordered, RFC 9562) natively in C at task
creation time. The task's log UID is derived from the UUID's random
portion so that UID and UUID are always correlated.

- rspamd_uuid_v7() in util.c: 48-bit ms timestamp + crypto-random bits
- task_uuid[37] field in rspamd_task, populated in rspamd_task_new()
- task:get_uuid() Lua method for plugin access

2 months ago[Fix] Preserve UNSCORED flag when config defines symbol without score
Vsevolod Stakhov [Fri, 13 Feb 2026 17:18:39 +0000 (17:18 +0000)] 
[Fix] Preserve UNSCORED flag when config defines symbol without score

When rspamd_config_add_symbol was called for an existing symbol without
a score (NAN), line 1811 unconditionally replaced all flags, clearing
RSPAMD_SYMBOL_FLAG_UNSCORED. This caused Lua-registered scores to be
ignored: if a symbol appeared in config (e.g. groups.conf) without a
score, the UNSCORED flag was lost, and the Lua score-setting block in
lua_config_register_symbol_from_table was skipped, leaving score at 0.

Now flags are updated correctly in both cases:
- Real score provided: flags fully replaced with UNSCORED cleared
- NAN score: flags updated but UNSCORED preserved from existing symbol

Also initialize score to NAN in lua_config_set_metric_symbol to avoid
passing uninitialized stack garbage to rspamd_config_add_symbol.

2 months ago[Feature] Add dual-mode HTML fuzzy: template matching + phishing detection
Vsevolod Stakhov [Fri, 13 Feb 2026 16:47:13 +0000 (16:47 +0000)] 
[Feature] Add dual-mode HTML fuzzy: template matching + phishing detection

When html_ignore_domains is enabled, generate two HTML fuzzy commands per
part: a template command (domains ignored) and a domain-sensitive command
(domains included). If the template matches but domains don't, the new
FUZZY_HTML_PHISHING symbol fires, detecting reused HTML templates with
swapped phishing links.

2 months ago[Fix] Load fuzzy_html_phishing rule in main rules loader
Vsevolod Stakhov [Fri, 13 Feb 2026 14:25:35 +0000 (14:25 +0000)] 
[Fix] Load fuzzy_html_phishing rule in main rules loader

2 months ago[Fix] Fix custom tokenizer UAF and improve CJK fuzzy detection
Vsevolod Stakhov [Fri, 13 Feb 2026 14:09:59 +0000 (14:09 +0000)] 
[Fix] Fix custom tokenizer UAF and improve CJK fuzzy detection

Fix use-after-free in custom tokenizer integration: when tokens were
shallow-copied from a custom tokenizer result, cleanup_result would free
per-token normalized/stemmed/unicode strings while the copies still
referenced them. Now copy these strings into the mempool before cleanup.

Improve fuzzy hash generation for CJK (Japanese/Chinese/Korean) text:
- Lower shingle word count threshold by 3x for CJK languages (64→21,
  floor 32→12) since CJK morphemes carry higher semantic density
- Boost text_multiplier by 3x for CJK to compensate for 3-byte UTF-8
  characters, allowing short CJK emails to pass the min_bytes gate

Previously, short Japanese phishing emails (~50 chars, ~20 words) would
generate zero fuzzy hashes — failing both the word count gate for shingles
and the byte length gate for direct hash.

2 months ago[Fix] Use class_name as Redis label for multiclass Bayes without class_labels config
Vsevolod Stakhov [Fri, 13 Feb 2026 10:58:25 +0000 (10:58 +0000)] 
[Fix] Use class_name as Redis label for multiclass Bayes without class_labels config

get_class_label() fell through to the legacy "S"/"H" fallback when
class_labels hash table was NULL, even when an explicit class_name
(e.g. "cold_marketing") was set on the statfile. Since all non-binary
classes have is_spam=FALSE, every class mapped to "H", causing:
- All tokens stored under the same Redis hash field "H"
- All learn counters going to learns_ham
- Classification returning identical data for all classes
- Mempool runtime caching collisions (all saved under key _H)

Fix: check class_name with !is_spam_converted before the S/H fallback
so explicit multiclass configs use their actual class name as the Redis
label, while legacy binary configs (auto-converted from spam=true/false)
still use "S"/"H".

Also fix controller learn log to show actual class name instead of
always "spam"/"ham".

2 months ago[Rework] Rewrite fuzzy_html_phishing rule with proper config awareness
Vsevolod Stakhov [Fri, 13 Feb 2026 10:11:25 +0000 (10:11 +0000)] 
[Rework] Rewrite fuzzy_html_phishing rule with proper config awareness

Rewrite fuzzy HTML phishing mismatch detection to read fuzzy_check
config at load time and only check symbols from rules that have
html_shingles enabled. Fix broken virtual symbol registration by
registering callback first and linking virtuals via parent id.
Skip registration entirely when no rules have html_shingles.

2 months ago[Fix] Resolve composite references in inverted index for fast path
Vsevolod Stakhov [Fri, 13 Feb 2026 07:37:27 +0000 (07:37 +0000)] 
[Fix] Resolve composite references in inverted index for fast path

When the inverted index fast path collects potentially active composites,
it scans symbols already in the scan result. Composite symbols are not
yet in the scan result at that point, so composites referencing other
composites as atoms were never activated and silently failed to fire.

Fix by resolving composite references at index build time: recursively
collect leaf (non-composite) atoms and propagate dependent composites
to those atoms' index entries. Also refactor atom name parsing into a
shared helper to reduce duplication.

2 months ago[Fix] Remove check for non-existent fuzzy_result mempool variable
Vsevolod Stakhov [Fri, 13 Feb 2026 07:24:50 +0000 (07:24 +0000)] 
[Fix] Remove check for non-existent fuzzy_result mempool variable

The fuzzy_result mempool variable is never set by fuzzy_check, so the
early return was always triggered, making the phishing mismatch check
a no-op. The actual logic uses get_symbols_all() which works correctly.

2 months ago[Feature] Add ignore_link_domains option for HTML fuzzy rules
Vsevolod Stakhov [Fri, 13 Feb 2026 07:23:20 +0000 (07:23 +0000)] 
[Feature] Add ignore_link_domains option for HTML fuzzy rules

Allow HTML fuzzy shingles to match purely on structure (tag skeleton +
classes) by stripping link domains from structural tokens. Domain hashes
are still computed separately for the metadata fields. A separate cache
key suffix (_nd) prevents cross-contamination with normal hashes.

2 months ago[Fix] Handle native boolean values in lua_util.toboolean()
Vsevolod Stakhov [Thu, 12 Feb 2026 17:35:32 +0000 (17:35 +0000)] 
[Fix] Handle native boolean values in lua_util.toboolean()

toboolean() only handled string and number inputs, falling through to
the error branch for native Lua booleans and always returning false.
This broke the fuzzy_check "checks" configuration block since UCL
converts boolean values to native Lua booleans, and apply_checks_overrides
in lua_fuzzy.lua passes them through toboolean() via bool_opt().

Fixes: #5775
2 months ago[Minor] Try to fix CC issue with tabs
Vsevolod Stakhov [Thu, 12 Feb 2026 16:27:21 +0000 (16:27 +0000)] 
[Minor] Try to fix CC issue with tabs

2 months agoMerge pull request #5885 from rspamd/vstakhov-lua-perl-utils
Vsevolod Stakhov [Thu, 12 Feb 2026 10:42:43 +0000 (10:42 +0000)] 
Merge pull request #5885 from rspamd/vstakhov-lua-perl-utils

[Feature] Rewrite rspamd_stats.pl and mapstats.pl as rspamadm Lua subcommands

2 months ago[Fix] Fix issues in logstats/mapstats from code review 5885/head
Vsevolod Stakhov [Thu, 12 Feb 2026 10:00:14 +0000 (10:00 +0000)] 
[Fix] Fix issues in logstats/mapstats from code review

- Shell-quote paths in io.popen() to prevent injection
- Fix typo: correllations -> correlations in JSON output
- Pre-compile ignored symbol regexes instead of recompiling per call
- Deduplicate score change output logic in logstats
- Use native rspamd_ip equality instead of string comparison in mapstats

2 months ago[Fix] Use fuzzy group and options instead of hardcoded symbol names for phishing...
Vsevolod Stakhov [Wed, 11 Feb 2026 12:19:39 +0000 (12:19 +0000)] 
[Fix] Use fuzzy group and options instead of hardcoded symbol names for phishing detection

Detect text/html fuzzy matches by checking the symbol group ('fuzzy')
and parsing the type from symbol options (flag:hash:prob:type) rather
than pattern-matching on symbol names that may not exist.

2 months ago[Test] Add functional tests for logstats and mapstats
Vsevolod Stakhov [Wed, 11 Feb 2026 11:06:54 +0000 (11:06 +0000)] 
[Test] Add functional tests for logstats and mapstats

Robot Framework tests covering:
- logstats: JSON output, text output, symbol filter, alpha_score
  warning, stdin mode, scan time display
- mapstats: map loading, inline comments for plain/IP/regexp maps,
  match counts, regexp matching

Includes test data: sample log files, plain/IP/regexp map files,
and a minimal multimap config.

2 months ago[Fix] Fix missing inline comments in mapstats output
Vsevolod Stakhov [Wed, 11 Feb 2026 09:54:30 +0000 (09:54 +0000)] 
[Fix] Fix missing inline comments in mapstats output

rspamd_regexp_search() truncates captures at the first unmatched
optional group, so when the score group was absent the comment
group was lost. Extract comments with Lua patterns before passing
the line body to rspamd_regexp.

2 months ago[Minor] Add colored output and TTY-aware progress to logstats/mapstats
Vsevolod Stakhov [Wed, 11 Feb 2026 09:25:31 +0000 (09:25 +0000)] 
[Minor] Add colored output and TTY-aware progress to logstats/mapstats

Gate spinner and ANSI escape codes behind isatty() so piped output is
clean. Add ansicolors to logstats (Ham/Spam/Junk labels, symbol names,
actions, warnings, summary) and mapstats (map status, match counts,
unmatched warnings).

2 months ago[Minor] Warn when symbols are filtered by alpha_score
Vsevolod Stakhov [Wed, 11 Feb 2026 09:12:10 +0000 (09:12 +0000)] 
[Minor] Warn when symbols are filtered by alpha_score

2 months ago[Minor] Handle blank lines in mapstats maps
Alexander Moisseev [Wed, 11 Feb 2026 06:41:23 +0000 (09:41 +0300)] 
[Minor] Handle blank lines in mapstats maps

Replace regex-based empty line detection with Lua pattern matching
to correctly identify and preserve blank lines instead of treating
them as syntax errors.

2 months ago[Minor] Use positional argument for log file in logstats/mapstats
Vsevolod Stakhov [Wed, 11 Feb 2026 09:05:50 +0000 (09:05 +0000)] 
[Minor] Use positional argument for log file in logstats/mapstats

2 months ago[Fix] Fix broken ip_within in mapstats: parse CIDR and use apply_mask return value
Vsevolod Stakhov [Tue, 10 Feb 2026 20:47:10 +0000 (20:47 +0000)] 
[Fix] Fix broken ip_within in mapstats: parse CIDR and use apply_mask return value

rspamd_ip.from_string rejects '/' in CIDR notation, so strip the mask
before parsing. Also apply_mask returns a new IP object rather than
modifying in place, so capture the return values.

2 months ago[Feature] Rewrite rspamd_stats.pl and mapstats.pl as rspamadm Lua subcommands
Vsevolod Stakhov [Tue, 10 Feb 2026 20:10:43 +0000 (20:10 +0000)] 
[Feature] Rewrite rspamd_stats.pl and mapstats.pl as rspamadm Lua subcommands

Add rspamadm logstats and rspamadm mapstats commands that replace the
Perl utility scripts utils/rspamd_stats.pl and utils/mapstats.pl.

- lua_log_utils.lua: shared library for log format detection, timestamp
  conversion, compressed file handling, directory scanning, and progress
  spinner
- logstats.lua: full port of rspamd_stats.pl with all options including
  symbol filtering, bidirectional symbols, groups, correlations, score
  multipliers, time range filtering, and JSON output via UCL
- mapstats.lua: full port of mapstats.pl using native rspamd_config for
  multimap access, rspamd_regexp for regex maps with full flag support,
  and rspamd_ip for IP/CIDR matching (no external dependencies)

2 months ago[Test] Add unit tests for ring hash consistent upstream hashing
Vsevolod Stakhov [Tue, 10 Feb 2026 15:00:22 +0000 (15:00 +0000)] 
[Test] Add unit tests for ring hash consistent upstream hashing

Verify consistency, distribution, weight-scaling, stability, and
except-parameter behaviour of the Ketama-style ring hash introduced
in 4ea750466.

2 months ago[Rework] Replace broken Jump Hash with Ring Hash (Ketama) for consistent upstream...
Vsevolod Stakhov [Tue, 10 Feb 2026 14:52:30 +0000 (14:52 +0000)] 
[Rework] Replace broken Jump Hash with Ring Hash (Ketama) for consistent upstream hashing

Jump Consistent Hash (Lamping & Veach 2014) only handles bucket
addition/removal at the end of the range.  When an upstream in the
middle failed, the old code rehashed with mum_hash_step and retried
up to 20 times, which destroyed the consistency property: keys that
mapped to the dead node were redistributed randomly instead of
deterministically, and didn't return when the node recovered.

Replace with a Ketama-style ring hash:
- Each alive upstream gets MAX(weight,1)*100 virtual nodes on a
  sorted hash ring (keyed by name, order-independent).
- Lookup is a binary search: O(log(n*v)) instead of O(ln n) * retries.
- When an upstream fails, only its ~1/n fraction of keys slide to the
  next ring point — true minimal disruption.
- When it recovers, the same keys return — true consistency.
- The 'except' parameter walks forward on the ring instead of rehashing.
- Ring is rebuilt lazily (dirty flag set on active/inactive transitions).

2 months agoMerge pull request #5884 from rspamd/vstakhov-ssl-server
Vsevolod Stakhov [Tue, 10 Feb 2026 14:30:51 +0000 (14:30 +0000)] 
Merge pull request #5884 from rspamd/vstakhov-ssl-server

Implement HTTPS server support for workers

2 months agoMerge pull request #5883 from moisseev/webui
Vsevolod Stakhov [Tue, 10 Feb 2026 14:18:20 +0000 (14:18 +0000)] 
Merge pull request #5883 from moisseev/webui

[Minor] Update RequireJS to 2.3.8