logicspike/docs

Communication

Public contact addresses — decision record

Decided 2026-10-05. Revised 2026-10-06: these addresses are received by mailboxes in Vlozi's own Mailbox (§2), not by Cloudflare Email Routing rules (§4, §5). This is the single source for which addresses Vlozi publishes and why. The policy drafts in ../../legal/, the AWS request text and the site must all use exactly these. Do not add another public address without updating this file.

1. The decision

All addresses are on vlozi.app. No personal address appears on the site or in the policies. The AWS root login uses founder@vlozi.app, which an explicit Cloudflare rule delivers to a personal inbox (gap-log G56).

Published (site, policies, AWS case)

Address For Notes
hello@vlozi.app general contact, product questions, support already on the site; keep as the From of platform mail
privacy@vlozi.app data-protection requests and the grievance contact statutory clocks (30 days) run from here, so it gets its own address
abuse@vlozi.app spam, phishing and other abuse reports RFC 2142 role address; AWS checks it
postmaster@vlozi.app mail-delivery questions from other mail operators RFC 2142 role address; AWS checks it
security@vlozi.app vulnerability reports already on the site; also in /.well-known/security.txt
legal@vlozi.app legal notices and legal process

Courtesy (not published)

support@, billing@, info@. People guess these. An address with no mailbox behind it is accepted and then lost (the comms worker drops mail that has no owner, with no bounce; §3). A mailbox for each costs nothing (§2), so each has its own.

Private (never published)

ops@vlozi.app — an extra contact for vendor accounts: registrar, hosting and billing notices, and an additional contact on the AWS form. It lets a second person receive these later. Which address AWS uses to reach you is a separate rule: see §7. (Cloudflare and AWS root logins keep whatever address they already use; do not change a root login to do this.)

Deliberately not used

  • noreply@ / no-reply@: platform mail is sent as Vlozi <hello@vlozi.app> so a recipient who replies reaches a monitored address.
  • A published catch-all. Never advertise one; it invites spam.
  • grievance@: not needed; privacy@ is the grievance contact. The name is reserved so nobody can claim it.

All of these names are reserved in the shared list (packages/core-types/src/reserved-local-parts.ts). Comms looks for an account on <name>@vlozi.app first and falls back to the mail.vlozi.app namespace, where a customer could claim an unreserved name and then receive platform mail.

2. Where they are received: Vlozi's own Mailbox, on vlozi.app (decided 2026-10-06)

Decided 2026-10-06: these addresses are Vlozi Mailbox mailboxes (aliases there are unlimited, and it is the product being dogfooded), not Cloudflare Email Routing rules, and the mailbox domain is vlozi.app itself.

The model. Every workspace gets mail.vlozi.app by default and may add its own verified domain. Vlozi's own workspace (ls_8acbb3d7-…) is the one that holds the verified vlozi.app sending domain, so vlozi.app is one of its domains, and its role addresses are mailboxes on vlozi.app: the address people write to, the mailbox, and the address replies leave from are the same.

