Guardrails
Observation is the default. Enforcement is opt-in. Both are here.
The Guard
SquatchMail.Guard.check/1 evaluates every send against two things:
- The Do-Not-Disturb Registry — is this recipient suppressed?
- The complaint-rate circuit breaker — is your account complaining its way toward an SES suspension?
The breaker trips at a 0.1% complaint rate by default, because that’s SES’s own account-suspension threshold. SquatchMail pauses you before AWS does — think of it as a friend grabbing your backpack strap at the cliff edge.
A minimum-volume floor keeps the math honest: five sends and one complaint is not a 20% complaint rate, it’s a Tuesday. Until you clear min_volume, the breaker stays calm.
config :squatch_mail,
guard: [
complaint_rate_threshold: 0.001,
auto_pause: true,
min_volume: 1_000
]
Watchtower: when observing isn’t enough
Most hosts only need SquatchMail.Capture’s pure observation — footprints get recorded, nothing about your send path changes. But recording that you emailed a suppressed address is different from not emailing them.
SquatchMail.Adapters.Watchtower is an opt-in Swoosh adapter that wraps your real adapter and actually blocks the send when:
- the recipient is on the Do-Not-Disturb Registry, or
- the account is auto-paused by the circuit breaker.
config :my_app, MyApp.Mailer,
adapter: SquatchMail.Adapters.Watchtower,
inner_adapter: Swoosh.Adapters.AmazonSES
# ...your existing SES config
Blocked sends come back as tagged errors — “The trail went cold: recipient suppressed” — so your app can decide whether that’s a shrug or a page.
Why enforcement is opt-in
SquatchMail’s core promise is zero changes to your send path. Telemetry capture can’t break your mail because it isn’t in the way. Watchtower is in the way, deliberately — so it’s a decision you make explicitly, not a side effect of installing a dashboard.
Observe first. Enforce when you’ve seen enough footprints to trust the tracker.
The Pruner
Guardrails for your disk, too. SquatchMail.Pruner runs on a timer (six hours by default) and deletes:
- emails and their footprints older than
retention_days - webhook audit logs older than a fixed 30 days
Pack it in, pack it out.