What's new

Changelog

New features, improvements, and fixes in euromail.dev.

RSS feed

Fixed

  • landing: WCAG keyboard and screen-reader accessibility pass #548

Fixed

  • daily-report: count direct-MX 'sent' status as delivered #546

New

  • api: add new-account burst circuit-breaker

Fixed

  • auth: invalidate JWT sessions on password reset/change #541
  • gdpr: make account hard-delete all-or-nothing in a transaction #542
  • k8s: move ollama to a dedicated namespace so the LLM can schedule
  • k8s: stop release tag-bump from clobbering the ollama image
  • api: keep scam classification alive past the sync timeout (AUDIT L9)
  • api: ramp send-velocity limits by account age and fix off-by-one
  • auth: prevent email verification from lifting an abuse suspension

Fixed

  • auth: run Argon2 password verification on the blocking pool #539
  • k8s: pin ollama image to 0.30.0 and harden pod security #538
  • worker: only suppress on Message-ID-verified ARF complaints #543

New

  • spa: best-in-class UI polish pass #536

New

  • auth: add MFA verification step to SPA login flow
  • spa: add favicon and dynamic page titles per route
  • spa: complete dashboard with all pages, analytics, and full API coverage

Fixed

  • spa: add aria-modal, role=dialog, and aria-labelledby to Modal component
  • spa: allow session auth on API endpoints for SPA dashboard access
  • ci: allowlist frontend SPA pages in gitleaks to permit curl example snippets

New

  • spa: deploy React SPA as app.euromail.dev #534

Fixed

  • spa: add missing frontend/index.html and unblock it from .gitignore

New

  • spa: React SPA foundation with session auth, Redis login guard, CSRF protection #533

New

  • streams: message streams for isolated email reputation tracking #531

Fixed

  • fbl: deduplicate reports and link complaints to original email #532

New

  • dashboard: complete dark mode migration across all templates #525

Fixed

  • migrations: include 'fbl' in suppressions_reason_check from migration 084 #526
  • dashboard: pluralize count labels correctly when N=1 #524
  • ci: align team-invite test with upsert behavior and ignore alpha-pin advisories #523

New

  • sending-ips: admin /admin/sending-ips dashboard + docs update #522
  • sending-ips: worker reads active set from DB + auto-retire stale rows #521
  • sending-ips: add SPF auto-sync via Cloudflare DNS API #520
  • sending-ips: persist PTR + DNSBL state, auto-pause on bad signals #519
  • sending-ips: add Hetzner Robot API reconciler with Redis lease #518
  • sending-ips: add lifecycle registry and node-local discovery #517

New

  • worker: add smtp-2 and smtp-3 outbound relays on bare-metal nodes #515

New

  • api: add transactional field with default true to suppress list headers #512
  • deliverability: spamtrap and typo-domain recipient guard #511

New

  • deliverability: block sends to spamtrap and typo-domain recipients #510
  • deliverability: auto-suppress stale recipients after 90 days of inactivity #509
  • deliverability: per-recipient engagement tracking #508
  • bounces: suppress addresses after 5 cumulative soft bounces
  • delivery: parse Retry-After hints from SMTP 4xx responses
  • reporting: add TLS-RPT report receiver (RFC 8460)
  • dkim: add ED25519 key generation and signing support
  • inbound: add SPF verification for incoming email

Fixed

  • delivery: increase retry window from 3min to 42h
  • dkim: add missing AUID to ED25519 signer for consistency with RSA
  • api: bound scam classifier accept latency #507

Fixed

  • team: upsert invitation on resend instead of failing silently #506
  • dashboard: org switcher can't return home and invite not accepted when logged in #505
  • smtp: accept all recipient domains for inbound email #504

New

  • dashboard: show sending IP in email detail view and delivery log #502

Fixed

  • dashboard: use system DNS resolver for DNSBL blacklist checks #500
  • dns: use system resolver in dns_verification and validate route #501
  • worker: suppress Feedback-Id and Cfbl-Address on transactional emails #498
  • dashboard: replace IP whitelist with rate-limit on public dashboard ingress #499
  • security: restrict dashboard to operator IP and rate-limit API #494
  • worker: correct COUNT(*) type from i32 to i64 in domain health analyzer #497
  • smtp: use system DNS resolver instead of hardcoded Google DNS #496
  • security: apply 6 unmerged security audit fixes ()

Fixed

  • security: address audit findings

New

  • security: add gitleaks CI guard and SECURITY.md policy #421
  • monitoring: expose prometheus scrape annotations on euromail-worker pod

