Trust & Email Sending Practices
Last updated: 2026-05-16 · Maintained for AWS SES reviewers, security auditors, and compliance teams.
This page consolidates the operational facts about how MiGo Piggy sends email. It is intended as a single-page reference for AWS Support reviewers evaluating our SES production access request, as well as for any third party assessing our sender hygiene.
1. Product status
- Product: MiGo Piggy — a local business directory for Chinese-speaking residents of Sydney, Australia. iOS-first.
- Stage: Closed beta / TestFlight. Preparing for App Store public launch.
- Operator: Gao Kang, sole proprietor, registered in NSW, Australia.
- Postal address: 55 Dalmeny Avenue, Rosebery NSW 2018, Australia.
- Public website: https://migopiggy.com
- Privacy policy: https://migopiggy.com/privacy (compliant with the Australian Privacy Act 1988 and 13 APPs).
- Support page: https://migopiggy.com/support (5 business day SLA).
2. What email is used for
Email is used only for transactional, user-initiated messages tied to a specific account action. There is no marketing email, no newsletter, no announcement broadcast, no campaign feature, no CSV upload, and no arbitrary-text sending interface anywhere in the product.
| Template | Trigger | Recipient |
|---|---|---|
email_otp | User taps "Send code" in iOS app or web | The exact address the user just typed |
The codebase contains one and only one entry point to SES SendEmail (apps/accounts/services/email.py). It enforces a compile-time template allowlist (ALLOWED_TEMPLATES = {'email_otp'}). Any future template additions (merchant claim approval, account security notice) will go through the same allowlist; no caller can pass a free-form subject or body.
3. Non-use commitments
- No marketing email, newsletter, or promotional content.
- No mailing list. Recipient lists are never imported, scraped, purchased, or stored in aggregate.
- No CSV / bulk upload feature exists in any admin or API surface.
- No "broadcast to all users" capability.
- No third-party email service shares — we send through AWS SES (or a console fallback for staging) and nothing else.
- No tracking pixels, no remote images, no link-click tracking in any email body.
4. Recipient source & consent
Each recipient address comes from a single user action: the user opens the iOS app or our website, types their own email into a labelled input field, and taps "Send verification code." This is the only path that produces a recipient address. The user is shown, before tapping, an explicit notice:
"Enter your email to receive a one-time verification code. We will only use this email for login, account security, and service-related notifications. We do not send marketing emails."
There are no pre-checked boxes, no opt-out / unsubscribe required (the message is a single-purpose security code under RFC 8058, which exempts transactional security messages), and no cookies or trackers gate this flow.
5. Anti-abuse controls (live in production today)
The OTP send endpoint is hardened at multiple layers before reaching SES. All limits are enforced atomically in Redis and tested under concurrency.
| Layer | Limit | Source |
|---|---|---|
| Per-email cooldown | 1 request per 60 seconds (atomic SETNX) | OTP_RATE_LIMIT_TTL=60 |
| Per-email 24h cap | 10 sends per address per day | OTP_PER_ACCOUNT_DAILY_LIMIT=10 |
| Per-IP cap | 3 OTP send requests per minute (DRF AnonRateThrottle) | DEFAULT_THROTTLE_RATES['otp_send'] |
| Global daily cap (email channel) | 50,000 sends per 24h, account-wide circuit breaker | OTP_GLOBAL_DAILY_LIMITS['email'] |
| Disposable email blocklist | 50+ throwaway domains (mailinator, guerrillamail, 10minutemail, tempmail, yopmail, ...) — rejected before any SES call | apps/accounts/services/email_validation.py |
| Typo domain correction | Common typos (gmial.com, qq.con, hotmial.com, icould.com, ...) intercepted with a "did you mean ___?" response — no SES call wasted | Same module |
| Pre-send suppression check | Local EmailSuppression table queried before every SendEmail; known-bad addresses silently skip the API call | EmailSuppression model |
| OTP verify attempts | 5 wrong codes invalidates the OTP; failed verifies do not extend the OTP's TTL (no infinite-window attack) | OTP_MAX_ATTEMPTS=5 |
| User enumeration | otp_send returns the same response whether the address exists in our user table or not | apps/accounts/views.py |
6. Email service provider
During MVP launch we send through Resend (region us-east-1) — a transactional-only email service that runs on AWS SES infrastructure under the hood. Post-launch we will migrate primary sending to our own AWS SES production access (currently in sandbox).
Both providers feed the same local EmailSuppression table; bounce/complaint hygiene is provider-agnostic.
7. Bounce & complaint feedback loop
End-to-end pipeline, verified in production:
Resend send (or SES SendEmail when switched back) → provider webhook (Svix HMAC-SHA256 for Resend; SNS+cert for SES) →https://api.migopiggy.com/api/v1/auth/resend-events/(or/ses-events/) → signature verification + replay window check (±5 min Resend, ±1 hour SES) → PostgreSQLEmailSuppressiontable (permanent, indexed on lowercase email) → All future sends for that address silently skipped before reaching the provider
- Permanent bounce → suppressed permanently; further sends are short-circuited locally.
- Complaint → suppressed permanently and the corresponding user account flagged for manual review.
- Transient bounce / delivery delayed → logged only; no over-reaction to mailbox-full or temporary issues.
8. Domain authentication
- Sender:
noreply@migopiggy.com - DKIM (Resend, currently active): 1024-bit RSA, selector
resendatresend._domainkey.migopiggy.com. - DKIM (AWS SES, ready for failback): 2048-bit RSA,
d=migopiggy.com, 3 CNAME records published. - SPF (apex):
v=spf1 include:amazonses.com include:_spf.mx.cloudflare.net ~all— covers both providers (Resend uses AWS SES infrastructure). - Return-Path subdomains:
send.migopiggy.com(Resend) andbounce.migopiggy.com(SES) — both isolated from apex, both have their own SPF+MX. - DMARC:
v=DMARC1; p=none; rua=mailto:dmarc-reports@migopiggy.com; fo=1; adkim=r; aspf=r. Passes on both SPF and DKIM legs. Tightening top=quarantine4 weeks post-launch, thenp=reject. - DNSSEC: Enabled (Cloudflare), DS record propagated to the
.comTLD. - CAA: 5 records limiting issuance to Let's Encrypt, Amazon, Google Trust Services.
9. Sample OTP email
Below is the verbatim production template (bilingual EN + Simplified Chinese, text/plain + text/html multipart). No tracking, no remote images, no marketing copy.
Subject: MiGo Piggy verification code / 验证码 You received this email because someone requested a MiGo Piggy login verification code using this email address. Your verification code is: 482913 This code expires in 5 minutes. If you did not request this code, you can safely ignore this email. Your account remains secure and no action is required. If you receive these messages repeatedly without requesting them, please contact us at support@migopiggy.com so we can investigate. -- MiGo Piggy Team noreply@migopiggy.com https://migopiggy.com Support: support@migopiggy.com
10. Contact & RFC 2142 mailboxes
All standard administrative mailboxes are deliverable and monitored (via Cloudflare Email Routing):
- abuse@migopiggy.com — abuse reports
- postmaster@migopiggy.com — mail-server complaints
- support@migopiggy.com — general support, 5 business day SLA
- dmarc-reports@migopiggy.com — DMARC aggregate / forensic reports
- hello@migopiggy.com — general contact
11. AWS resources
- Account: 848504403908
- Region: ap-southeast-2 (Sydney)
- Configuration Set:
yellowpage-otp - SNS Topic:
arn:aws:sns:ap-southeast-2:848504403908:yellowpage-ses-events - Health endpoint: https://api.migopiggy.com/api/v1/health/ (publicly reachable, returns
db/cache/version)
12. Reporting abuse
If you receive an unsolicited MiGo Piggy email, suspect impersonation, or want to report a sender reputation issue, please email abuse@migopiggy.com or postmaster@migopiggy.com. Include full message headers if possible. We respond within 5 Australian business days; urgent reports prefixed [URGENT] are prioritised.