Skip to content

Per-tenant SES identity automation: provision + poll + auto-attest - #28

Merged
Aakash1337 merged 1 commit into
mainfrom
claude/relay-identity-automation
Jul 13, 2026
Merged

Per-tenant SES identity automation: provision + poll + auto-attest#28
Aakash1337 merged 1 commit into
mainfrom
claude/relay-identity-automation

Conversation

@Aakash1337

Copy link
Copy Markdown
Owner

Gap-filler 3 of 3: Phase 4 gave every tenant a sender_from_address and a manual operator attest gating real sends. This closes the loop with the provider so onboarding a tenant's sending domain needs no SES console work.

What it does

  • src/relay/senders/identity.py — a thin SESv2 identity seam following the sender registry's construction rules: deferred boto3 import, injectable client, provider gate (IdentityUnavailable unless RELAY_SENDER_PROVIDER=ses). identity_for() maps a tenant address to the identity by scope: domain (default — DKIM CNAMEs, one DNS setup covers every mailbox) or address (SES mails a confirmation link to the mailbox).
  • POST /internal/tenants/{id}/sender-identity/provision (admin) — creates the identity at SES, idempotently (AlreadyExists is fine), audits, and returns the exact DKIM CNAME records the tenant must publish.
  • POST /internal/tenants/{id}/sender-identity/sync (admin) — polls SES; the moment VerifiedForSendingStatus is true, sender_identity_verified flips automatically, audited as ses:identity-sync, exactly once. Deliberate asymmetry: the attest never unflips on a transient provider blip — revoking eligibility stays an operator decision. The manual attest endpoint remains as the escape hatch for non-SES providers.

What stays with the tenant, on purpose: publishing the DNS records (their zone). What stays with AWS: the verification decision. Nothing here can send — verification only satisfies one of the seventeen send checks.

Tests + docs

  • 8 new tests (branch suite 331 → 339, all green) against a FakeIdentityClient injected at the production seam — no network, no boto3: provider gate 409, missing-address 409, provision returns the DKIM records and audits once, sync flips the attest exactly once (idempotent re-sync), no-unflip on provider blip, address scope, and admin-token enforcement on both endpoints.
  • docs/prototype-status.md item 2 updated: per-tenant domain/mailbox verification is now automated; what remains manual is DNS publication and the production-access request (runbook shipped in PR deploy/aws: SES infrastructure as code + production-access runbook #27).

Pairs with #27 (which provisions the platform's sending infrastructure; this PR handles tenants' identities at runtime). Independent diffs — either can merge first.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ASAVj4XgJCH3UcHkZZaYzM


Generated by Claude Code

New senders/identity.py seam (deferred boto3, injectable client — the
sender registry's construction rules) and two admin endpoints:
.../sender-identity/provision creates the tenant's SES identity
idempotently (domain scope by default, address scope optional) and
returns the DKIM CNAMEs the tenant must publish; .../sync polls SES
and flips sender_identity_verified automatically — audited as
ses:identity-sync — the moment AWS confirms. The attest never unflips
on a transient provider blip; revocation stays an operator decision,
and the manual attest endpoint remains the non-SES escape hatch.

8 new tests against a fake client behind the production seam (provider
gate, missing-address 409, DNS records + audit, flip-once idempotency,
no-unflip, address scope, admin auth). Suite 331 -> 339 on this branch.
prototype-status item 2 updated: per-tenant verification is now
automated; DNS publication stays with the tenant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASAVj4XgJCH3UcHkZZaYzM
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Aakash1337, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e00fad07-d833-4af7-b51a-6ff03b075e64

📥 Commits

Reviewing files that changed from the base of the PR and between 23460d0 and ae6ce34.

📒 Files selected for processing (5)
  • docs/prototype-status.md
  • src/relay/api/routes.py
  • src/relay/api/schemas.py
  • src/relay/senders/identity.py
  • tests/test_sender_identity.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/relay-identity-automation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Aakash1337
Aakash1337 marked this pull request as ready for review July 13, 2026 18:07
@Aakash1337
Aakash1337 merged commit c241454 into main Jul 13, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants