Skip to content

v0.25.0

Choose a tag to compare

@heliocosta-dev heliocosta-dev released this 27 Oct 14:05
· 40 commits to main since this release
382dac8

MillionSend v0.25.0 changes how the regional breaker works: it trips at SES's 0.1% review line and holds only the teams drawing the complaints (#207). On Cloud, several small Free accounts can no longer add up against the shared sending reputation (#205, #206). The content monitor makes fewer false holds (#204), and a few layouts are fixed (#208). There are three database migrations; see "Upgrading a self-hosted instance".

Breaker and deliverability

These apply to self-hosted instances too.

  • Trips at SES's review line (#207). A complaint trip now starts at 0.1% (it was 0.4%). It resumes only once the rate is back under 0.08%, so a rate that hovers at the line can't flap. Bounce trips are unchanged.
  • Holds the teams drawing the complaints (#207). A complaint trip holds the broadcasts of teams whose own 30-day complaint rate is 0.1% or higher, not the whole region. Manual holds and bounce trips still hold the whole region. The console shows such a region as "paused · N teams", with their names on hover.
  • Earlier signal (#207).
    • The operator's complaint alert fires from 0.08% (it was 0.3%).
    • It names teams by the mail they sent in the last 30 days, which is how SES files complaints.
    • A change in SES's account enforcement status mails the operator too.
  • Feedback on unknown mail (#207). Some SES complaints and bounces are about mail with no email row: a deleted team's, or a row past retention. These are now kept for 35 days and counted in the operator mails.
  • Mailbox simulator (#206). Bounces and complaints from the SES mailbox simulator still suppress the address and fire webhooks. They no longer count toward a team's guardrail rates, the breaker or the Free fuse. A team's own broadcast stats still show them.

The Free plan on Cloud

  • One pool per person (#206).
    • The Free teams one person creates share one daily cap and one bounce and complaint record.
    • Deleting a team doesn't erase the record.
    • Free complaints count from the first email.
  • Team creation (#206). A Free account may have created at most 3 teams in the last 30 days, deleted ones included.
  • Burst allowance (#206). Free mail to providers that report complaints goes out 10 recipients at once, then 10 an hour. That covers Outlook, Yahoo and most other providers; Gmail, Apple and T-Online don't report complaints. Account mail and the onboarding email never wait.
  • Review hold and fuse (#207).
    • During a complaint trip, Free teams whose mail drew a complaint are held for review.
    • Three complaints on Free teams' mail within 7 days trip a platform-wide fuse. It holds Free mail to complaint-reporting providers until an operator releases it from Trust & safety.

Sign-up (#205)

  • Turnstile now also guards Google and GitHub sign-in and the product-updates form, wherever Turnstile keys are set. Without keys, nothing changes.
  • The product-updates form accepts posts only from its own page, at 20 an hour per instance (it was 200).
  • Cloud: one network, an IPv4 address or an IPv6 /64, can open at most 3 accounts a day.
  • Setup wizard: the next @millionsend/setup release prints the subscribe link instead of posting the address. On an instance with Turnstile keys, 0.9.2 and earlier are refused and fall back to printing it.

Content monitor (#204)

  • Loopback addresses (localhost, 127.0.0.0/8, ::1, 0.0.0.0) are never treated as a new destination. A verdict that rests on one waits for a person.
  • A product's billing page on its own app platform is spared like its sign-in page. It is not spared when the mail carries a payment instrument or a Pix code.
  • Naming MillionSend as your sending provider, as setup tests do, is no longer read as impersonating it.
  • Mail only to the team's own people never waits out the monitor's burst window. It still parks like any other mail.
  • The monitor flag opens only on an abuse verdict from the last 7 days, and stays open until the risk falls back under the line.

Interface (#208)

  • Buttons keep their label centred whenever a layout widens them. "Accept invitation" on the invite page and an inbox thread's "Newer messages" are as wide as their label again.
  • Console team lists (Teams, Trust & safety, limit requests) end a long team name in an ellipsis, with the full name on hover, instead of scrolling sideways.
  • Contact properties: the footer counts the in-use properties it lists.

Dependencies and the tracking edge

  • Production dependencies (#199):
    • smtp-server 3.21.0, nodemailer 10.2.0, better-auth 1.7.12, bedrock-sdk 0.36.0, plus AWS SDK patches.
    • A differential run of the SMTP relay, the MX, mail parsing and MIME building found no change in behaviour.
  • Tracking edge (#200):
    • The edge's Caddy image is pinned by digest (2.11.7).
    • The edge serves HTTP/1.1 only until Caddy ships with Go's HTTP/2 fixes.
    • Follow the upgrade steps in the tracking edge README on that box.

Upgrading a self-hosted instance

  • Upgrade as usual: docker compose pull and docker compose up -d, or on Kubernetes upgrade with --version 0.25.0. The MX image changes too (smtp-server 3.21.0), so update it as well.
  • Three database migrations run on start:
    • 0063 adds teams.pool_owner_id and the free_pool_usage table.
    • 0064 adds the free_pool_bursts table and a nullable emails.free_burst_at.
    • 0065 adds:
      • the breaker scope, the SES enforcement status and the Free fuse columns;
      • a fuse park reason;
      • the ses_unmatched_events table;
      • a partial index on email_events.
  • Plan for the email_events index. It is built without CONCURRENTLY, so writes to email_events (SES events, opens and clicks) wait while it builds. On a large table, upgrade at a quiet moment.
  • Cloud-only features. The Free pool, the burst allowance, the Free fuse and the per-network sign-up cap only run on Cloud. On a self-hosted instance their tables stay empty.
  • No new environment variables.
  • The breaker's new lines apply to self-hosted instances. A complaint pause starts at 0.1% and holds only the teams at or above that line. A self-hosted instance never review-holds anyone.
  • Rolling back to v0.24.0 needs no extra step: the migrations only add tables, columns and an enum value that v0.24.0 ignores. A region paused for some teams then reads as paused for every team, until v0.24.0's next breaker run resumes it below 0.4%.