feat(dovecot): historical-account andriy.oblivantsev@wheregroup.com for wheregroup incubator (#252)

This commit is contained in:
2026-09-02 13:42:58 +01:00
parent 8041ab3962
commit 6cdd704aef
2 changed files with 39 additions and 18 deletions
+29 -15
View File
@@ -20,12 +20,21 @@ mailboxes:
- `ano@produktor.io`
- `postmaster@produktor.io`
- `postman@produktor.io`
- `wheregroup@produktor.io` — incubator mailbox (issue #251): nobody logs in;
legacy `.eml` corpus is imported via `doveadm import` by the ETL connector.
- `andriy.oblivantsev@wheregroup.com` — incubator mailbox of the гдеgroup
period (issue #252): the owner's **historical address**, not an abstract
source box. Nobody logs in; legacy `.eml` corpus is imported via doveadm by
the ETL connector (`bin/mail/incubator.go`, 2dph), routed to
`Sent`/`INBOX`/`INBOX/Unmatched` by the recipient headers.
- `wheregroup@produktor.io` — superseded A1 pilot box (abstract "source"
model, issue #251): its 1000 imported messages are migrated to
`andriy.oblivantsev@wheregroup.com` and the account is then deleted (issue
#252). Listed until deletion; `user-patches.sh` skips owners that are no
longer in `postfix-accounts.cf`.
Passwords live in `.env` (`INFO_PASSWORD`, `ANDRIY_PASSWORD`; `ano@` uses
`GATOR_MAIL_PASS` in the gator repo `.env`; `wheregroup@` uses
`WHEGROUP_PASSWORD`, random — no interactive login). Do not commit `.env`.
`GATOR_MAIL_PASS` in the gator repo `.env`; `andriy.oblivantsev@wheregroup.com`
uses `ANDRIY_WG_PASSWORD`, random — no interactive login; `wheregroup@` uses
`WHEGROUP_PASSWORD`). Do not commit `.env`.
## Web UI (Roundcube)
@@ -75,21 +84,24 @@ changedetector (`check-for-changes.sh`, polls every 2 s) picks up the edited
Dovecot. No mail is lost, no container recreation.
```bash
# 1. random password for the new source mailbox (stored in .env only)
# 1. random password for the new historical-address mailbox (stored in .env only)
PW=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 32)
printf 'WHEGROUP_PASSWORD=%s\n' "$PW" >> .env # never commit .env
printf 'ANDRIY_WG_PASSWORD=%s\n' "$PW" >> .env # never commit .env
# 2. add the account — password is read from stdin, never from argv/ps
# 2. add the account — password is read from stdin, never from argv/ps.
# DMS accepts any virtual user: the domain need not be serviced by
# mail.produktor.io (verified live with wheregroup.com, issue #252) — the
# account only serves IMAP/doveadm, no inbound delivery.
printf '%s\n%s\n' "$PW" "$PW" |
docker exec -i mailserver setup email add wheregroup@produktor.io
docker exec -i mailserver setup email add andriy.oblivantsev@wheregroup.com
# 3. changedetector applies within ~5 s; verify
docker exec mailserver doveadm user wheregroup@produktor.io
docker exec mailserver doveadm user andriy.oblivantsev@wheregroup.com
# 4. give the fresh mailbox an INBOX (a brand-new owner has none — without it
# `doveadm mailbox list -u <owner>` is empty and user-patches.sh cannot
# record any share), then apply the shared/ACL policy
docker exec mailserver doveadm mailbox create -u wheregroup@produktor.io INBOX
docker exec mailserver doveadm mailbox create -u andriy.oblivantsev@wheregroup.com INBOX
docker exec mailserver /bin/bash /tmp/docker-mailserver/user-patches.sh
```
@@ -107,11 +119,13 @@ right sets for `info@`:
- **production owners** (`ano@`, `andriy.oblivantsev@`, `postmaster@`):
read-only — `lookup read` (issue #79);
- **incubator owners** (`wheregroup@produktor.io`, later `gmail_lenovo@`,
`tb-*`/`pst-*`): read **and delete** — `lookup read delete expunge
write-deleted` (issue #251). The owner never logs in; mail arrives via
`doveadm import`. Deleting a message in Roundcube = filter/exclusion from
the corpus (auto-sync, epic B), so the delete button must work in `Shared/`.
- **incubator owners** (one account per historical address of the owner:
`andriy.oblivantsev@wheregroup.com` for the гдеgroup period; later
`eslider@gmail.com`, `...@viscreation.de`, ...): read **and delete** —
`lookup read delete expunge write-deleted` (issue #251). The owner never
logs in; mail arrives via `doveadm import`. Deleting a message in
Roundcube = filter/exclusion from the corpus (auto-sync, epic B), so the
delete button must work in `Shared/`.
Verified on live: `write-deleted` is sufficient for the `\Deleted` flag
Roundcube sets (no extra `write` right needed), `expunge` is also what
Dovecot MOVE needs on the source side when Roundcube moves a deleted message
+10 -3
View File
@@ -3,11 +3,18 @@
# two owner classes (issue #79, issue #251 / epic #250):
# - production owners (ano@, andriy.oblivantsev@, postmaster@): read-only
# (`lookup read`) — one login in Roundcube covers the whole account list;
# - incubator owners (wheregroup@produktor.io; future sources like
# gmail_lenovo@, tb-*/pst-*): read + delete (`lookup read delete expunge
# - incubator owners: read + delete (`lookup read delete expunge
# write-deleted`) — the mailbox owner never logs in, mail is imported via
# doveadm; deleting a message in Roundcube = filter/exclusion from the
# corpus (autosync, epic B).
# Incubator model (corrected 2026-09-02, issue #252): the incubator mailbox IS
# the owner's HISTORICAL ADDRESS per period, not an abstract "source" mailbox.
# The wheregroup period = andriy.oblivantsev@wheregroup.com; later periods get
# their own account (eslider@gmail.com, ...@viscreation.de, ...).
# wheregroup@produktor.io stays listed as the superseded A1 pilot box until
# its 1000 messages are migrated and the account is deleted — grant_share
# skips owners that are not (yet) in postfix-accounts.cf, so a deleted account
# is a silent no-op.
# DMS runs this only on the FIRST start of each container instance (plain
# `docker compose restart` skips the setup step by design — /CONTAINER_START
# marker), so it must stay idempotent. ACLs, the shared dict and subscriptions
@@ -31,7 +38,7 @@ chmod 0770 "${SHARED_DB_DIR}"
# when Roundcube moves a deleted message to Trash.
READER='info@produktor.io'
PRODUCTION_OWNERS='ano@produktor.io andriy.oblivantsev@produktor.io postmaster@produktor.io'
INCUBATOR_OWNERS='wheregroup@produktor.io'
INCUBATOR_OWNERS='andriy.oblivantsev@wheregroup.com wheregroup@produktor.io'
grant_share() { # $1=owner, remaining=right names
local owner=$1