v0.9.0 — credentials encrypted at rest, Drive actually reachable
Mailbox passwords are no longer stored in the clear
The server keeps live IMAP credentials for two reasons: sessions renew a bearer token without re-prompting, and the open-tracking pixel sends its notification as the original sender long after the request that created it. Both were plaintext columns, so any copy of the sqlite files — a backup, a volume snapshot, a stray docker cp — was a set of working passwords for every active user.
They're now sealed with AES-256-GCM. A fresh nonce per value, so the database doesn't leak which users share a password, and a tampered row fails to decrypt rather than decrypting to something the tamperer chose.
Set CREDENTIAL_ENCRYPTION_KEY (openssl rand -hex 32). Without it the server generates a key beside the databases and warns you — that keeps an upgrade zero-config, but a backup of the data directory then carries the key next to the ciphertext it protects, which is not the same thing as protection.
Upgrading is safe. Rows written before this have no prefix and are read back unchanged, so nobody is signed out; they're replaced with ciphertext on the next write. A session that can't be decrypted is dropped rather than handed to IMAP with a password we can't vouch for.
Drive was unreachable for almost every user
Buckets were provisioned with no CORS rules at all. The webmail's Drive is a direct browser-to-S3 client, so a bucket without them can never be opened: the preflight is refused and every operation surfaces as a bare "Failed to fetch". On the reference deployment exactly one bucket had rules — added by hand — which is why the feature looked like it worked.
createBucket now attaches rules from DRIVE_CORS_ORIGINS, and an existing bucket found without any gets topped up the next time it's provisioned.
"ManageSieve auth failed" now says what's wrong
mailcow gates ManageSieve per mailbox (attributes.sieve_access). Dovecot answers a disabled mailbox with a bare Authentication failed, which became a 502 ManageSieve auth failed — an operator staring at that has no way to know the password was fine and a checkbox in mailcow is the actual answer. The server now checks the attribute and says so, with the path to the setting.
Worth recording why this was investigated: production was logging these failures, but they turned out to be a service account with sieve deliberately disabled, not a bug. Every human mailbox on the deployment has it enabled and mail rules work fine.
320 tests, 319 passing, 1 skipped.