Fixed

  • api: add WWW-Authenticate header to metrics 401 and harden tests
  • api: require METRICS_BEARER_TOKEN in production and constant-time compare
  • ci: allowlist k8s/secrets/ SealedSecret manifests in gitleaks
  • ci: preserve original tarball filename for gitleaks sha256sum verification
  • config: move METRICS_BEARER_TOKEN production validation from AppConfig to ApiConfig
  • security: harden worker/monitor metrics auth and fix Prometheus scraping
  • security: narrow gitleaks allowlist and remove misleading IngressRoute annotation
  • security: raise IngressRoute priority to 100 so IPAllowList beats catch-all Ingress
  • tests: add missing AppState/AppConfig fields to metrics_auth test
  • tests: correct misleading subtle::ct_eq comment in metrics_rejects_token_prefix
  • llms: remove hardcoded [email protected] from skill description
  • openapi: correct batch response schema and domain update docs
  • ci: pin shared docker-build-targets to multi-arch commit
  • landing: add llms.txt discovery to robots.txt and nginx aliases
  • smtp: add accepted_domains to RouterReceiveConfig test helpers
  • smtp: rename local_part to strip_angle_brackets and update stale liveness doc
  • smtp: replace manual splitn with split_once for clippy
  • smtp: validate RCPT TO domain at connect time, reject unknown domains
  • ci: update shared workflow to multi-arch build commit #490
  • security: disable k8s API token automount in all pods + seal secrets

New

  • ollama: migrate storage from Longhorn to local-path on bare-metal-llm
  • security: encrypt secrets at rest with AES-256-GCM #485
  • dashboard: Phase 5 settings/admin + Phase 6 responsive tables + Phase 7 WCAG 2.1 AA #483

Fixed

  • lint: remove redundant encrypt closures flagged by clippy #487
  • security: add encryption_key to AppConfig struct literals in tests and test helpers #486

New

  • dashboard: migrate settings/admin templates to warm palette #482
  • dashboard: migrate deliverability/activity templates to warm palette
  • dashboard: migrate inbound and mailboxes pages to DESIGN.md palette #480
  • monitor: deploy euromail-monitor crate (Dockerfile + k8s + CI) #474
  • monitor: scaffold euromail-monitor crate with 4 SMTP probes (Phase 1) #473
  • smtp: synthetic-probe short-circuit in router data_end #472
  • smtp: flip tcp/25 to tokio-native listener #459

Fixed

  • monitor: update TODOS with Phase 2 completion notes #479
  • monitor: use smtp-1 private IP for SMTP probe target #477
  • smtp: put domain first in EHLO response per RFC 5321 + fix TLS server name #478
  • monitor: point SMTP probes at LB IP instead of worker Service name #476
  • monitor: align deployment image tag with kustomization baseline #475

New

  • worker: wire STARTTLS into the parallel SMTP listener #465

Fixed

  • scripts: make smtp_soak_diff.sh DMARC fixture trip the parser #471
  • smtp: restore body's trailing CRLF stripped by smtp-proto receiver #470
  • scripts: make smtp_soak_diff.sh actually exercise both paths #469
  • smtp: advertise STARTTLS in router EHLO when TLS is configured #466
  • platform: mark platform account active so team-switch works #463

New

  • platform: grant kalle owner membership of platform account #462
  • smtp: add tokio listener + connection driver for receive module #448
  • smtp: AUTH PLAIN + LOGIN with handler-owned rate limit #450
  • smtp: STARTTLS upgrade via tokio-rustls for receive module #449
  • worker: start parallel tokio-native SMTP listener for soak #455
  • smtp: add tokio-native receive module (Handler trait + session FSM) #446
  • smtp: emit per-kind ingress counters and listener liveness probe #444
  • worker: daily digest of platform mailbox inbound to MAILBOX_DIGEST_TO #452
  • platform: forbid non-platform accounts from claiming euromail.dev #447

Fixed

  • dashboard: guard checks access in dns_records.html DMARC set #457

New

  • platform: seed agent mailboxes for general euromail.dev addresses #445

New

  • api: wire scam classifier into /v1/emails pre-send path #439
  • common: add scam_classifier module for pre-send phishing checks #438

Fixed

  • api: deny-all CORS default and validate allow-list on startup #409 #436
  • worker: distinguish daily status enqueued vs delivered #405

New

  • worker: system email metrics + PII log scrubbing #404
  • domains: include DMARC record in domain setup DNS table #398
  • smtp: route system emails through worker via email:system stream #397

Fixed

  • queue: loop XAUTOCLAIM cursor until pending list is drained #401
  • auth: rate-limit forgot_password_submit (per-IP + per-email) #400
  • smtp: bind system email egress to SENDING_IPS[0] #395
  • dashboard: register mailboxes/message_detail.html in template env #394

New

  • dashboard: add mailbox message detail view + declutter list page #392
  • dashboard: add LLM provider preference radio group to integrations page #387

Fixed

  • worker: send Ollama keep_alive as integer, not duration string #393
  • worker: route LLM calls through a separate HTTP client (no SSRF resolver) #391
  • team: send invitation emails via platform domain, not customer pipeline #390
  • worker: disable Gemma 4 thinking mode in Ollama requests #389

