fix(ses): let self-hosters use SNS emulators and the log-only switch
KM
3d ago+97+97+01234567890123456789-1-1-0123456789
Follow-up to #466 for self-hosted installs. Cloud behaviour is unchanged.
What changed
- Emulator subscriptions confirm again. #466 always rejected a
SubscriptionConfirmationwhoseSubscribeURLisn't an AWS SNS host, even inlog-onlymode and in development. SNS emulators send their own URL (local-ses-snssendshttp://localhost:<port>), so with an emulator the subscription never confirmed and no delivery events arrived. This also broke the local dev setup indocker/dev. A non-AWSSubscribeURLis now accepted only when bothAWS_SNS_ENDPOINTis set and signature enforcement is off (NODE_ENV=developmentorSNS_SIGNATURE_VERIFICATION=log-only). Real AWS setups, including ones usingAWS_SNS_ENDPOINTfor a VPC endpoint, keep the strict AWS-host check. The fetch still refuses redirects. SNS_SIGNATURE_VERIFICATIONreaches Docker Compose installs.docker/prod/compose.ymllists every variable explicitly and didn't pass this one, so the escape hatch from #466 did nothing for Compose users. It now defaults toenforce.- Documented in
.env.selfhost.exampleand the self-hosting overview (Step 3), including what to look for in the logs if statuses stop moving past Sent.
Trade-off
In the emulator mode (custom endpoint + log-only), someone who knows the topic ARN could make the server send one GET to a URL of their choosing. That mode is explicitly opted into and already accepts unsigned events, so this is no wider than what the operator chose.
How I checked it
- New route tests: the emulator
SubscribeURLis confirmed withAWS_SNS_ENDPOINT+log-only; rejected when signatures are enforced even withAWS_SNS_ENDPOINTset; rejected inlog-onlywithoutAWS_SNS_ENDPOINT. vitest -c vitest.api.config.ts src/app/api/ses_callback: 14/14.vitest -c vitest.unit.config.ts src/server/aws: 39/39.pnpm --filter=web typecheck: clean.local-ses-sns'sSubscribeURLformat checked in usesend/local-ses-snssrc/index.ts.
Summary by cubic
Fixes self-hosted SNS emulator setups so subscription confirmations work again, and makes the log-only signature switch effective for Docker Compose installs. Cloud behavior is unchanged.
- #466 always rejected a
SubscriptionConfirmationwhoseSubscribeURLisn't on an AWS SNS host, even when signature verification was off, so SNS emulators could never confirm and delivery events never arrived. - A non-AWS
SubscribeURLis now accepted only when bothAWS_SNS_ENDPOINTis set and signature enforcement is off (development orSNS_SIGNATURE_VERIFICATION=log-only); real AWS setups keep the strict AWS-host check, and fetch still refuses redirects. docker/prod/compose.ymlnow passesSNS_SIGNATURE_VERIFICATION(defaultenforce) — it previously wasn't listed, so the switch had no effect for Compose users.- Documented in
.env.selfhost.exampleand the self-hosting overview (Step 3), including what to look for in the logs if statuses stop moving pastSent. - Trade-off: with a custom endpoint plus
log-only, anyone who knows the topic ARN could make the server send one GET to a URL of their choosing; that mode already accepts unsigned events and requires explicit opt-in.
Summary by CodeRabbit
- New Features
- Self-hosted deployments can configure SNS signature verification. Verification is enforced by default;
log-onlyaccepts and logs messages with invalid signatures. - When using an SNS emulator such as LocalStack, a custom endpoint can allow subscription confirmation URLs during
log-onlymode.
- Self-hosted deployments can configure SNS signature verification. Verification is enforced by default;
- Documentation
- Self-hosting guidance now explains signature verification, how to investigate rejected SNS messages, and the settings relevant to emulator use.