How mail gets there. The Cloudflare catch-all hands mail for <name>@vlozi.app to the comms worker (nine of the ten addresses arrived in the 2026-10-06 tests, see R4; the test in §4 proves it per address). Comms first asks the Mailbox whether there is an account on <name>@vlozi.app; if so the mail is delivered into it. If not, it falls back to the older rule (filed under <name>@mail.vlozi.app, so customers' aliases keep working). Nothing is created from that path.

How Vlozi's workspace is recognised. By holding the verified vlozi.app domain: a row that no tenant can create for itself, that only one tenant can ever hold, and that comms already uses to decide who may send as vlozi.app. No setting and no tenant id in code. If comms cannot be asked, the answer is no. Everything below keys off that.

What Vlozi's workspace may create:

  1. On vlozi.app: only the platform's role and platform names (abuse, privacy, postmaster, security, legal, hello, support, billing, info, ops ...). Not ordinary names, because a root mailbox called acme@vlozi.app would shadow a customer's acme@mail.vlozi.app alias (comms files mail for <name>@vlozi.app under that namespace). Free (mailboxes on a verified domain are never charged).
  2. On mail.vlozi.app: the same role and platform names are also allowed (they are refused to every other workspace), free, and the workspace may use the Vlozi name in a sender name there ("Vlozi Privacy"). Prefer vlozi.app (see "replies" below).

What stays closed to everyone, Vlozi's workspace included: notify (the shared sender every other workspace's mail leaves from: a mailbox of that name would receive replies to other workspaces' customers) and the brand names (paypal, apple, ...). For every other workspace nothing changed: mail.vlozi.app by default, add your own domain, reserved names refused.

Replies. A reply from abuse@vlozi.app goes out through Resend, which serves vlozi.app. That matters: the SES account is declared for platform mail only, so while it is, the SES guard refuses all tenant sends from mail.vlozi.app (Resend does not serve that domain). A reply from a role mailbox on mail.vlozi.app would therefore be refused (503) until tenant sending is enabled. On vlozi.app it works.

Trade-offs:

  1. Independence. An abuse inbox now depends on the comms worker, the Mailbox worker and the Cloudflare catch-all all being up. When any of them fails, mail to abuse@ is lost silently (no bounce; what Cloudflare does when a worker throws is not documented). Cloudflare Email Routing alone had none of those dependencies. Mitigation: the outside test in §4 on a schedule (monthly is enough), and watch for any [Inbound] error line in wrangler tail logicspike-communication.
  2. Unknown addresses (§8): since 2026-10-21 (gap-log G11), mail to an unclaimed or deactivated address is refused, so the sender gets a bounce. A failed lookup is still never turned into a bounce, so trade-off 1 stands. Claiming the courtesy names removes the likely cases.
  3. A new platform-only rule on vlozi.app: a name that is not a role name cannot be a mailbox there (see 1 above).

3. What the live checks showed (2026-10-05)

  • vlozi.app and mail.vlozi.app both have MX at Cloudflare Email Routing (route1/2/3.mx.cloudflare.net).
  • A recipient-only SMTP check (no message sent) against Cloudflare's MX: every address was accepted, including a made-up one (zz-probe-nonexistent…@vlozi.app), on both zones. So a catch-all exists. It hands mail to the comms worker (that is what the Vlozi alias lane runs on, and what §2 relies on); the test in §4 proves it per address. What the comms worker does with an address nobody owns is drop it, with no bounce (inbound.ts), so an unclaimed address loses its mail.
  • in.vlozi.app does not exist in DNS. Reply routing (reply+…@in.vlozi.app) and mailto unsubscribe cannot work yet; both are off, which is consistent, and neither matters for Phase A. It matters for tenant mail (see phase-b.md).
  • SPF at vlozi.app is Cloudflare routing only; DMARC is p=none with Cloudflare's report address; there is no bounce.vlozi.app. These are gate R3.

4. Set it up (you, in the Vlozi dashboard; about 15 minutes)

First, deploy, in this order: comms, then mailbox, then the dashboard (a push to main; Vercel deploys it). core-types is a build-time dependency of the workers; no separate deploy. Comms first because the mailbox relies on it for the domain check, and the dashboard last because it only needs to offer the new option.

Then, signed in to the Vlozi workspace (the one that sends as hello@vlozi.app; the Vlozi tools showed it holds the verified vlozi.app domain):

  1. Mailbox → Manage → New mailbox, pick the domain @vlozi.app in the address box, and create each of: hello, privacy, abuse, postmaster, security, legal, support, billing, info, ops. Give each a display name that says what it is, for example Vlozi Privacy, Vlozi Abuse Desk. The dialog says it is free and shows no "no direct inbox" warning. If @vlozi.app is not in the domain list, this workspace does not hold the verified vlozi.app domain. If it says only role addresses can be created there, you typed a name that is not a role name.
  2. Access. Owners and admins see every mailbox automatically. Give anyone else who should read or answer one access from Manage. For abuse and postmaster, give at least two people when you can.
  3. Test each published address from an outside account (a Gmail you do not forward from): send to <name>@vlozi.app, then confirm it appears in that mailbox in the dashboard. Note the time. This is gate R4 and evidence item 7. Then reply from the dashboard to one and confirm the outside account receives it from <name>@vlozi.app. If a test mail never appears, nothing bounces: look for [Inbound] lines in npx wrangler tail logicspike-communication (from apps\communication, with $env:CLOUDFLARE_ACCOUNT_ID set; the tail prints recipient addresses, so keep its output out of evidence).
  4. If an address does not arrive, look for an explicit Cloudflare Email Routing rule for it. A specific rule wins over the catch-all that hands mail to the worker, so an old rule (hello@ had one on 2026-10-06, gap G55) sends that address somewhere else and its mailbox stays empty. Deleting the rule moves the address into the Mailbox. Decided 2026-10-07: keep the rules for hello@ and founder@ (gap-log G55, G56). Otherwise do not touch the catch-all or any Cloudflare rule for this: nothing is needed there beyond what already hands vlozi.app mail to the comms worker.
  5. Repeat the outside test monthly (and after any comms or mailbox deploy that touches inbound mail), because a failure here is silent.

5. Replying from these addresses

Reply from the mailbox in the dashboard. The mail leaves from <name>@vlozi.app with the display name you set, through Resend (which serves vlozi.app), and a reply to it comes back to the same mailbox. It does not touch the AWS account.

To send as one of these from a mail client instead, use Resend's SMTP with a separate sending-only key limited to vlozi.app, kept out of the repo and the production secrets. Do not use SES SMTP for this while the account is in the sandbox: it can only send to verified addresses.

6. Platform mail identity (unchanged, on purpose)

Mail From Reply-To
login codes, resets, invitations, account and billing notices Vlozi <hello@vlozi.app> none (a reply goes to hello@)
owner alerts same same

hello@ is the From because it is monitored and already public. A reply to a login-code email is rare and harmless; it lands in the same inbox.

7. Using these on the AWS account

The rule (gap-log G56). Any address AWS uses to reach you must not depend on this platform or the vlozi.app domain being up. If a DNS or routing fault cut the domain, AWS's mail would be lost at the moment it matters. So:

  • Alternate contacts (billing, operations, security) and the CloudWatch alarm email (R9): the operator's existing private inbox, the one that already receives founder@ mail (decided 2026-10-07, gap-log G63; the address is deliberately not written in the repo). Confirm the alarm's SNS subscription from that inbox.
  • "Additional contact addresses" on the production-access form: ops@vlozi.app is fine as an extra, only after its mailbox exists and an outside test mail has arrived in it.
  • AWS root login: founder@vlozi.app, delivered by a Cloudflare rule to a personal inbox. Do not change it. Test that an outside mail to it arrives (R2).
  • The case text names abuse@ and postmaster@ only; never name ops@ publicly.

8. Follow-ups this surfaced (not done)

Rejecting unknown recipients instead of accepting and dropping them (gap-log G11, G12: live since 2026-10-21), in.vlozi.app and INBOUND_DOMAIN, and custom-domain inbound for tenants are tracked in phase-b.md and the gap log. The rest:

Item Why When
security.txt renewal /.well-known/security.txt expires 2027-09-30; an expired file is flagged as invalid by scanners Calendar reminder, September 2027
security.txt after the next site deploy It is in the static export (checked), but confirm Cloudflare Pages serves the dot-folder: curl.exe -i https://vlozi.app/.well-known/security.txt should be 200 text/plain Next deploy
MTA-STS / TLS-RPT Hardens inbound mail transport; low value for the AWS request Later, optional
Google Postmaster Tools for vlozi.app Reputation view for Gmail; the domain already carries a Google site-verification record, so it may verify with the same Google account Phase 2 of the runbook, optional
Communication