Skip to content
covey

Mail from the installation

Two different things send mail in covey, and they have nothing to do with each other:

  • An agent's mail. Its own mailbox, read by IMAP, answered by SMTP, brokered per call. That is the email target system and it is configured per organisation.
  • The installation's mail — this page. A confirmation link, a password reset, a notification that something is waiting for a decision. From the instance itself, to one person, no mailbox of its own.

Short version: Platform → E-mail, enter the server, save, send a test mail. Until that test arrives, signup.mode cannot leave off.

1. What you need

A mailbox the instance may send from — a mail server of your own, or a hosted one with SMTP submission. Not an inbox: nothing is read here, only sent. An address like no-reply@example.com is the usual shape.

At providers that require it (Gmail/Workspace, Office 365) this is an app password, not the account password.

2. The settings

Platform → E-mail, visible to a system_admin. The page writes into the instance's own settings table, not into the environment — a wrong SMTP host is corrected here and takes effect on the next send, with no redeploy and no restart.

Setting Meaning
mail.smtp_host the server, without a scheme and without a port (mail.example.com)
mail.smtp_port 587 for STARTTLS, 465 for TLS from the first byte
mail.security starttls | tls | none
mail.smtp_user the login name — often the sender address, but not always
mail.smtp_password sealed with the master key, never returned: the field shows whether one is stored, and an empty save clears it
mail.from the sender address
mail.from_name the name in front of it; empty falls back to site.name

none sends in plain text and exists for a local double, not for a mailbox.

Two more settings decide what the mails contain, under Platform → Settings:

Setting Meaning
site.url the address this installation is reachable at from outside (https://covey.example.com) — every link in a mail is built from it. It takes precedence over COVEY_SITE_URL; empty means the variable, and for notification mails no links at all
notify.window how long notification events are collected before a mail goes out — a duration such as 5m or 1h, at most 24h; 0s sends on the sender's next pass, which runs once a minute
notify.decision, notify.task, notify.cost, notify.ops the installation's master switch per notification class, on or off. off overrides every account's own switch: nothing of that class is written down for anybody, and rows still waiting when the switch is flipped are retired rather than sent

3. The test mail

The button sends to the address of the administrator pressing it, over exactly the path a real mail takes: the same sender, the same stored settings, only the body differs. A test that travelled its own route could pass while every real mail failed.

It sends the settings as stored, not what stands in the form — save first, then test, so that what was proven is what will run. The button stays disabled while something is unsaved.

The result is kept (mail.last_test_at, mail.last_test_error) and shown under the card. Whoever opens registration a week later can see when this last worked instead of having to remember.

When it fails

The SMTP server's answer is passed through verbatim, because it is more precise than any sentence that could replace it:

What it says Where to look
connection refused port or host — 587 and 465 are not interchangeable
smtp starttls: … the server wants TLS from the first byte: mail.security = tls, port 465
535 5.7.8 authentication failed user or password; at Gmail/Office 365 an app password is required
550 … not permitted to send the mailbox may not send under this mail.from
a timeout after 15 seconds a firewall between instance and mail server that swallows the packets

4. What the installation sends

Four messages, all of them in the language the sender page was in — the catalogues are the interface's own (web/src/locales/*.json), so a mail arrives in the language somebody registered in. Each goes out as multipart/alternative: a plain-text part, and beside it the same content as HTML in the interface's design language. The HTML loads nothing — no font, no image, no stylesheet from anywhere — so a client that blocks remote content shows the same mail as one that does not, and the text part is the whole mail for a client that shows no HTML.

Mail Trigger Lifetime of its link
Confirmation a registration, and send a new link on /verify 24 hours, one use
Password reset forgotten your password? on the sign-in page one hour, one use
Test mail the button on this page
Notifications something needs a person: a waiting decision, a finished task, a budget cap, a runner that left

Notification mails are grouped: an event is sent after the notify.window has passed, together with whatever joined it in that window, and what has been dealt with by then produces no mail at all. Every person chooses per class on their own account page which of them they want; the installation's notify.<class> switches stand above that choice, and a class switched off there shows greyed out on the account page.

Set site.url on a public instance. The links in those mails need a host. The notification sender has no request to derive one from and leaves the links out while neither the setting nor COVEY_SITE_URL is set; the confirmation and reset mails fall back to the request — which means to the Host header. Behind a proxy that passes the origin through, that is right; where anybody can set the header, it lets a stranger decide which domain a confirmation link points at, and the mail would carry the token there. The setting closes both gaps from the same page the mail server is configured on.

5. Why registration hangs on it

signup.mode cannot leave off while no test mail has ever gone through. That is deliberate: an instance that opens self-registration without a working mailer produces accounts whose confirmation link is never sent — and the people affected cannot report it, because reporting it would require an account.

A filled-in host is not evidence. A delivered message is.

6. Trying it out locally

demo/fakemail (go run ./demo/fakemail) is a mail double: it accepts everything and keeps a log of what arrived. For a development instance:

mail.smtp_host      127.0.0.1
mail.smtp_port      1025
mail.security       none
mail.from           covey@example.test

What arrived is then listed by curl http://localhost:8025/mails — and the test mail travels the same path a real server would see, TLS aside.