Carry attachment references inside the stored email body blob, resolve bytes on workers, and encode attachments for Gmail API and SMTP sends without changing the Kafka send-email contract.
Persist outbound warmup Message-IDs so later warmup replies can find thread parents.
Normalize reply headers and re-check partner health during recipient selection before using a mailbox as a warmup recipient.
Add star-rate settings to warmup content controls and include star actions in generated engagement plans.
Execute Gmail stars via STARRED labels while keeping IMAP behavior a no-op to avoid duplicate flagging.
Brings in PR #15 (email warmup process 4) plus its preceding commits:
customer-defined warmup routing on premium pool, free-trial warmup +
1 inbox for 14 days, customer webhook subscriptions with HMAC signing
+ retry, bumped default API rate limits to 100 req/s with flat per-
user/per-plan caps, plus dev-fixture additions.
One real conflict: internal/client/smtpimap/imap/client.go added
distinct imports on each side (this branch added 'net' for the
*net.TCPAddr BindIP field; main added 'sync' for a Mutex). Kept both.
Everything else auto-merged additively:
cmd/backend/main.go - imports + handler fields + DI lines
internal/api/handler/handler.go - new fields next to existing ones
internal/api/routes.go - new route group next to existing ones
Full build + test suite pass (no regressions).
SMTP and IMAP clients now expose a BindIP *net.TCPAddr field. When set,
outbound TCP binds to that IP; otherwise the netbind helper falls back to
WORKER_BIND_IP env var, then default route.
IMAP switched from raw tls.Dial to netbind.TLSDialer so the source IP is
configurable; behavior identical when BindIP is nil.
No call-site changes required: backwards compatible.
Tiny helper that builds a *net.Dialer (and tls.Dialer wrapper) with an
optional LocalAddr. Used by the SMTP and IMAP clients so a worker on a
multi-IP box can bind outbound TCP to a specific source IP.
Bind IP comes from an explicit *net.TCPAddr on the client, or falls back
to the WORKER_BIND_IP env var, or finally to the OS default route. The
env-var path is cached once via sync.Once.
4 tests cover explicit bind, nil-passthrough, tls.Dialer composition,
and timeout sanity.
mark_read, mark_important, remove_from_spam, and move_to_warmbly now run
on outlook and custom smtp/imap mailboxes, not just gmail. routes by
provider in the worker handler. carries source mailbox UIDValidity on
the action so the worker can SELECT the right folder before mutating.
Disable the linters that fire on legacy code without flagging real
bugs: `unused` (orphan repos kept for future feature flags),
`unconvert` (defensive type conversions), `gosimple` (style
suggestions in code we don't want to touch).
govet: disable `shadow` (idiomatic `err :=` re-decls in transaction
patterns) and `nilness` (legitimate defensive nil checks that look
tautological to the analyzer).
Ran `gofmt -w internal/ cmd/` — every Go file now passes
gofmt -l with no output.
Kept: govet, staticcheck, ineffassign, typecheck, bodyclose, noctx,
sqlclosecheck, gofmt, goimports, misspell — the real-bug checks.
- cipher: use the already-available ctx parameter for DynamoDB Put
- goog: use context.Background for OAuth token refresh callback since
it runs asynchronously outside any request lifecycle