New

  • worker: add Ollama+Gemma 4 backend with resolver and per-account fallback #385
  • accounts: add llm_provider_preference column and typed enum #384
  • dashboard: migrate contact_lists pages to DESIGN.md palette #378
  • dashboard: migrate email templates pages to DESIGN.md palette #380
  • dashboard: migrate newsletters pages to DESIGN.md palette #382
  • dashboard: migrate signup_forms + onboarding to DESIGN.md palette #379
  • dashboard: migrate webhooks pages to DESIGN.md palette #381
  • dashboard: migrate api_keys and api_playground pages to DESIGN.md palette #375
  • dashboard: migrate domains pages to DESIGN.md palette #374
  • dashboard: migrate emails pages to DESIGN.md palette #376
  • dashboard: migrate overview page to DESIGN.md palette #373
  • dashboard: align dark-mode tokens to DESIGN.md dark palette #371
  • dashboard: migrate MFA pages onto DESIGN.md system #370
  • dashboard: bring 404 and 500 pages onto DESIGN.md system #369
  • dashboard: migrate chrome to warm DESIGN.md palette #368
  • dashboard: apply European Editorial design to auth pages #363
  • landing: apply brand stripe + unify fonts across docs/blog/legal #366
  • email: apply European Editorial design to daily status report #361
  • email: apply European Editorial design to system emails #359
  • email: redesign team invitation email to match DESIGN.md + fix XSS #360

Fixed

  • tests: assert 202 ACCEPTED in quota overage tests #364

Fixed

  • security: SSRF + stored XSS + k8s hardening + rustls-webpki bump #356

New

  • dashboard: per-account Anthropic API key settings #353
  • mailbox: threading headers, quote stripping, Haiku classification #352

Fixed

  • landing: allow Bunny Fonts and Fontshare in CSP #355
  • dashboard: register integrations.html in template loader #354

New

  • mailbox: at-least-once delivery with lease/ack/nack

Fixed

  • api: allow unsafe-eval in Swagger UI CSP so docs page renders

New

  • api: add per-email tracking override and suppress_list_management_header
  • dashboard: add tab navigation to analytics page
  • dashboard: simplify navigation with grouped sidebar and page consolidation
  • security: close remaining agent security gaps
  • security: add agent-specific security hardening for mailbox API
  • security: advanced prompt injection defenses for agent mailboxes
  • add dashboard delight, MCP tools, and GDPR coverage for agent mailboxes
  • api: add contact book and analytics for agent mailboxes
  • api: add full-text search and labels for agent mailbox messages
  • api: add reply API and conversation threading for agent mailboxes
  • worker: add attachment storage via Hetzner Object Storage
  • worker: add auto-responder with loop prevention for agent mailboxes
  • worker: add webhook support for agent mailbox messages
  • dashboard: register missing templates and redirect logged-in users from login #332
  • smtp: add STARTTLS support for inbound SMTP server #331

Fixed

  • landing: add Python SDK to code examples and deploy 404 page
  • dns: enforce DMARC reject and SPF hard fail policies
  • tracking: change tracking default to opt-in for better deliverability
  • security: restrict mailbox local_part to safe characters
  • migration: remove CONCURRENTLY from GIN index creation
  • resolve clippy warnings and update test fixtures for agent mailbox fields
  • dashboard: remove unsupported truncate filter from mailbox detail
  • smtp: use Trusted SSL config to serve full certificate chain
  • dashboard: use format_date/format_datetime filters in mailbox templates #330

New

  • dashboard: add dark mode support to auth pages
  • docs: add client-side search to API documentation
  • docs: add SDK code examples with language tabs for all 4 SDKs
  • status: configure Vartio status page with 5 monitors
  • api: add docs_url field to error responses
  • dx: add Makefile for common dev commands
  • dashboard: add mailboxes page for agent mailbox management
  • api: add agent mailboxes for AI agent email reception
  • api: support custom domains for agent mailboxes

Fixed

  • landing: correct doc links and left-align hero #329
  • align design system (fonts, colors, CSP, GDPR) #328
  • status: update links to use status.euromail.dev custom domain
  • dashboard: update chart fonts to Satoshi and add dark mode support
  • landing: add ARIA semantics to mobile menu and code tabs
  • status: correct SMTP monitors to mail1/mail2.euromail.dev
  • landing: add prefers-reduced-motion support for accessibility
  • dashboard: increase DNS copy button touch targets to meet 44px minimum
  • dashboard: replace Albert Sans remnants with Satoshi
  • dashboard: serialize activity events as JSON to prevent HTML entity encoding in URLs
  • dashboard: load custom fonts on standalone auth pages
  • common: accept mail1/mail2.euromail.dev in MX verification
  • dashboard: remove broken status page link
  • landing: add branded 404 page replacing bare nginx default
  • landing: serve quickstart and feature guides
  • dashboard: correct MX record instructions on inbound emails page
  • dashboard: show CNAME verification failure reason on tracking domain
  • dashboard: correct route syntax in insights router

