Every Dekimu app sends email — magic links, waitlist confirmations, invoice reminders, DSAR acknowledgements, deletion-confirmation cascades. Until last week, every one of them was wired straight to a single email provider. As of today, every place we send email across the ecosystem goes through one provider-neutral layer. The current provider stays the active path, hosted in the EU. A swap-ready alternative sits dormant alongside it for the day we want to pull the switch.
Provider lock-in is invisible until the day it isn't. A pricing change, a region outage, a regulatory shift in where transactional email can land — any of those flips email infrastructure from 'works fine' to 'critical migration' overnight. We wanted that switch to be a one-line config change, not a multi-file refactor. The abstraction is small on purpose: a single sender function, a normalized response shape, no provider details leaking past the boundary. Boring is the goal.
Every Dekimu app processes data on EU customers. That's not a marketing claim — it's where the legal exposure lives. Our active provider has hosted in the EU for months; we confirmed the region this week and locked it in. The dormant alternative is the next-step option if we want an even tighter EU-hosted path, end-to-end. Either way, transactional email never leaves the EU. That's the floor we're not willing to negotiate.
The right time to build a portable infrastructure layer is before you need to be portable. Otherwise you're refactoring under fire.
Two things, immediately. First, a per-app override — InvoiceUp could route through one provider while miniterms uses another, if regulatory or deliverability data ever justifies it. Second, a clean test surface: every test now asserts against the normalized response shape, not against vendor-specific internals that change across versions. The cleanup that came with this — replacing a stack of provider-specific setup notes with a single shared knowledge note — was its own quiet win.
Stage A is shipped across every app plus the starter template. Stage B (provider cutover) doesn't fire until we see one of three triggers we wrote down up front. Until then, the dormant adapter is a feature flag, not a bet.