Skip to content

v0.2.9

Choose a tag to compare

@rmyndharis rmyndharis released this 17 Jun 06:44

A reliability, security, and accessibility hardening release — no breaking changes. It tightens RBAC on
write endpoints, patches the ws/qs advisories, makes the busy message path and graceful shutdown
crash-resistant, fixes bulk-message terminal status, finally honors LOG_LEVEL, adds audit-log and
webhook-job retention, and improves dashboard accessibility and load-error states.

⚠️ RBAC tightening (action may be required): write endpoints for groups, contacts, labels, channels,
catalog, and status now require the OPERATOR role. If you used a VIEWER key for any of these writes,
switch it to OPERATOR (or ADMIN). Everything else is backward-compatible.

Security

  • Write endpoints for groups, contacts, labels, channels, catalog, and status now require the
    OPERATOR role
    , closing an unintended privilege gap where a VIEWER-role API key could create/leave
    groups, manage participants, block contacts, post statuses, send products, and mutate labels. Read
    (GET) endpoints remain open to any valid key, matching the message/session controllers.

    ⚠️ If you used a VIEWER key for any of these write operations, switch it to OPERATOR (or ADMIN).

  • Patched a high-severity ws advisory (and a moderate qs DoS) on the live socket.io transport by
    bumping in-range deps (ws→8.21.0, engine.io→6.6.9, qs→6.15.2, plus the incidental
    re-resolutions npm audit fix pulled in) in both the API and dashboard. Lockfile-only — no
    package.json/API change. The remaining advisories are build-only (sqlite3node-gyptar)
    and require a breaking sqlite3 major, deferred.

Added

  • LOG_LEVEL is now honored. It was read into config/compose but never applied (logging was hardcoded
    to info); the level (error/warn/info/debug/verbose) is now set at bootstrap.
  • Automatic audit-log retention. Audit logs older than AUDIT_RETENTION_DAYS (default 90; 0 disables)
    are pruned daily and once at startup — the existing cleanup() was never scheduled, so audit_logs grew
    without bound.

Fixed

  • Bulk-message batch status is now correct on cancel and stop-on-error. A cancelled batch could be
    silently reverted to PROCESSING (the final save overwrote the CANCELLED status with the stale
    in-memory one), and a stopOnError abort was reported as COMPLETED whenever at least one message had
    already been sent. The terminal status is now re-derived (cancelled → CANCELLED with reconciled
    counters; stop-on-error → FAILED; otherwise COMPLETED/FAILED).
  • Bulk-message item type is now validated against the allowed set (text/image/video/audio/document)
    with @IsIn, so an invalid type is rejected up front instead of failing mid-send.
  • Graceful shutdown is now robust. onModuleDestroy clears reconnect timers first and destroys engines
    in parallel, each isolated and time-bounded — so one hung/throwing Chromium can no longer abort teardown
    of the other sessions or stall shutdown.
  • A session that exhausts its reconnect attempts is now marked FAILED with a reason (surfaced via
    lastError) instead of sitting silently DISCONNECTED forever.
  • BullMQ webhook jobs are auto-evicted (removeOnComplete/removeOnFail retention) so completed/failed
    job payloads no longer accumulate unbounded in Redis (audit M19).
  • Engine-event handlers no longer risk unhandled promise rejections. Webhook dispatch is now
    self-contained (a failed webhook lookup is logged and swallowed, not rejected into the fire-and-forget
    callers), the onMessage/onMessageCreate hook chains carry a .catch(), and a process-level
    unhandledRejection backstop logs (instead of crashing) anything that still slips through. A transient
    DB hiccup on the busy message path can no longer drop the event silently or take the process down.
  • Audit-log writes are best-effort. A failed audit insert is logged and swallowed instead of turning
    an otherwise-successful operation (create/delete/start/stop session, etc.) into a 500.
  • Dashboard accessibility: toast notifications are now an ARIA live region (role="region"/aria-live,
    with role="alert" on error/warning toasts) so screen readers announce success/error feedback, and the
    toast close button has an accessible name. The API-key visibility toggles on the Login and API Keys pages
    now have state-reflecting aria-labels (show/hide). New common.showApiKey/common.hideApiKey strings
    across all locales.
  • Dashboard no longer shows a misleading "nothing here" empty state when a list fetch fails. The
    Webhooks, API Keys, and Logs pages discarded the query error and rendered the empty state on failure;
    they now surface an accessible error banner (role="alert") so the user knows the data failed to load.