New

  • billing: add annual billing with 2 months free
  • billing: annual billing with 2 months free + Stripe webhook fix
  • billing: overage billing for paid plans
  • billing: overage billing for paid plans + Stripe usage reporting fix
  • dashboard: add step-by-step onboarding wizard
  • status: add Vartio.dev status page integration
  • fbl: add ISP feedback loop ingest, archive, and dashboard
  • insights: add Claude-powered weekly operational insights
  • ISP feedback loops, per-domain self-tuning, and Claude-powered insights
  • worker: add per-domain health throttling and account auto-pause webhooks
  • analytics: add per-link click breakdown for tagged emails
  • analytics: add per-link click tracking with GET /v1/emails/{id}/links
  • newsletters: add tags and per-link click analytics
  • newsletters: add tags to newsletters and GET /v1/analytics/tags

Fixed

  • api: correct route syntax for analytics tags endpoint
  • billing: report Stripe overage usage before monthly quota reset
  • billing: resolve annual price ID in Stripe webhook plan lookup
  • insights: remove cross-account throttle data leak and harden acknowledge ownership
  • analytics: count sent events as delivered in stats aggregator
  • tracking: filter bot user-agents from open tracking pixel

New

  • contact-lists: add welcome email automation for new subscribers

Fixed

  • worker: add Precedence: bulk and List-Id headers for list emails
  • contact-lists: harden welcome email based on engineering review

New

  • landing: add favicon.ico for browsers requesting /favicon.ico

New

  • dashboard: add admin DNSBL blacklist checker #324
  • subscribe: use customer tracking domain for confirm links
  • signup-forms: add configurable from_address per signup form
  • subscribe: route confirmation emails through pipeline with branding

Fixed

  • infra: set SMTP EHLO hostname to mail3.euromail.dev for FCrDNS match #326
  • infra: route outbound SMTP through 204.168.195.152 to bypass Microsoft S3140 #325
  • api: remove duplicate /confirm route registration
  • migration: use IF NOT EXISTS for from_address column
  • api: properly isolate subscribe routes from restrictive global CORS

New

  • dashboard: add double opt-in toggle to contact list detail page
  • embed: add default styles to signup form embed.js
  • signup-forms: add customizable messages for form localization
  • dashboard: add inbound email toggle to domain detail page
  • design: European Editorial redesign with anti-slop rules
  • smtp: switch bounce server to port 25 natively
  • smtp: unified SMTP router for all inbound mail on port 25

Fixed

  • api: allow any origin for subscribe embed endpoints
  • admin: allow admin to change own plan and fix domain deletion FK
  • db: repair domain deletion FK on partitioned emails table
  • dashboard: cascade plan change to sub-accounts in admin panel
  • ci: correct runner label for update-k8s-tags job
  • dashboard: replace oversized checkmark animation with text-only copy feedback

New

  • dashboard: simplify domain detail page to Resend-style minimal layout
  • docker: enable port 25 binding for non-root worker process
  • seo: add sitemap index combining blog, docs, and legal sitemaps
  • smtp: set bounce SMTP to standard port 25 for inbound delivery

Fixed

  • api: install rustls CryptoProvider to prevent subscribe panic
  • blog: fix newsletter CORS error and merge duplicate CTAs
  • blog: use correct signup form slug for newsletter subscribe
  • domain-connect: mark MX record as essential OnApply
  • domain-connect: revert MX to essential Always (required for bounce handling)
  • smtp: add NET_BIND_SERVICE capability to bind port 25
  • smtp: capture tokio Handle for bounce/DMARC/complaint handlers
  • smtp: revert bounce port to 2525 with iptables PREROUTING redirect

