apps/mail is the standalone Tuturuuu mailbox app. It runs on https://mail.tuturuuu.com in production and port 7820 locally, delegates auth to apps/web, and only admits exact @tuturuuu.com accounts. Addresses such as @xwf.tuturuuu.com are intentionally denied.
Architecture
apps/mailowns the mailbox UI and proxies/api/*toapps/web.apps/webowns all protected mail APIs under/api/v1/workspaces/:wsId/mail/*.- The mailbox mirror is stored in
private.mail_*tables and is service-role only. App pages and route handlers must authorize exact-domain users and mailbox membership before returning data. - Outbound messages use
@tuturuuu/email-servicewith the selected mailbox address as the source. - Inbound messages use SES Email Receiving with S3 raw-message storage and SNS notifications. The webhook stores an idempotent inbound job, fetches raw MIME from S3, mirrors sanitized bodies and attachment metadata, and quarantines unknown recipients.
/verify-token in the proxy before centralized auth handling so
local Portless handoffs set cookies on mail.tuturuuu.localhost. The app keeps
token refresh same-origin through /api/auth/refresh-app-session; a valid
refresh cookie should rotate the Mail-local and Web-issued app-session cookies
without sending the browser back through apps/web login.
SES Receiving Setup
Do not change DNS from code or migrations. The current public MX fortuturuuu.com is Google-routed, so real @tuturuuu.com receiving requires an explicit staged MX cutover or a pilot subdomain first.
- Verify the domain or pilot subdomain in the SES receiving region.
- Create an S3 bucket for raw MIME objects.
- Create an SNS topic for receipt notifications and subscribe the web webhook:
POST /api/v1/webhooks/mail/ses. - Create an SES receipt rule that stores raw MIME in S3 and publishes the SNS notification.
- Configure
MAIL_SES_INBOUND_TOPIC_ARN,MAIL_SES_INBOUND_BUCKET,MAIL_SES_INBOUND_KEY_PREFIX, andMAIL_SES_REGION. - Only after validation, stage the MX/DNS change outside the app repository.
MAIL_SES_SNS_SIGNATURE_VERIFICATION=disabled. Do not use that setting in production.
Operations
- Run
bun sb:uplocally after mail schema changes, thenbun sb:typegen. - Keep new mail route access checks in
apps/web; do not add direct client Supabase reads inapps/mail. - Use
packages/internal-api/src/mail.tsfor client helpers and TanStack Query in the app UI. - Unknown inbound recipients are retained as
quarantinedjobs for administrator review instead of being delivered to a user inbox. - Keep generated public assets such as
/manifest.webmanifest,/sw.js, and Serwist worker files out of the auth proxy matcher. Redirecting those files to central Web login breaks standalone Mail startup and PWA registration. - Keep
apps/mail/src/proxy.tsconfig.matcherentries as inline string literals. Next.js statically parses proxy matcher config during Vercel builds and rejects imported constants even when they resolve to strings.
CI and deployment
Mail has dedicated Vercel workflows:.github/workflows/vercel-preview-mail.yaml.github/workflows/vercel-production-mail.yaml
tuturuuu.ts and use the shared
ci-check.yml switchboard. They require environment-scoped Vercel credentials
plus VERCEL_MAIL_PROJECT_ID; production Supabase and SES values should live in
the Vercel project environment rather than GitHub Actions.
The preview workflow builds and deploys prebuilt artifacts through Vercel CLI.
The production workflow also uses workflow concurrency so stale production runs
are canceled instead of deploying after a newer production commit is pushed.
Deployment credentials must stay environment-scoped in GitHub Actions. The
preview job is bound to the vercel-preview-mail GitHub Environment, and the
production job is bound to vercel-production-mail. Store VERCEL_TOKEN,
VERCEL_ORG_ID, and VERCEL_MAIL_PROJECT_ID in those environments instead of
repository-wide or organization-wide secrets. Do not add TURBO_TOKEN,
TURBO_TEAM, production Supabase service keys, or SES credentials to
workflow-level env; Mail deploys should rely on Vercel project environment
variables pulled by vercel pull. Manual production dispatch is only valid from
refs/heads/production.
apps/mail/vercel.json disables Vercel Git deployments and GitHub integration
so preview and production deploys only happen through the CI workflows.