Replace per-project notify host provisioning (7-9 API calls + DNS + async
Resend verification) with a shared platform host for all *.threesix.ai projects.
Under the new model:
- CreateProjectNotify: 3 calls only (account + send key + host grant)
- No per-project Resend domain, DNS records, or async verification
- All *.threesix.ai projects share `threesix.ai` as the platform host
- Custom domains still get a dedicated host via ReprovisionNotifyHost
Changes:
- domain/notify.go: slim NotifyCredentials (no Host/From/ResendDomainID);
add NotifyHostCredentials for reprovision return path
- port/notify_provisioner.go: update interface signatures and docs
- adapter/notify/provisioner.go: rewrite CreateProjectNotify (3 steps);
rewrite DeleteProjectNotify (account-only vs full cleanup)
- adapter/notify/provisioner_reprovision.go: return *NotifyHostCredentials
- adapter/notify/provisioner_test.go: update tests for new model
- service/project_infra_crud.go: store only NOTIFY_API_KEY on provision
- domain/credential.go: add CredKeyNotifySharedHost/CredKeyNotifySharedFrom
- cmd/rdev-api/config.go: add NotifySharedHost/NotifySharedFrom to InfraConfig
- service/component.go: add notifySharedHost/notifySharedFrom + WithNotifyDefaults
- service/component_deploy.go: inject shared host defaults when no custom host stored
- handlers/notify.go: handle shared-host projects in Reprovision guard;
add WithSharedNotifyHost builder
- cmd/rdev-api/main.go: wire SharedHost to provisioner, component service,
and notify handler
Bootstrap: NOTIFY_SHARED_HOST=threesix.ai and NOTIFY_SHARED_FROM=noreply@threesix.ai
stored in credential store (host id=1 already provisioned with Resend provider).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements ReprovisionNotifyHost to migrate a project's email sending
from an old notify host to a new one (e.g., from project-name-based to
slug-based host). Preserves the project's notify account and send key.
- Adds ReprovisionNotifyHost to port.NotifyProvisioner interface
- Implements revokeHostAccess on notifyAdminAPI + adminClient
- Implements Provisioner.ReprovisionNotifyHost (12-step migration)
in provisioner_reprovision.go (split to keep provisioner.go < 500 lines)
- Adds NotifyHandler.Reprovision handler (POST /notify/reprovision)
- Updates OpenAPI spec with reprovision endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three bugs in the notify provisioner DNS record upsert:
1. rec.Record ("DKIM"/"SPF") was used as the DNS record type — Cloudflare
doesn't know those labels. Fix: use rec.DNSType ("TXT"/"MX") from the
resendDNSRecord.type JSON field, which is the actual DNS record type.
2. rec.Name from Resend is already relative to the zone apex
(e.g., "resend._domainkey.mail.project-name"), not relative to the
registered domain. Code was doing rec.Name + "." + host which produced
a doubled subdomain. Fix: pass rec.Name directly — Cloudflare's
normalizeName appends ".baseDomain" to build the correct FQDN.
3. MX records have priority 10 in Resend's response but DNSRecord had no
Priority field and Cloudflare CreateRecord/UpdateRecord didn't send it.
Fix: add Priority int to domain.DNSRecord and include it in the body
for both Create and Update when non-zero.
These bugs caused DKIM/SPF DNS records to never be created for any project.
Re-provision affected projects using POST /projects/{id}/notify/provision
after clearing NOTIFY_RESEND_DOMAIN_ID from the credential store.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Repairs projects where notify account was created but Resend domain
provisioning (steps 7-9) failed — e.g., RESEND_API_KEY not yet
configured at project creation time.
- ProvisionNotifyDomain in port.NotifyProvisioner interface
- Provisioner.ProvisionNotifyDomain: creates Resend domain for existing
notify host, upserts DKIM/SPF DNS records in Cloudflare, kicks off
async verification via verifyWithRetry
- POST /projects/{id}/notify/provision handler:
- reads NOTIFY_HOST from credential store (fails if not set)
- rejects if NOTIFY_RESEND_DOMAIN_ID already present (use /verify)
- stores returned domain ID as NOTIFY_RESEND_DOMAIN_ID credential
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add verifyWithRetry to provisioner: 60s initial DNS propagation delay,
5 retries with 30s backoff before marking verification as failed
- Add GetNotifyDomainStatus: polls Resend API for domain verification status,
returns "not_configured" when Resend not set up
- Add VerifyProjectNotify: synchronous re-verification for handler use
- Add getDomainStatus to resendAPI interface + resendClient implementation
- Add NotifyDomainStatus domain struct (host, resend_domain_id, status)
- Guard NOTIFY_RESEND_DOMAIN_ID storage against empty string writes
- New handler: GET /projects/{id}/notify/status (returns verification state)
- New handler: POST /projects/{id}/notify/verify (triggers re-verification)
- Add verify-notify-domain cookbook step to persona-community,
slackpath-1, and slackpath-4 trees (polls status for up to 6 min)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix no-op RequireProjectAccess middleware to enforce project_ids
- Apply project access middleware to all project-scoped routes
- Filter GET /projects by allowed project IDs for restricted keys
- Add GET /me endpoint with key identity, scopes, and project access info
- Add PATCH /keys/{id} for partial key updates (name, scopes, project_ids, allowed_ips, expires_in)
- Add GET/POST/DELETE /projects/{id}/access for project-centric access management
- Auto-grant creating key access when using POST /project/create-and-build
- Accept grant_to_key_ids in create-and-build to grant multiple keys on project creation
- Move newProvisionerWithDeps test helper from production code to test file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add NotifyProvisioner (port + adapter) using real notify admin API
- Create notify account + send key + host grant per project
- Inject NOTIFY_API_KEY/HOST/FROM into component deployments
- Store NOTIFY_URL, NOTIFY_ADMIN_KEY, RESEND_API_KEY in credential store
- Add setup-notify.sh for one-time host/provider/domain setup
- Add NOTIFY_ADMIN_KEY constant to domain/credential.go
- Wire provisioner in main.go with connection test guard
- Add .claude/guides/services/notify.md and CLAUDE.md entry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>