Paul LütznerandClaude Opus 5 24a5ab3de1 build: mercure 0.8, and a database of PHPUnit's own
MERCURE 0.5.0 / symfony/mercure 0.8.0, kept out of the earlier sweep
because it carries the live-update transport and should not ride along
with eighteen patch bumps. It earned that: HubInterface gained
getProtocolVersion() and getCookieName(), so the two hub doubles in
CalendarSyncFinishedNotificationTest stopped satisfying it — a fatal
error, not a failing test. Both implement them now, returning what a real
hub would rather than pretending to configuration these tests do not
have. Verified where it matters: the full browser suite, including the
specs that stop and restart the hub to prove the stream recovers.

A DATABASE OF ITS OWN for the unit suite.

Both suites run APP_ENV=test, and doctrine.yaml's
`dbname_suffix: '_test%env(default::TEST_TOKEN)%'` therefore pointed both
at app_test. One database, two suites — and one test in here truncates
real tables, because exercising a data reset is its whole purpose.

Sequentially that is safe; Postgres rolls a TRUNCATE back like anything
else. Concurrently it is not: TRUNCATE takes an ACCESS EXCLUSIVE lock, so
live queries from the browser suite block until the unit test's
transaction ends, and specs with no connection to any of this report
timeouts. It fits the transaction abort seen here once and never
reproduced.

TEST_TOKEN is Symfony's own mechanism for exactly this, so the fix uses
it rather than inventing a second DATABASE_URL. bin/test-unit.sh sets it
and does the create/migrate/seed a fresh database needs.

The seeding is the part worth guarding: PageRendersTest, ListFragmentTest
and others SKIP THEMSELVES when the admin user is missing, so without it a
green run would quietly be ~50 tests smaller than it looks — a worse
failure than a red one. Checked rather than assumed: 4047 tests either
way, no skipped or incomplete markers.

The assertion count moved, 11540 → 11526, and settled there across runs.
That is the point of the change showing up: the unit suite was reading
rows the browser stack had left behind, and now it reads only its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 15:08:17 +02:00
2026-07-28 12:33:46 +02:00
2026-06-24 14:51:50 +02:00
2026-06-24 14:51:50 +02:00
2026-07-21 16:18:46 +02:00
2026-07-31 15:45:26 +02:00
2026-07-28 12:33:46 +02:00
2026-07-02 13:12:28 +02:00
2026-07-31 12:28:04 +02:00
2026-07-30 16:40:31 +02:00
2026-07-28 15:44:19 +02:00

plMail

Self-hosted mail with a calendar beside it. Connect the mailboxes you already have — IMAP, Gmail, Outlook — and read them side by side in one inbox, on a server you own.

PHP Symfony PostgreSQL License

📖 Read the handbook →

Installing on Docker, Linux, WSL2, macOS or a NAS · every feature in detail · connecting Gmail and Outlook, with the exact permissions to tick · how the internals work. Also on the wiki and in docs/ — all three are built from the same files.


What it is

A mail client you host yourself. It connects to the mailboxes you already have — IMAP, Gmail, Outlook — syncs them into your own PostgreSQL database, and gives them one inbox, one search box and one set of labels in the browser.

It is not a mail server: it doesn't receive mail from the internet or host your domain. Because everything is synced locally, search runs against your database, and your mail stays readable when a provider doesn't.

The plMail inbox

Highlights

  • Every account in one place — IMAP, Gmail and Outlook side by side, sending aliases included. Credentials are encrypted before they touch the database.
  • New mail arrives on its own — IMAP IDLE, Gmail and Outlook push, browser notifications.
  • Search that means it — full text plus from:, subject:, label:, has:, before: and friends, completed as you type.
  • Filters as a tree — any/all/none, nested, restated in plain English and counted against real mail before you save. Apply one to mail that arrived before it existed.
  • A calendar beside the mail — two-way sync with Google, Microsoft and CalDAV, drag-to-move week grid, invitations answered in place, share and booking links.
  • Files where you keep them — attach from and save to Drive, Photos, OneDrive, Dropbox, Nextcloud and Immich.
  • Yours — two-factor auth, per-app JMAP passwords, 41 themes, English, German and Pirate.

A look around

Reading a thread Writing a message
Threads — replies collapse into one conversation. Compose — rich text, autocomplete, undo send.
Dark mode Calendar
Themes — 41 of them, light and dark. Calendar — docked beside the mail, or its own page.
Filters Admin
Filters — counted before you save. Admin — workers, push health, logs, queue.

Screenshots use demo data — none of the accounts, senders or messages shown are real.

Install

Images are published for linux/amd64 and linux/arm64. Nothing to fill in first: the secrets are generated on first start, and the public address is asked for on the setup screen.

Docker

git clone https://github.com/karatektus/pl_mail.git
cd pl_mail
docker compose up -d

Open https://localhost and create your account.

TrueNAS SCALE

  1. Create a dataset for plMail — say yourPool/yourDataSet/plmail.
  2. Open truenas.compose.yaml and change one line to that path:
    path_data: &path_data /mnt/yourPool/yourDataSet/plmail
    
  3. Apps → Discover → ⋮ → Install via YAML, paste the file, install.

Reach it on port 30080 and put a reverse proxy in front of it. Everything else in that file works as it is; each setting explains itself in place.

Then

Add an IMAP mailbox in Settings → Mail accounts and you're done. Gmail and Outlook need OAuth credentials from Google or Microsoft first — the handbook walks through both, including the optional Pub/Sub topic for instant Gmail delivery.

Back up two things, separately: the PostgreSQL data and var/secrets/generated.env. The database holds your mail; the key inside that file unlocks the credentials in it. Keeping them apart is the point — and losing the key means the stored mailbox passwords are gone.

Contributing

Bug reports and pull requests welcome — see CONTRIBUTING.md for the dev setup, the test suites and the console commands.

License

AGPL-3.0.

S
Description
Upstream source mirror for plMail webmail client
Readme AGPL-3.0
48 MiB
0 Stars 1 Watchers 0 Forks
Languages
PHP 69.1%
JavaScript 12.9%
Twig 9.9%
TypeScript 6.7%
CSS 1.2%
Other 0.1%