bilias/rqwatch
bilias/rqwatch is a lightweight Laravel package for watching Redis queues. It gives visibility into queued jobs and queue activity, helping you spot stalled or failing work and keep background processing healthy.
Architecture fit. The release notes describe Rqwatch as a standalone PHP application deployed next to Rspamd/Postfix (CLI entrypoint ./bin/cli.php, config.local.php, Apache vhost fragment, Rspamd local.d config) — not a Composer-installable Laravel package and not something consumed via composer require / service provider registration. The TPM framing should be corrected up front: we do not "leverage" this as a library inside a Laravel app. We operate it as a quarantined-mail management service that integrates at three boundaries: (1) the Rspamd HTTP map endpoint (/maps) that Rspamd servers fetch, (2) the Rspamd/mail APIs used for get/release, and (3) the mail store / notification path. Any expectation that this is a drop-in Laravel dependency is a scoping error that should be resolved before planning.
Integration feasibility. Feasible but multi-touch. v2.1.2 is not an app-code-only update: it requires a coordinated change across database migrations, .env, a second config file (metadata_exporter.conf), Apache vhost, an Rspamd force_actions.conf drop-in, and (new in this release) syslog on the web node for release auditing. That is five independently owned surfaces (app, web tier, MTA/Rspamd tier, secrets/config, log pipeline), which is the main schedule risk. Nothing in the release is delivered as a packaged migration, config-management hook, or health check.
Technical risk. High, driven by two primary factors plus a set of smaller exposures:
/maps files under the shipped Apache config, session non-invalidation on alias/credential/admin-flag changes, username enumeration via login timing, log-line injection, and CSRF-less logout. Pre-upgrade, these are live exposures. If our deployment matches the shipped Apache config, /maps disclosure and BCC leakage may already have occurred, which is an incident-response question, not merely a patch question.mail_logs.ever_stored index build scales with table size (~2s at ~860k rows) and blocks writes cluster-wide for that duration. Duration is a function of our row count, not the reference number.cron:notifications -B (blacklisted mails at any score) changes outbound notification volume if adopted. Fuzzy learning remains off by default but expands the change surface if enabled.Key questions.
db:migrate run). This could double or triple the effort.mail_logs row count? This sets the write-outage window and whether it can be done in a normal change slot.metadata_exporter.conf? RSPAMD_API_PASS must be changed in .env and that file together, or import/remote get/release breaks./maps currently reachable from anything other than our Rspamd servers? (Determines whether we have a disclosure event.)CHANGE_ME_ placeholders in .env today, or an empty MAILER_FROM? Both now hard-fail import and password-less release.rqwatch_virus_no_notify already exist? The migration will fail; needs $notification_skip_virus_map set first.cron:notifications without -B? Adoption of -B changes blacklisted-mail delivery behaviour and support expectations.Stack fit. PHP runtime + MySQL/MariaDB (Galera-consistent writes implied), Rspamd, Postfix, Apache, Redis (DNS failure cache, login throttle), syslog, and cron. New scheduled work: cron:import_spool (replay of imports that failed during the migration window; $import_spool is on by default) and cron:fuzzy_cleanup (only if fuzzy learning is enabled — it is off by default). cron:notifications -B is an option change, not a new job, but it alters delivery volume. Fuzzy learning introduces a new external dependency surface on Rspamd's fuzzy worker; treat it as a separate, opt-in phase, not part of the security patch.
Migration path. Hard-gated and non-skippable:
Compatibility (breaking for users and for us).
$notification_links_idle_minutes (10) of idle. $notification_links_one_time = false restores reopen-until-release behaviour — a product decision, not a default we should accept blindly.$USER_MAP_MAX_ENTRIES = 0 to disable).CHANGE_ME_ API passwords now block import and remote get/release rather than silently proceeding.MAILER_FROM is empty; MAILER_DSN and MAILER_FROM are validated on the sending node.@domain).Sequencing.
mail_logs row count, Galera topology, /maps exposure, CHANGE_ME_ grep, existing rqwatch_virus_no_notify map, in-flight notification links, GetMail/Release URL shape (redirect or direct), syslog retention target, IPv6 IP map presence./maps exposure and BCC leakage warrant incident handling before the change window.mail_logs; time the ever_stored index build; run full db:migrate; verify import spool replay; exercise remote get/release through the real URL path to confirm the redirect refusal does not bite.RSPAMD_API_PASS in .env + metadata_exporter.conf; confirm MAILER_FROM/MAILER_DSN; set $notification_skip_virus_map if a name collision exists; confirm syslog destination and retention for release audit records.cron:import_spool, optionally cron:fuzzy_cleanup) → Apache <Location /maps> block restricted to Rspamd server IPs → Rspamd force_actions.conf → reload.$fuzzy_learn_flags, $fuzzy_learn_expire_days) and cron:notifications -B adoption as their own changes.Maintenance. Two new cron jobs to own and monitor (cron:import_spool is now load-bearing for correctness during any DB write stall, not optional; cron:fuzzy_cleanup only if fuzzy is enabled). A permanent two-file secret coupling between .env and metadata_exporter.conf — this is a drift risk that will bite on the next password rotation unless it is documented in the runbook. A new syslog consumer for release audit records — retention, rotation, and access controls all need an owner, and the user/client fields likely trigger a data-classification review. Personal map caps (1000/user/map) introduce a new support category. The rqwatch_virus_no_notify map is created empty by migration and must be maintained deliberately; empty means no behaviour change, which is the right default. IP map canonicalisation adds a small reconciliation step for any admin who maintains maps from external files.
Support. Expect a measurable helpdesk bump in the first 2–4 weeks: expired/broken notification links, "link says used", "why do I need JavaScript", "why is it asking for my current password", users hitting the 1000-entry map cap, and admins noticing IPv6 map entries have been rewritten in canonical form. Pre-briefing the service desk with the four behaviour changes is cheaper than handling them reactively. Note also that fuzzy learning is admin-only and off by default — keep it off during rollout so support has one fewer new surface. If cron:notifications -B is adopted, support expectations for blacklisted-mail delivery change and should be documented as part of that decision, not the security patch.
Scaling. Five pressure points: (a) the ever_stored index build blocks writes cluster-wide under Galera, duration linear in mail_logs size — schedule, don't roll into a normal deploy; (b) DB_WEB_MAX_STATEMENT_TIME (default 30s) is a new fail-fast on web DB statements, so previously "slow but working" searches now become user-visible errors — validate the heaviest searches against the 30s ceiling before go-live; (c) DNS resolution per details page is now capped, and SERVFAIL/timeouts are cached 60s instead of a negative 3600s TTL, which increases upstream DNS query volume relative to current behaviour; (d) release audit records double the write path (file + syslog) on every release — modest per event but new log volume that must be sized against existing syslog capacity; (e) login throttling now counts IPv6 clients on their /64, which is correct but will lock out shared IPv6 egress blocks faster than operators expect.
Failure modes.
| Failure | Symptom | Mitigation |
|---|---|---|
RSPAMD_API_PASS left as CHANGE_ME_ or .env/metadata_exporter.conf diverge |
Mail import and remote get/release fail hard | Single ticket, dual-write, verify both |
ever_stored index build on large mail_logs under Galera |
Cluster-wide write stall, length unknown until measured | Rehearse at prod scale; quiet window |
| Imports attempted during migration stall | Imports fail | $import_spool (default on) queues them; cron:import_spool replays — confirm cron is deployed in the same window |
Pre-existing rqwatch_virus_no_notify map |
Virus notification skip map migration aborts |
Set $notification_skip_virus_map to a free name, re-run |
/maps left unrestricted |
Whitelists and personal entries world-readable | Apache <Location /maps> block, IP-allowlist Rspamd servers only |
MAILER_FROM empty on the sending node |
Password-less release refused with a message | Set and verify on every release-capable node |
| GetMail/Release URL resolves through a redirect (LB, CDN, TLS terminator) | Release attempts fail although the API is healthy | Address API directly from release-capable node; add post-deploy check to verification |
| Release audit syslog destination unavailable or full | Release logging gaps; possible release-path errors depending on syslog behaviour | Validate syslog sink during staging; ensure disk/forwarder headroom |
| Pre-release notification links | Users hit dead links | Comms + deliberate choice on $notification_links_one_time |
| Runaway search | Request fails at DB_WEB_MAX_STATEMENT_TIME instead of hanging |
Expected; tune default if it fires on legitimate queries |
| IPv6 map entries non-canonical before upgrade | Entries rewritten on write; admin diff noise | Reconcile IP map exports post-upgrade |
| Galera node divergence post-migration | Inconsistent mail/log state | Run db:migrate once, from a single node, verified |
Ramp-up. Low-to-moderate for operators, moderate for support. The upstream docs are unusually good — dedicated upgrade guides per version hop, DB migration guides, an Apache contrib fragment, a Rspamd force_actions drop-in — which shortens ramp-up. The gap is that nothing here is automated: no packaged migration, no config management hook, no health check. Budget roughly one engineer-week for pre-flight, staging rehearsal, and the change itself, plus 2–4 weeks of elevated support, plus a separate phase if fuzzy learning or cron:notifications -B adoption is pursued. If the version gate (item 1 in Key Questions) puts us below 1.8.4, add the 1.8.x hop and re-baseline.
How can I help you explore Laravel packages today?