Email / SMTP (Meeting Invites)
Status: Complete
Wire Chatalot to an outbound mail relay so it can send calendar invites when meetings are scheduled, updated, or cancelled.
What It Does
When a meeting is created, updated, or cancelled in Chatalot, the server sends each human channel member a calendar invite as an .ics attachment -- the same format used by Google Calendar, Outlook, and Apple Calendar. Bot accounts are skipped. Sends are fire-and-forget: a delivery failure for one recipient is logged and skipped; it does not fail the scheduling operation or block other recipients.
Email Is Opt-In
If SMTP_HOST or EMAIL_FROM is not set, outbound email is silently disabled. Every other feature -- messaging, voice, file uploads, communities -- continues to work normally. There is no degraded mode, no error on startup, and no user-visible indication that email is unconfigured. Configure it when you want invite emails; leave it out when you do not.
Email is enabled only when both SMTP_HOST and EMAIL_FROM are set.
Security & Privacy
Chatalot is privacy-first, and a calendar invite necessarily leaves your server's boundary for attendees' mail providers. The feature is built to send the minimum required and to protect the transport:
- Transport encryption. With
SMTP_TLS=true(the default) Chatalot uses STARTTLS and validates the relay's TLS certificate — a relay with an invalid/expired/self-signed certificate is rejected, not silently accepted. There is no "accept invalid certificate" option. UseSMTP_TLS=false(plaintext) only for a trusted local relay or test catcher on the same host/private network; it logs a startup warning. Never point a plaintext connection at a remote relay — credentials and mail would travel unencrypted. - What data is sent. Each attendee receives an invite carrying only: the meeting title, start/end time + timezone, the organizer (name + address), themselves as the sole attendee, and the in-app join link. It deliberately does not include the meeting's free-text description (that stays in-app, behind the join link) and does not include the other attendees' addresses — each recipient's
.icslists only the organizer and themselves, so the attendee roster is never disclosed across recipients. No message history or end-to-end-encrypted content is ever emailed. - Bots excluded. Bot/automation accounts are never emailed.
- Rate-limited. Rapid edits to a meeting are throttled (one invite email per meeting per minute) so a burst of changes can't spam attendees; cancellations always send.
- Deliverability is your responsibility. SPF, DKIM, and DMARC for your sending domain are operator-configured (see Deliverability below). Without them, invites will be spam-foldered or rejected.
- Credentials. Provide
SMTP_PASSWORDvia an env var or a Docker secret; Chatalot never logs it. Use a least-privilege, app-scoped credential where your provider supports one. - Failure is silent to users. A mail outage never blocks scheduling and is never surfaced to the person scheduling — failures are logged server-side only.
Environment Variables
| Variable | Description | Default |
|---|---|---|
SMTP_HOST |
Hostname of your SMTP relay (e.g. smtp.mailgun.org) |
(none -- email disabled) |
SMTP_PORT |
SMTP port | 587 |
SMTP_USERNAME |
SMTP authentication username | (none) |
SMTP_PASSWORD |
SMTP authentication password (see "Secrets" below) | (none) |
SMTP_TLS |
true = STARTTLS + auth (real relays, default); false = plaintext only (local catchers) |
true |
EMAIL_FROM |
Envelope and From: address (e.g. noreply@example.com) |
(none -- email disabled) |
EMAIL_FROM_NAME |
Display name in the From: header |
Chatalot |
SMTP_PASSWORD -- env var or Docker secret
The password is resolved in this order:
SMTP_PASSWORDenvironment variable (direct value in.env)./run/secrets/smtp_password-- a Docker secret file. Use this to keep the password out of your compose environment entirely.
If neither is present, authentication is attempted without a password (appropriate for local catchers that require no auth).
STARTTLS vs Plaintext
SMTP_TLS=true (the default) connects with STARTTLS and authenticates. Use this for every real mail relay -- Gmail, SES, Mailgun, SendGrid, Postfix, Proton Bridge, etc.
SMTP_TLS=false uses plaintext SMTP with no TLS. This exists exclusively for local development catchers (e.g. Mailpit) that accept unauthenticated plaintext on a loopback port. Never point SMTP_TLS=false at an internet-facing relay -- credentials will be transmitted in the clear.
From Address and Display Name
EMAIL_FROM must be a valid RFC 5321 address your relay is authorised to send from. If EMAIL_FROM_NAME is omitted, recipients will see Chatalot <noreply@example.com> in their mail client.
Choose an address on a domain you control and for which you have set up SPF, DKIM, and DMARC (see "Deliverability" below). A mismatch between EMAIL_FROM and the domain your relay authenticates will cause rejections at destinations that enforce DMARC.
Deliverability
Chatalot sends the email; getting it accepted and placed in the inbox is the responsibility of the operator. Three DNS records protect your sending reputation and are required by most modern mail servers:
SPF -- A TXT record on your sending domain that lists which mail servers are authorised to send on its behalf. Example for a single relay at mail.example.com:
DKIM -- Your relay signs outgoing messages with a private key; a TXT record publishes the corresponding public key. The exact setup depends on your relay software or provider -- consult their documentation. Without a valid DKIM signature, many providers will reject your mail or route it to spam.
DMARC -- A TXT record that tells receiving servers what to do when SPF or DKIM fails. Start with p=none (report only) while you verify your configuration, then tighten to p=quarantine or p=reject:
A domain without these records, or with a misconfigured p=reject policy and no working DKIM, will have most invites delivered directly to spam or rejected outright. If you are sending from a shared relay domain (e.g. a transactional service's subdomain), the relay provider handles SPF/DKIM on their end -- confirm this in their documentation.
Container-to-Host SMTP Relay
If your SMTP relay runs on the same machine as Chatalot (for example, a Postfix instance or a local Mailpit catcher), the chatalot container cannot reach it via localhost or 127.0.0.1 -- those addresses resolve to the container itself, not the host.
Use Docker's host-gateway feature instead. In docker-compose.yml, add an extra_hosts entry:
Then set:
The chatalot container will route SMTP traffic to the Docker host's network stack, where your relay is listening.
Worked Examples
Generic authenticated relay (STARTTLS, port 587)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=noreply@example.com
SMTP_PASSWORD=your-smtp-password
SMTP_TLS=true
EMAIL_FROM=noreply@example.com
EMAIL_FROM_NAME=Chatalot
Amazon SES (SMTP interface)
Create SMTP credentials from the SES console (IAM user with ses:SendRawEmail). SES requires STARTTLS on port 587 or 465.
SMTP_HOST=email-smtp.us-east-1.amazonaws.com # adjust region
SMTP_PORT=587
SMTP_USERNAME=AKIA... # SMTP access key ID from SES
SMTP_PASSWORD=your-ses-smtp-secret-key
SMTP_TLS=true
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME=Your Instance Name
Verify your sending domain in the SES console and ensure the account is out of the sandbox before sending to non-verified recipients.
Mailgun / SendGrid
Both services provide SMTP credentials in their dashboard. The pattern is the same as the generic relay example; substitute the hostname and credentials from your provider.
Mailgun:
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USERNAME=postmaster@mg.yourdomain.com
SMTP_PASSWORD=your-mailgun-smtp-password
SMTP_TLS=true
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME=Chatalot
SendGrid:
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USERNAME=apikey
SMTP_PASSWORD=SG.your-api-key
SMTP_TLS=true
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME=Chatalot
Self-hosted Postfix (relay on Docker host)
# docker-compose.yml (add to the chatalot service)
services:
chatalot:
extra_hosts:
- "host.docker.internal:host-gateway"
# .env
SMTP_HOST=host.docker.internal
SMTP_PORT=587
SMTP_USERNAME=chatalot@yourdomain.com
SMTP_PASSWORD=your-postfix-password
SMTP_TLS=true
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME=Chatalot
Ensure Postfix is configured to accept authenticated submissions on port 587 (submission service in master.cf) and that your Chatalot user is present in the credential store (e.g. /etc/sasldb2).
Proton Mail Bridge (Seglamater's own setup -- an example, not required)
Note: This is how Seglamater runs its own Chatalot instance. It is included as a concrete reference only. There is no requirement to use Proton Mail Bridge; it is one of many valid choices.
Proton Mail Bridge runs as a local SMTP daemon that bridges Proton Mail accounts to standard SMTP clients. It does not use STARTTLS on the local port -- it expects a plaintext connection from the container and handles encryption itself on the outbound side.
# .env
SMTP_HOST=host.docker.internal
SMTP_PORT=1025 # Bridge's local SMTP port (check Bridge settings)
SMTP_USERNAME=your@proton.me
SMTP_PASSWORD=your-bridge-smtp-password
SMTP_TLS=false # Bridge handles TLS on the outbound side; local connection is plaintext
EMAIL_FROM=your@proton.me
EMAIL_FROM_NAME=Chatalot
Verifying Your Configuration
Before pointing at a real relay, test locally with Mailpit -- a zero-config SMTP catcher with a web UI that shows every email the server sends without delivering anything.
# .env -- point chatalot at the local catcher
SMTP_HOST=host.docker.internal
SMTP_PORT=1025
SMTP_TLS=false # required; Mailpit does not use STARTTLS
EMAIL_FROM=test@example.com
EMAIL_FROM_NAME=Chatalot Test
Open http://localhost:8025 to see captured emails. Once invites appear there, switch SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, and SMTP_TLS to your real relay values.
Next Step
For general environment variable reference, see Configuration. For hardening advice (file permissions, secrets management), see Security Hardening.