Skip to content

Releases: Notifuse/notifuse

v30.2

Choose a tag to compare

@pierre-b pierre-b released this 13 May 19:25
  • Improvement: SES 4.4.7 Message expired (retry-exhaustion) now suppresses on the first event, and any recipient that accumulates 5 consecutive soft bounces with no successful delivery in between is also suppressed; MessageTooLarge/ContentRejected/AttachmentRejected never count (#323).
  • Fix: Email AI Assistant setEmailTree tool now declares items on its children array schema, so OpenAI-compatible providers no longer reject the request with array schema missing items (#324). Anthropic was already lenient about this; only OpenAI-compatible endpoints surfaced the error.
  • Improvement: /api/templates.compile now accepts and returns subject and subject_preview, rendered through the same Liquid engine used at send time. Previously the API only returned mjml/html, so the console preview drawer rendered the subject in-browser with liquidjs, which could diverge from the Go-side liquidgo output used by the send pipeline. Any API consumer can now retrieve the rendered subject directly (#329).
  • Deps: Bumped liquidjs to 10.25.7, postcss to 8.5.14, fast-xml-parser override to ≥5.8.0 (+ new fast-xml-builder ≥1.1.7 override), and github.com/prometheus/prometheus to v0.311.3.

v30.1

Choose a tag to compare

@pierre-b pierre-b released this 12 May 10:07
  • Security: Bumped go.opentelemetry.io/otel to v1.41.0 in telemetry/go.mod (CVE-2026-29181).
  • Deps: Bumped gomjml to v0.12.0.

Breaking Changes

  • SMTP auth with SMTP_USE_TLS=false: When TLS is explicitly disabled, the SMTP client now uses PLAIN-NOENC (go-mail's SMTPAuthPlainNoEnc) explicitly instead of SMTPAuthAutoDiscover. Previously, go-mail's auto-discover refused PLAIN/LOGIN over an unencrypted connection (only SCRAM-SHA-* and CRAM-MD5 were tried), and SMTPAuthPlain itself also refused unencrypted connections at the AUTH step. PLAIN-NOENC bypasses both gates while sending the standard AUTH PLAIN command on the wire, so any server that advertises AUTH PLAIN (e.g. local maddy/Mailpit relays) accepts it. Operators who have set SMTP_USE_TLS=false have already accepted plaintext credential transit, so forcing PLAIN aligns with their stated intent. Action: none if your relay accepts PLAIN. If your relay only accepts SCRAM/CRAM-MD5, you must enable TLS (SMTP_USE_TLS=true) — auto-discover continues to apply when TLS is on.

v30.0

Choose a tag to compare

@pierre-b pierre-b released this 24 Apr 11:27

Breaking Changes

  • Webhooks: Signatures now conform to the Standard Webhooks specification and match the published verification code in the docs (#318)
    • Stored secrets are now prefixed with whsec_ and the 32 random bytes after the prefix are base64-decoded before use as the HMAC key (previously the 44-char base64 string was used directly as raw bytes, making the published Python/JS/Go/PHP verification snippets always fail)
    • V30 migration rotates every existing webhook secret to the new format. Subscriptions, URLs, event filters, enabled state, and delivery history are preserved; only the secret value changes
    • Consumer action required: copy the new whsec_… secret from the console into your environment and update your verification code to the spec-compliant form shown in the docs. Deliveries that fire during the gap will retry automatically once the consumer's secret is updated

Data migration

  • Timezone: Europe/Kiev is rewritten to the IANA-canonical Europe/Kyiv across workspaces.settings, contacts.timezone, segments.timezone, and broadcasts.schedule.timezone. Stored Europe/Kiev continued to resolve at runtime via Go's tzdata alias, but the console dropdown (which no longer lists the obsolete name) showed an empty selection for affected rows. The contacts triggers are briefly disabled around the rename so it does not emit contact.updated webhook events or fill contact_timeline with rename entries.

Other changes

  • Task dispatch no longer swallowed by auth proxies (#320, #317): The scheduler's internal POST /api/tasks.execute client now refuses to follow redirects (CheckRedirect = http.ErrUseLastResponse). Previously, an auth-walling reverse proxy (Cloudflare Access, Authelia, oauth2-proxy, Traefik Forward Auth, etc.) sitting in front of the API could respond with a 302 to its login page; Go's default http.Client followed the redirect as a GET, the login page returned 200 OK HTML, and the dispatcher logged "dispatched successfully" while the task never ran. The 302 is now surfaced in the non-200 branch (with the Location header logged) so the misconfiguration is loud instead of silent. Operator note: if your ingress performs an HTTP→HTTPS redirect on the API path, set $API_ENDPOINT to the final HTTPS URL — the dispatch client will no longer silently upgrade it.
  • Task scheduler: Scheduler tick no longer waits on in-flight HTTP dispatches, so one slow recurring task can't delay dispatch of others on the same tick. Stale tasks left in running with an expired timeout_after are now reclaimed by MarkAsRunningTx on a subsequent tick instead of looping on 409 indefinitely (#317).
  • Task dispatch observability: The scheduler-side "Task execution request dispatched successfully" log now includes the HTTP status_code, and tasks.execute logs an entry line on the handler side. Diffing the two streams makes any remaining silent-interception failure mode visible.
  • Feature: Added AWS region eu-central-2 (Europe, Zurich) to the S3 provider and integrations region selectors (#316).

v29.5

Choose a tag to compare

@pierre-b pierre-b released this 20 Apr 10:08
  • Feature: Pause, resume, and cancel broadcasts mid-delivery — even after the orchestrator has finished enqueueing — and cancel is now allowed from the Processing state (#303)
  • Contacts: Added in-table bulk actions (multi-select delete, add to list, remove from list, unsubscribe) with progress modal and "Skipped" tagging for no-op cases (#299)

v29.4

Choose a tag to compare

@pierre-b pierre-b released this 17 Apr 11:17
  • Feature: Added SMTP_BRIDGE_TLS setting (off / starttls / implicit) to let operators run the SMTP bridge behind a TLS-terminating reverse proxy or in implicit-TLS (SMTPS) mode (#314)
  • Feature: Blog RSS 2.0 and JSON Feed 1.1 syndication — automatic /feed.xml and /feed.json endpoints per workspace, per-category feeds, conditional GET with ETag, gzip, XSS-sanitized content, autodiscovery <link> tags, and admin-configurable feed settings
  • i18n: Notification center confirmation banner (subscribe/unsubscribe result) is now translated in all supported languages instead of always showing English (#315)

v29.3

Choose a tag to compare

@pierre-b pierre-b released this 12 Apr 11:07

Fix: Double opt-in confirmation link now correctly transitions contacts from Pending to Active instead of resending the confirmation email in a loop (#313)

v29.2

Choose a tag to compare

@pierre-b pierre-b released this 09 Apr 07:29
  • Feature: Added OpenAI as LLM provider alongside Anthropic — supports any OpenAI-compatible endpoint (OpenRouter, Ollama, vLLM, LiteLLM, Azure, etc.) via custom base URL, with full streaming and tool use support
  • Security: Updated liquidjs to 10.25.5 in console and vitest to 3.2.4 in notification center to fix 5 Dependabot vulnerabilities
  • Deps: Updated @vitejs/plugin-react to 5.2.0 in console and notification center
  • Feature: Added System Settings drawer for root admin to view and edit system configuration from the dashboard
  • Workspace: Enforce workspace creation limits via MAX_WORKSPACES env var (0 = unlimited), with "upgrade your plan" messaging in the console
  • Improvement: Email open tracking now works independently of click tracking — added Cache-Control headers to prevent proxy caching, encrypted tracking URLs (/t/, /r/) to avoid pixel blocker detection, and padded tracking pixel (#307)
  • i18n: Added Polish language support to the notification center

v29.1

Choose a tag to compare

@pierre-b pierre-b released this 07 Apr 20:47
  • Security: Upgraded Vite to 7.3.2 in console and notification center to fix arbitrary file read via WebSocket (CVE-2026-39363)
  • Fix: Removed invalid visibility attribute from MJML section output that caused template compilation errors (#305)
  • Fix: Automation now exits when contact is unsubscribed/bounced/complained for marketing emails, while still allowing transactional emails to be sent (#304)
  • Fix: Social media buttons now link directly to pages by default instead of wrapping URLs in share prompts; added "Share link" toggle to social element settings (#306)

v29.0

Choose a tag to compare

@pierre-b pierre-b released this 04 Apr 13:30

[29.0] - 2026-04-04

Breaking Changes

  • Rename: "SMTP Relay" renamed to "SMTP Bridge" throughout the application
    • Environment variables: SMTP_RELAY_* renamed to SMTP_BRIDGE_* (old names still accepted for backward compatibility)
    • Database settings keys migrated automatically via V29 migration
    • JSON API: smtp_relay_* fields renamed to smtp_bridge_* in setup endpoints
    • Frontend routes: /settings/smtp-relay changed to /settings/smtp-bridge
    • UI labels: "SMTP Relay" changed to "SMTP Bridge"
  • Workspace: Enforce team member limits via MAX_USERS env var (0 = unlimited), with checks on invite, accept invitation, and direct add — API key users are excluded from the count
  • Security: Fixed SSRF vulnerability in /api/detect-favicon endpoint by adding a safe HTTP client with private IP blocking, DNS rebinding protection, scheme validation, and response size limits
  • Security: Upgraded happy-dom to 20.8.9 in notification center and picomatch to 4.0.4 in console
  • Improvement: SMTP EHLO hostname now defaults to the from-email domain instead of the SMTP host, improving compatibility with strict providers (#301)
  • Security: Updated lodash/lodash-es to 4.18.x, brace-expansion to 5.0.5, and yaml to 2.8.3 to fix prototype pollution, code injection, ReDoS, and stack overflow vulnerabilities

v28.4

Choose a tag to compare

@pierre-b pierre-b released this 27 Mar 10:33
  • Security: Upgraded picomatch to 4.0.4 in notification center
  • Contacts: Fixed dropdown menu becoming unresponsive after deleting contacts, and pagination state now persists in URL across page refreshes (#294)
  • Templates: Test emails now load the full contact record, so Liquid variables like {{ contact.first_name }} render correctly