New

  • blog: add "Send Your First Email in 5 Minutes" getting started tutorial
  • blog: add "Why We Chose Rust for Transactional Email" post
  • ci: add Python SDK publish workflow with PyPI trusted publisher
  • dashboard: add animated SVG icon system for UI state feedback
  • dashboard: animated SVG icon system for UI state feedback ([#icons](https://github.com/kalle-works/euromail.dev/issues/icons))
  • dashboard: one-click Cloudflare DNS setup via Domain Connect
  • dashboard: personalized code examples in onboarding with API key pre-filled
  • docs: add sidebar navigation with TOC and sibling page links #321
  • domains: configurable sending subdomain for domain reputation isolation
  • video: add procedurally generated ambient audio track
  • video: add Remotion marketing video project
  • video: design polish — iconic SVGs, centered grid, ambient audio ([#polish](https://github.com/kalle-works/euromail.dev/issues/polish))

Fixed

  • dashboard: correct loading spinner rotation origin
  • dashboard: redesign loading icon from spinner to envelope with pulsing dots
  • docs: use consistent X-EuroMail-Api-Key header and update DNS docs
  • domain-connect: add txtConflictMatchingMode to DKIM record
  • domain-connect: use Prefix txtConflictMatchingMode with v=DKIM1 prefix
  • landing: correct Go SDK import path to kalle-works/euromail-go
  • landing: hide nginx version from response headers
  • landing: update SDK links to standalone repos, add Python SDK
  • security: remediate CSO audit findings
  • tests: add missing provider_warmup_limits field and suppress clippy type_complexity
  • tests: correct remaining CREATED -> ACCEPTED assertion in quota overage test
  • tests: correct status codes and suppression reason in integration tests
  • worker: seed warmup state for configured sending IPs on startup

New

  • daily status report email with delivery metrics and warmup status #320

New

  • billing: add annual billing with 2 months free #316
  • dashboard: add dark mode with toggle and localStorage #317
  • dashboard: warm empty states with CTAs across 10 pages #311
  • onboarding wizard and overage billing for paid plans #313
  • security.txt and suppression CSV import/export #310
  • status: add Vartio.dev status page integration #312
  • team: add team management with roles and invitations #314
  • team: auto-accept, account switcher, RBAC, invitation emails #315

New

  • api,sdk: accept string or array for email `to` field
  • api: add /.well-known/security.txt (RFC 9116)
  • api: add signup forms CRUD API and embeddable JS widget
  • api: add suppression bulk import and CSV export
  • dashboard: add dedicated sending IP admin UI
  • dashboard: add send test email button to overview page
  • docs: add quickstart guide with all 4 SDK examples
  • implement remaining issues (,,,) #300
  • landing: add Go SDK example and documentation links
  • sdk: add missing API methods, fix response deserialization, add examples
  • sdk: add signup form methods to all 4 SDKs
  • smtp,dashboard: add SMTP headers and quota exceeded warnings #299
  • worker: add IP warmup auto-progression with bounce monitoring

Fixed

  • dashboard: apply format_datetime filter to raw timestamps in template list and version history
  • sdk: add HTTP timeout, HTTPS enforcement, and Python lockfile
  • sdk: update Rust SDK tests for Recipient type and API envelope

Fixed

  • ci: use self-hosted runner for k8s tag updates #298

New

  • tracking: add vanity tracking domain support (em.customer-domain)
  • newsletter microapp with signup forms, double opt-in, and blog integration #288

Fixed

  • worker: use Recreate strategy for hostNetwork deployment #296
  • landing: improve hero copy, feature links, text contrast, and registration UX #295
  • dns: update SPF record with correct SMTP node IPs and IPv6
  • landing: use url-encoded form body for newsletter signup
  • tests: register sidebar nav template in GDPR dashboard tests
  • suppress clippy too_many_arguments for query and task functions
  • apply cargo fmt formatting to newsletter microapp code

New

  • api: add broadcast endpoint for sending to contact lists
  • dashboard: add DNS provider auto-detection and Resend-style records table
  • dashboard: remodel domain setup and add premium animations #283

Fixed

  • dashboard: repair broken label contrast, standardize typography, and apply micro-interactions #286

New

  • dashboard: add dynamic sidebar with per-account feature visibility

Fixed

  • dashboard: always show setup features in sidebar to prevent chicken-and-egg
  • audit: propagate client IP to all audit log calls and configure TRUSTED_PROXIES
  • audit: propagate client IP to all audit log calls and configure TRUSTED_PROXIES #282
  • smtp: bind outbound SMTP connections to configured sending IP #281

New

  • domains: remove root domain SPF from verification requirements #273
  • cli: add euromail-cli and fix dashboard scopes form #269
  • cli: add interactive auth command for API key setup #270
  • dashboard: declutter registration form #268

Fixed

  • smtp: filter DNSBL meta-responses from public resolver rejection #280
  • security: DNS pinning for webhook SSRF and audit logging gaps #279
  • security: remediate CSO audit findings — TLS deps, CI pinning, K8s hardening #275
  • blog,docs: remove generated illustrations from blog posts and guides #267
  • sdk: align Domain and Email types with actual API response #272

New

  • blog,docs: add illustrations to GDPR blog post and documentation guides
  • blog: add illustrations to Gmail Postmaster Tools guide
  • dashboard: enhance deliverability insights with volume chart and engagement metrics
  • blog: add Google Postmaster Tools monitoring guide

Fixed

  • blog: add missing image files for Gmail Postmaster Tools guide
  • ci: prevent release builds from being cancelled mid-flight
  • dashboard: CSP blocks fonts and Alpine.js in production #265

New

  • docs: add guides for sub-accounts, email validation, DMARC, deliverability, and scheduled sending #264

New

  • design-system: implement DESIGN.md tokens across dashboard, landing and docs #261
  • sub-accounts: add multi-tenant sub-accounts with pooled quotas
  • sub-accounts: merge multi-tenant sub-accounts feature

Fixed

  • design-system: align dashboard logo color with warm gray-900 #263
  • design-system: complete warm palette and add tabular-nums #262
  • sdk: remove unused ApiKey import in Rust SDK sub_accounts module
  • sub-accounts: add toast notifications and polish dashboard UX
  • sub-accounts: correct HTTP status codes and test assertions for sub-account endpoints
  • sub-accounts: correct quota SUM type mismatch and register dashboard templates
  • sub-accounts: prevent quota race conditions and aggregate analytics auth gap
  • sub-accounts: resolve CI lint and compilation issues

New

  • isp-reputation: add Google Postmaster Tools and Microsoft SNDS integration #257 #258
  • tools: add templates-as-code CLI for managing email templates #257
  • dashboard: add interactive API playground #256
  • mcp: add MCP server for AI assistants #255
  • dashboard: add DNS copy-paste helper per provider #254

New

  • dashboard: add webhook delivery debugger and email HTML preview #253
  • api: add email validation service endpoint #252
  • dashboard: DMARC monitoring dashboard with per-domain stats and trend charts #251
  • dashboard: add GDPR compliance report PDF generation #250
  • deploy: automate migration init container and CI image tag updates #248
  • metrics: add async operations observability metrics #249
  • sdk: automate Rust SDK versioning and publishing with release-plz #239
  • api: add async operations tracking for batch send and GDPR erase #238
  • dashboard: add deliverability insights page with ISP breakdown and reputation scoring #237
  • sdk: add API keys, GDPR modules and fix signature inconsistencies #236
  • auth: add scoped API keys with fine-grained permissions #235
  • auth: unify API and Dashboard auth into shared middleware #234

Fixed

  • api: post-sprint hardening — enum types, self-hosted Chart.js, query limits, tests #247
  • tests: check error type instead of status code for scope gate test
  • api: correct operations SQL enum casting and scoped key test payloads
  • ci: correct trigger function name in operations migration and apply cargo fmt
  • dashboard: register admin domains template in Minijinja environment #246
  • sdk: allow dirty working directory in release-plz config #244
  • sdk: add missing permissions and config path for release-plz #243
  • sdk: install gh CLI on self-hosted runner for release-plz #242
  • sdk: install clippy component in CI toolchain #241
  • worker: use sorted set delay queue for webhook retry backoff #232
  • auth: return 503 Service Unavailable on database errors during authentication #233

New

  • dashboard: add live email event activity stream with SSE #231
  • dashboard: auto-generate API key on signup for instant onboarding #230
  • security: add comprehensive abuse prevention hardening #229
  • api: add scheduled email sending with delayed delivery #228
  • smtp: add configurable inbound SMTP rate limiting #227
  • dashboard: add DNS provider-specific setup wizard #226
  • landing: add og:image for light theme brand #223
  • landing: unify Zola templates with light theme #222
  • landing: redesign with Nordic light theme #221

Fixed

  • common: replace positional params with named structs #225

New

  • dashboard: hand-crafted light theme redesign #220

Fixed

  • k8s: bump image tags to v1.54.2 #219

Fixed

  • db: add ON DELETE SET NULL to emails.domain_id foreign key #218

New

  • domains: simplify DNS setup from 5 records to 3 records
  • dashboard: comprehensive admin panel improvements #215
  • sdk: support EUROMAIL_API_URL environment variable for base URL #212
  • dashboard: add subdomain sending recommendation for root domains #205 #206
  • dns: add SPF DNS lookup count validation per RFC 7208 #204 #208
  • smtp: add ARC (Authenticated Received Chain) support per RFC 8617 #200 #210
  • smtp: add DANE TLSA certificate verification for outbound delivery #211
  • smtp: add dedicated IP assignment per account #203 #209
  • smtp: add PTR/rDNS validation for sending IPs at startup #202 #207
  • ci: auto-cancel outdated CI and Release workflow runs #184
  • ci: auto-cancel outdated SDK workflow runs #183
  • sdk: add crates.io publish workflow and metadata for Rust SDK #180
  • sdk: upgrade Rust SDK to edition 2024 #181

Fixed

  • ci: use GHCR-mirrored service images to avoid Docker Hub rate limits
  • dashboard: add list filter to split iterator in setup_content template
  • dashboard: resolve domain detail 500, overview SQL errors, and enable tracking #214
  • landing: replace Python code example with Rust in hero tabs
  • landing: replace Python SDK with Rust SDK in landing page
  • landing: add main landmark and fix code tab contrast
  • landing: add cache-busting query strings to static assets
  • landing: improve Lighthouse accessibility and performance scores #213
  • sdk: add workflow file to TypeScript SDK CI trigger paths and re-trigger npm publish #199
  • sdk: add build dependencies and fix CI for Rust SDK crates.io publish #198
  • workspace: apply rustfmt formatting across source files #197
  • dashboard: guard analytics charts against empty daily_stats data #196
  • sdk: install Rust toolchain via dtolnay/rust-toolchain in CI #182

New

  • dashboard: add TOTP two-factor authentication for accounts #179
  • docs: add inbound email processing guide #178
  • api: add per-email GDPR export and erasure endpoints #175
  • dashboard: add template version history, diff, and rollback #176
  • smtp: add MTA-STS policy checking for outbound delivery #177
  • ci: add automated npm publish workflow for TypeScript SDK #174

Fixed

  • ci: use self-hosted runner for SDK TypeScript workflow
  • sdk: bump @euromail/sdk to 0.1.1 to trigger first npm publish
  • docs: remove unimplemented feature claims from site and documentation #173

New

  • sdks: add Go SDK and update TypeScript, Python, Rust SDKs with full API coverage #163
  • dashboard: add step-by-step domain DNS setup wizard with manual mode toggle #162
  • ci: rename runner label to kalle-works-arm64 for org-level access
  • inbound: inbound email for AI agents #142

Fixed

  • dashboard: rewrite domain setup wizard for Alpine.js CSP-safe build #169
  • dashboard: register setup_content.html template in load_templates #167
  • docker: make sccache conditional on AWS credentials #165
  • ci: apply rustfmt to resolve CI formatting failures #164
  • dashboard: improve inbound UI with CSP-safe Alpine, responsive tables, and date filters #159
  • dashboard: register inbound route templates and fix nav syntax error #151
  • dashboard: improve onboarding UX for unverified accounts #130

New

  • docs: add feature guide documentation pages with landing page integration #101
  • api: add A2A agent-card, agents.json, and expand AI discoverability
  • dashboard: add Alpine.js toast notification system with animations
  • dashboard: comprehensive UI/UX, accessibility, and security improvements #87
  • mcp: add llms.txt to landing page and MCP server for EuroMail API
  • sdks: complete TypeScript SDK and create Rust SDK with full API coverage
  • api,worker,smtp,common,dashboard: add production readiness infrastructure
  • billing: operationalize Stripe integration with webhook idempotency, trial support, and consolidated helpers

Fixed

  • ci: add Docker Hub login to avoid pull rate limits
  • landing,dashboard: improve anchor navigation, hero clarity, and signup form
  • dashboard: add focus indicators, aria-hidden, and autocomplete to templates
  • dashboard: exclude whitespace from special characters in password validation
  • dashboard: strengthen password policy, brute-force limits, and registration rate limiting
  • api,smtp: replace production unwraps with proper error handling #76
  • api,tests: resolve 9 failing integration tests across analytics, contact lists, and unsubscribe
  • common: suppress clippy too_many_arguments on DB upsert functions
  • remove accidentally committed sdks/rust/target and fix gitignore
  • tests: add missing AppConfig timeout fields to test initializers

New

  • api,dashboard,worker: add unsubscribe/contact lists and analytics dashboard

New

  • dashboard: add UI reviewer custom subagent for design consistency #65
  • dashboard: add Alpine.js collapse plugin for smooth DNS record animations #64

Fixed

  • dashboard: standardize design tokens across all templates #66
  • dashboard: replace tojson filter with conditional template logic

New

  • dashboard: simplify domain detail page with collapsible DNS records #63
  • api: add machine-readable error codes, rate limit headers, and documentation links #59
  • dx: add DNS auto-retry timer and sandbox mode for unverified domains #60
  • dashboard: add getting started onboarding checklist to overview #58
  • dashboard: improve accessibility and WCAG compliance #57
  • dashboard: add send test email button on domain detail page #56
  • dashboard: add domain detail page with list-to-detail navigation #55
  • dashboard: improve DNS verification UX with cleaner messages and auto-check on domain add
  • dashboard: show smart DNS merge suggestions for existing records
  • dashboard: animated domain add UX and real DNS verification

Fixed

  • dashboard: use idiomatic range contains in format_date filter
  • ci: migrate all jobs to self-hosted runner #61
  • dashboard: apply rustfmt to send test email handler
  • dashboard: apply rustfmt to domain delete handler
  • common: use tokens.contains() instead of iter().any() per clippy
  • dashboard: register admin templates in include_str loader
  • common: apply cargo fmt to dns merge suggestion functions
  • dashboard: register new domain templates in include_str loader
  • landing: add HSTS header, canonical URL, og:image, RSS autodiscovery, and deduplicate footer CSS

New

  • landing: add Zola-powered legal pages for Privacy Policy, Terms of Service, and DPA #51
  • landing: add auto-generated API documentation from OpenAPI spec #50
  • landing: add Zola-powered blog with GDPR article #48
  • dashboard: add admin panel for account and reputation management
  • reputation: add automated bad actor detection and account suspension

Fixed

  • api: resolve clippy warnings in gen_api_docs
  • apply cargo fmt to all workspace crates
  • landing: extend logo SVG viewBox to prevent shield clipping #49

New

  • dmarc: bind DMARC reports to customer accounts for multi-tenant filtering #43
  • worker: receive and display DMARC aggregate reports
  • dashboard: DKIM-sign system emails for improved deliverability
  • tests: add E2E tests for system email delivery
  • dashboard: deliver system emails via direct MX resolution
  • docker: unify Dockerfiles with cargo-chef, sccache and registry cache

Fixed

  • dashboard: use correct DKIM selector "euromail" for system emails
  • api: log version on startup for deployment verification
  • dashboard: apply rustfmt to system_email module
  • docker: bump Rust base image to 1.88-bookworm
  • docker: pin Rust base image to 1.85-bookworm for reproducible builds
  • deploy: update image tag to v1.12.1 for Flux deployment
  • ci: add workflow_dispatch trigger to Release workflow
  • docker: add OCI source label to link GHCR packages to repository

New

  • ci: add self-hosted ARM64 runner via ARC for native Docker builds
  • api: add migrate-only subcommand and improve K8s deployment reliability #21
  • dashboard: compile Tailwind CSS locally, add error feedback, cache invalidation, and E2E tests #17
  • dashboard: replace PNG logo with SVG and add favicon
  • infra: migrate DNS to Cloudflare and configure euromail.dev domain
  • landing: add euromail.dev root domain landing page
  • smtp: configurable EHLO hostname and DNSBL blocklist monitoring #20
  • smtp: DKIM SPKI format, per-domain bounce, and 5xx error handling #19

Fixed

  • smtp: extract DeliveryOptions struct to resolve clippy too_many_arguments
  • tests: add missing smtp_ehlo_hostname field and apply cargo fmt
  • dashboard: replace Alpine.js with CSP-safe build #18
  • dashboard: use POST for template update route and add CSP unsafe-inline #16
  • worker: strip angle brackets from Message-ID for RFC 5322 compliance #15

Fixed

  • dashboard: serve static assets in Docker and fix CSP for Tailwind CDN #14
  • dashboard: CSRF token on first visit and rustls CryptoProvider #13
  • dashboard: register all templates and add .dockerignore #12

New

  • deploy: migrate from UpCloud to Hetzner K3s with FluxCD GitOps
  • branding: add EuroMail logo to API and dashboard
  • docs,legal: add project documentation, privacy policy, ToS, and DPA
  • api: add LLM discoverability endpoints for AI coding assistants
  • billing,fbl: ARF complaint handling and Stripe billing integration
  • platform: system email delivery, OpenAPI completeness, error pages, env documentation
  • platform: production readiness — signup, password reset, CSRF, BCC delivery, attachments, tracking, audit log, GDPR, retention
  • security: batch suppression queries, billing-cycle quota keys, metrics auth, BCC privacy, template validation
  • worker: partition-pruned queries, bounce rate guard, retry backoff, TLS enforcement, quota reconciliation

Fixed

  • dashboard: add /health endpoint for K8s liveness and readiness probes
  • api: check domain duplicates before plan limits and fix test data
  • ci: detect semantic-release version via git tag comparison
  • ci: add missing workflow permissions and resolve parallel test panics
  • ci: eliminate duplicate workflow runs on PR branches
  • ci: resolve formatting, clippy, and security audit failures

New

  • security: CC/BCC suppression guards, deterministic IP hash, domain cache invalidation, custom headers
  • smtp: VERP return-path for reliable bounce-to-email matching
  • api: add domain cache and configurable DB pool for higher throughput
  • api: add hard bounce suppression, pagination with total counts, and test webhook delivery
  • api: add idempotency keys, account endpoint, bounce buffer limit, webhook auto-recovery
  • api: add OpenAPI documentation with Swagger UI
  • api: add SHA-256 API key cache and optimize auth + Docker setup
  • domains: generate DKIM keypairs at registration and implement DNS verification
  • euromail: implement complete transactional email service
  • helm: add production hardening with security contexts, probes, PDBs, and alerting
  • observability: add Prometheus metrics and OpenTelemetry OTLP tracing
  • ops: add composite DB indexes, worker readiness probe, and template render timeout
  • ops: add cross-process API key cache invalidation, worker port dedup, and dashboard error logging
  • quota: replace DB quota with Redis atomic counting and partition emails table
  • resilience: add graceful shutdown, DB pool hardening, and worker task draining
  • resilience: domain validation, quota batch sync, JWT pinning, and orphan prevention
  • security: add CSPRNG, SSRF protection, security headers, per-account rate limits, login brute-force guard
  • security: add email validation, body size limits, template fuel cap, and streaming webhook reads
  • security: bounce guard, partition pruning, config validation, template tenant isolation
  • security: bounce tenant isolation, webhook HMAC timestamps, SDK contract fixes
  • security: dashboard tenant isolation, input validation, SDK response alignment
  • smtp: add stream MAXLEN, multi-IP pool, warm-up schedule, circuit breakers
  • smtp: enforce STARTTLS, recover stuck emails, harden webhooks and session cookies
  • worker: add webhook retry with exponential backoff and HTML-to-plaintext fallback

Fixed

  • data-integrity: orphan cleanup, bounce scoping, DKIM generation, quota reset, SDK fixes