docs: sessions, accounts, trusted senders, conversations, themes and the new reader
README: what the webmail now does, the architecture roles, a short guide with the shortcuts, testing with a real database, and an honest list of what is not finished. New docs/WEBMAIL.md as the reference for each feature and for the colour system. OPERATIONS: sessions and trusted senders. DEPLOYMENT: the two-step release as API calls, and what to expect when upgrading. MAILBOX-CORE: the conversation invariants and migrations 0015 to 0018. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c00134f2eb
commit
58d6f8e9e2
5 files changed
+471
-17
No files matched your search
@@ -26,6 +26,11 @@ paid tiers and no artificial limits on domains or mailboxes.
|
||||
| **Filing** | Sieve rules built in the UI, custom folders, and an RFC 5230 vacation responder that replies in thread and once per sender per week |
|
||||
| **Identity** | Per-user profile photos with a circular crop editor, shown inside the webmail; BIMI logo hosting and validation for the mark external clients render |
|
||||
| **Web** | Webmail at `/user/*` and an admin console at `/admin/*`, isolated by session audience, with TOTP two-factor, recovery codes, app passwords, and English, Portuguese and Spanish |
|
||||
| **Sessions** | Webmail sessions last 30 days and renew while used; the console stays at 8 hours. Several accounts can be signed in at once and switched from the sidebar, without signing out |
|
||||
| **Trusted senders** | Trusting a sender keeps their mail out of spam and always shows their images, kept on the server per mailbox. "Not spam" in the Spam folder does both in one click. A forged From is not rescued: delivery needs DMARC, or SPF for the From domain |
|
||||
| **Conversations** | Mail is listed one row per conversation, with who wrote, how many, and whether you have answered. The messages stack in the reader and the reply is written under them. One switch turns it off |
|
||||
| **Reading** | A message written plainly takes the app's own colours; a designed one (newsletter, receipt) keeps its white sheet. Quoted history folds away, remote images are blocked until you say so, and a plain message can be seen as sent |
|
||||
| **Appearance** | Dark (default) and light themes, a command menu on `Ctrl K` / `Cmd K`, `c` and `/` shortcuts, and an installable app with its own icon. Fonts are served by the app itself |
|
||||
| **Console** | Server-side paging and filtering, user editing (role, quota, language, password, second factor), per-user aliases, and a diagnostics page at `/admin/tests` |
|
||||
|
||||
## Architecture
|
||||
@@ -33,12 +38,14 @@ paid tiers and no artificial limits on domains or mailboxes.
|
||||
Two runtimes, split by what each is good at:
|
||||
|
||||
- **`protocols`** (Go) — every protocol listener, the delivery worker, TLS
|
||||
certificate handling, DNS reconciliation, and the webmail's mail API. It owns
|
||||
folder, UID, flag and blob semantics.
|
||||
- **`auth`** (TypeScript) — logins, sessions, two-factor, account management and
|
||||
the admin console API.
|
||||
- **`web`** (Next.js) — both web surfaces, and the proxy that gives the browser a
|
||||
single origin.
|
||||
certificate handling, DNS reconciliation, and the webmail's mail API
|
||||
(messages, conversations, drafts). It owns folder, UID, flag and blob
|
||||
semantics, and decides at delivery which conversation a message joins and
|
||||
whether a trusted sender's mail skips spam.
|
||||
- **`auth`** (TypeScript) — logins, sessions and the accounts signed in on a
|
||||
browser, two-factor, trusted senders, preferences, and the admin console API.
|
||||
- **`webmail`** (Next.js) — both web surfaces, and the proxy that gives the
|
||||
browser a single origin.
|
||||
- **`storage`** (TypeScript) — health only today.
|
||||
|
||||
Postgres is the source of truth for everything, including the outbound queue.
|
||||
@@ -46,14 +53,37 @@ Redis is a cache and dispatch index; losing it loses no mail.
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
browser ─────────▶│ web │──┬─▶ auth (sessions, accounts, admin)
|
||||
└──────────┘ └─▶ protocols (mail read/send, DKIM, TLS)
|
||||
browser ─────────▶│ webmail │──┬─▶ auth (sessions, accounts, trusted senders, admin)
|
||||
└──────────┘ └─▶ protocols (mail read/send, conversations, DKIM, TLS)
|
||||
│
|
||||
internet ──25/587/465/143/993──────────┘
|
||||
│
|
||||
Postgres ─┴─ Redis ─ Rspamd ─ ClamAV
|
||||
```
|
||||
|
||||
## Using the webmail
|
||||
|
||||
The reference is **[docs/WEBMAIL.md](docs/WEBMAIL.md)**. The short version:
|
||||
|
||||
- **Stay signed in.** A session lasts 30 days and renews each day it is used.
|
||||
Add another account from the menu at the foot of the sidebar and switch
|
||||
between them; sign-out leaves the others signed in.
|
||||
- **Trust a sender.** The shield beside a sender's name trusts them: their mail
|
||||
stops going to spam and their images load. In the Spam folder, **Not spam**
|
||||
moves the message to the inbox and trusts the sender.
|
||||
- **Read as conversations.** One row per conversation; open it and reply under
|
||||
the messages. Settings → Language turns this off.
|
||||
- **Press `Ctrl K` (or `Cmd K`).** Folders, Compose, Settings, accounts and
|
||||
appearance, by typing. `c` writes a message and `/` searches.
|
||||
- **Pick a theme** (dark, light or system) from the same menu at the foot of the
|
||||
sidebar. Dark is the default.
|
||||
|
||||
| Key | |
|
||||
| :--- | :--- |
|
||||
| `Ctrl K` / `Cmd K` | Command menu |
|
||||
| `c` | New message |
|
||||
| `/` | Search the list |
|
||||
|
||||
## Deploying
|
||||
|
||||
For a real VPS, follow **[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)**. It covers the
|
||||
@@ -134,6 +164,7 @@ The webmail is on `http://localhost:3000`. The seeded password is
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
make test-db-up # a throwaway Postgres on :55432; prints the two variables to export
|
||||
make test # Go suite, auth and webmail
|
||||
make lint # go vet, tsc, and the language lint
|
||||
```
|
||||
@@ -141,7 +172,16 @@ make lint # go vet, tsc, and the language lint
|
||||
The Go and auth suites include integration tests that need Postgres. They skip
|
||||
without `TEST_DATABASE_URL` and run against a real database in CI, which is
|
||||
where the security regressions are enforced — wrong password, wrong TOTP code,
|
||||
session revocation, surface isolation, cross-mailbox reads.
|
||||
session revocation, surface isolation, cross-mailbox reads, and now sessions
|
||||
that renew, accounts that switch, trusted senders and which conversation a
|
||||
reply joins. A green run **without** the variable skips all of that, so
|
||||
`make test` says so loudly; run it with the database before trusting it.
|
||||
|
||||
The webmail suite is file- and logic-level (`vitest`): it guards the colour
|
||||
system, the sanitiser, the reader's modes, the composer's undo and discard, and
|
||||
that every string exists in all three languages. The screens themselves are
|
||||
checked in a real browser; see
|
||||
[docs/WEBMAIL.md](docs/WEBMAIL.md#testing-it).
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -298,15 +338,31 @@ Stated plainly, because a mail server that half works loses mail:
|
||||
|
||||
- **Read-only console panels.** The guided domain onboarding checklist reports
|
||||
on state rather than driving it.
|
||||
- **Not implemented.** JMAP, CalDAV/CardDAV, multi-node. Backups are
|
||||
documented but not automated.
|
||||
- **Attachments from the composer.** The picker lists files but nothing is
|
||||
uploaded, and the app says so on screen. Reading and downloading received
|
||||
attachments works.
|
||||
- **Spam filter learning.** "Not spam" stops condemning that sender, but Rspamd
|
||||
does not learn from it, so similar mail from strangers is judged as before.
|
||||
- **Conversations are grouped at delivery.** Older mail is grouped by subject,
|
||||
and only where a reply exists; a conversation whose replies lost their
|
||||
`References` and subject may show as separate rows.
|
||||
- **Not implemented.** IMAP `APPEND`, JMAP, CalDAV/CardDAV, multi-node. Backups
|
||||
are documented but not automated.
|
||||
- **DANE** is off by default. See [DANE](#dane) for turning it on; under
|
||||
`TLS_MODE=traefik` it stays off deliberately, because the proxy's key changes
|
||||
at every renewal and the published TLSA record stops matching.
|
||||
|
||||
## Contributing
|
||||
|
||||
- All code, comments and identifiers are in English. `make lint` enforces it.
|
||||
- All code, comments and identifiers are in English. `make lint` enforces it
|
||||
(and, for code, ASCII only: write `\u00e9`, not an accented letter).
|
||||
- Colours come from the theme variables in `globals.css`, never a literal, and a
|
||||
new one is defined in both themes; see
|
||||
[docs/WEBMAIL.md](docs/WEBMAIL.md#the-colour-system). The logo is generated
|
||||
from [`scripts/brand/`](scripts/brand/README.md).
|
||||
- New migrations are numbered in `migrations/` and run on deploy. Delivery code
|
||||
must not depend on one that may not have run: the end-to-end tests apply only
|
||||
the first.
|
||||
- Tests come with the change that needs them; the suites above must stay green.
|
||||
- Commits reference their issue: `#N: what changed and why`.
|
||||
|
||||
|
||||
Reference in new issue
Block a user