carlos / platform Public

Managed email identities: many senders per app Issue open

The tenant-email primitive (2026-08-19 spec) is app-scoped: an app declares that it sends, and gets one identity on its own host. A multi-tenant app — one whose own customers bring sending domains — needs MANY identities under one app, each isolated and counted per end-customer. That granularity is missing.

Proposal, spec'd in docs/superpowers/specs/2026-08-28-managed-email-identities-design.md on branch managed-email-identities:

  • A tenant sub-label on the identity record → SES tenant carlos-<sqid>-<label> plus a per-label configuration set, so reputation and bounce/complaint counts split per end-customer. Label-less records and walks are byte-identical to today (pinned as an exact call trace).
  • Four app-API doors patterned on the managed-domains resource: capability folded into the list answer (no discovery endpoint), a cheap per-identity door so a poller never pays for the collection, create claim-first, delete referrer-counted. records[] is structured and verbatim-renderable — DKIM CNAMEs, region-qualified MAIL FROM MX + SPF TXT, and a DMARC TXT marked recommended, excluded from the verification verdict.
  • The axis re-widening tenant-email §11 called additive: dkim_status and mail_from_status carried alongside the collapsed Status, so a consumer can say which record is still wrong.
  • An estate-grade sending credential (operator-granted only, never self-serve): the enumerated IAM policy overflows its 2048-char ceiling in the low dozens of identities, so estate apps take a broad sending credential and the From-address boundary moves app-side, with per-label counting on watch behind it. Tradeoff stated plainly in spec §5.

Stage 1 is on the branch (record + provisioner half): the label with one-way adoption (ErrTenantLabelFixed), the sub-tenant walks with the identity-and-config-set associations that SES's X-SES-TENANT precondition demands, the axis statuses, and a tenant-aware fix to VerifyDomain's #272 reconciliation — without which the first poll would silently rebind a sub-tenant identity back to the account set. 151 tests green, each new fence mutation-checked.

Stage 2 (the doors, CLI verbs, docs/site/cli.md) is held for this discussion. Also flagged in the spec addendum: stage 2's Delete must tear down the sub-tenant and its config set, since stage 1 accumulates one per label with nothing reaping them.

Ready to review stage 1 and put it up as a PR, but I cannot find the branch. Where I looked:

  • github.com/carlosframework/platform: the only email-ish branches are email and email-followups, and both are fully merged — git log origin/main.. is empty for each. No managed-email-identities.
  • amadan carlos/platform: 21 branches, email among them, also fully contained in main. No managed-email-identities.
  • No commit in any reachable ref adds ErrTenantLabelFixed, and nothing on this machine matches *managed-email-identities*, so the spec is not here either.

So stage 1 exists somewhere I cannot reach — a local checkout, or a namespace this session cannot see. Push the branch to amadan (git push amadan managed-email-identities) or to origin and say which, and I will pick it up.

What I would review it against, so there are no surprises:

  • the byte-identical claim for label-less records and walks — the write-up says it is pinned as an exact call trace, and that is the fence the whole additive story rests on;
  • the one-way adoption (ErrTenantLabelFixed) — specifically what happens to a record that already carries a label when a caller omits one, which is where an "additive" field usually turns out to be a silent clear;
  • the #272 reconciliation fix, since without it the first poll rebinds a sub-tenant identity back to the account set — that one is a live-data bug, not a design question;
  • dkim_status / mail_from_status carried alongside the collapsed Status, against the wire-shape rule this repo has paid for: anything written before the change must still parse.

Two things I am NOT reviewing, because they are Paul's rather than mine: §5's estate-grade sending credential, which moves the From-address boundary app-side once the enumerated IAM policy overflows 2048 characters, and whether stage 2 gets built at all.

One thing worth pulling forward from the addendum, since it is accumulating while this waits: stage 1 creates a sub-tenant and a configuration set per label with nothing reaping them, and the teardown lives in stage 2's Delete. If stage 1 lands first, that is an unbounded resource with no owner — worth either a reaping duty in stage 1 or an explicit note that stage 1 does not go near production until stage 2 exists.

Still not reachable, five days on. Re-checked just now against the amadan hub, which is where this repo's branches live: 31 heads, and the email-shaped ones are email (fully contained in main) and custom-domains-managed. No managed-email-identities, and nothing in any reachable ref adds ErrTenantLabelFixed.

So the review offer stands exactly as written above and nothing has changed on my side. Push the branch — git push origin managed-email-identities reaches the amadan hub now that this repo's origin is amadan.net — and I will pick it up against the four things listed in the previous reply.

The one item that gets worse with time rather than staying still is the addendum's: stage 1 creates an SES sub-tenant and a configuration set per label with nothing reaping them, and the teardown lives in stage 2's Delete. If stage 1 lands before stage 2 exists, that is an unbounded AWS resource with no owner. Either a reaping duty moves into stage 1, or it is written down that stage 1 does not go near production alone.

Leaving open — this is blocked on a branch only you can push, not on a decision.

Triage, 2026-09-05. Re-checked, and the position is unchanged: blocked on a branch only you can push.

Swept every ref the hub has. 40 remote heads on carlos/platform; the email-shaped ones are email (fully contained in main), email-followups, custom-domains-managed and worktree-inbound-email-spec. No managed-email-identities. git log --all -S'ErrTenantLabelFixed' returns nothing, so stage 1 is in no reachable ref, and no spec file matching *managed-email-identities* is on this machine either.

That is now eight days from the first ask. git push origin managed-email-identities reaches the hub — this repo's origin is amadan.net — and the review offer stands exactly as posted: the byte-identical claim for label-less records and walks, the one-way adoption behaviour when a caller omits a label that is already set, the #272 reconciliation fix (a live-data bug rather than a design question), and dkim_status/mail_from_status against the wire-shape rule.

The one thing that is worse for waiting, restated because it is the part with a cost attached: stage 1 creates an SES sub-tenant and a configuration set per label, and the teardown lives in stage 2's Delete. If stage 1 ever lands first, that is an unbounded AWS resource with no owner. Two acceptable outcomes — a reaping duty moves into stage 1, or it is written down that stage 1 does not go near production alone. Neither costs anything to decide now, and both stop being free once it is deployed.

Not closing. This is not stale, it is waiting on one push.