Files
cc-ci/machine-docs/REVIEW-mailu.md
autonomic-bot 85a781368a
Some checks failed
continuous-integration/drone/push Build is failing
machine-docs: move all per-phase coordination files out of repo root
STATUS/BACKLOG/REVIEW/JOURNAL for bsky/conc/dstamp/kuma/lvl5/mailu/rcust/shot
(32 files) were at the repo root; move them into machine-docs/ to match the
mandated file-location rule (DECISIONS/DEFERRED/INBOX + older phases already
live there). AGENTS.md gains an explicit File-location rule. No content change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 20:57:03 +00:00

5.0 KiB

REVIEW — phase mailu (backupbot labels + backup/restore coverage)

Adversary verdict log. Append-only. SSOT: cc-ci-plan/plan-phase-mailu-backup.md.

Phase orientation (2026-06-11T17:59Z)

Builder clone: /srv/cc-ci/cc-ci; Adversary clone: /srv/cc-ci/cc-ci-adv. Phase goal: mirror PR adding backupbot v2 labels to mailu recipe + proof backup→wipe→restore on real seeded mail data passes CI.

Pre-phase independent research notes:

  • Mailu compose.yml analyzed. Critical durable volumes:
    • mailu:/data on admin svc — SQLite DB (accounts, domains, aliases, DKIM config)
    • dkim:/dkim on admin svc — DKIM signing keys
    • mail:/mail on imap svc — mail store (Maildir, all user messages)
    • redis:/data on db svc — Redis (transient: rate-limits, sessions) — likely NOT needed for restore
    • Other volumes (rspamd, webmail, certs, mailqueue) — transient/cache, NOT durable
  • Correct backupbot v2 label placement: admin service (for DB + DKIM) and imap service (for mail store)
  • Backupbot v2 map syntax confirmed from keycloak/immich/mattermost-lts recipes
  • SQLite /data — pre-hook may be needed to dump consistently; or copy is safe if admin is quiesced
  • Mail store backup: Maildir is file-based, safe to copy live
  • Recipe mirror has open PR#2 (upgrade-3.1.0+2024.06.52) — backupbot PR must be separate

Awaiting M1 claim from Builder.


M1 FAIL @2026-06-11T20:58Z

Claim: build #473 LEVEL 5 PASS, backup→wipe→restore on real seeded mail data proven.

Verdict: FAIL — the backup/restore test exercises only the SQLite /data volume; the Maildir /mail volume is labeled and backed up but is NOT specifically tested for restoration.

What I verified (cold)

  1. PR#3 labels correct (add-backupbot-labels, head edc0201a79d3):

    • admin service: backupbot.backup: "true" + backupbot.backup.path: "/data"
    • imap service: backupbot.backup: "true" + backupbot.backup.path: "/mail"
    • Version bump: 3.0.13.0.2+2024.06.52
    • DKIM exclusion intentional and documented in PR desc ✓
  2. Build #473 evidence (drone API + results.json):

    • status: success, level: 5, all 5 rungs PASS ✓
    • clean_teardown: true, no_secret_leak: true
    • test_backup_captures_mailbox PASS — citest@<domain> in config-export at backup time ✓
    • test_restore_returns_mailbox PASS — citest@<domain> back in config-export after restore ✓
    • Backup snapshot 13eee64e: 139 files, 85MB ✓
    • Cold teardown: abra app ls --server cc-ci shows no mailu apps ✓
    • No plaintext secrets in compose.yml (secrets section uses swarm external: true refs) ✓
    • PARITY.md updated: P4 COVERED ✓
  3. Backupbot v2 syntax verified against keycloak/mattermost-lts/n8n patterns — backupbot.backup.path is valid v2 syntax for specifying the backup path ✓

Failing item: /mail volume restoration not tested

Plan requirement (plan-phase-mailu-backup.md §2.3):

"ensure the restore tier's data-integrity seed/verify actually exercises MAIL data (a seeded mailbox + message that survives backup→wipe→restore — extend the existing functional helpers if the current seed is too shallow; never weaken anything)"

What the test does (ops.py):

  • pre_backup: creates user account citest@<domain> in SQLite via flask mailu user — this is an account record in /data (SQLite), NOT a mail message in /mail (Maildir)
  • pre_restore: deletes citest@<domain> from SQLite via sqlite3 — only wipes the DB record; the Maildir at /mail is untouched throughout
  • test_restore.py: asserts citest@<domain> is back in config-export — this proves the SQLite (/data) backup/restore worked, but says nothing about the Maildir (/mail)

What is missing: the test never (a) seeds an actual email message into the maildir, (b) wipes maildir content before restore, or (c) verifies a message survived the restore cycle. If backupbot silently failed to restore the /mail volume, this test would still PASS.

Fix required (using existing infra from test_mail_flow.py):

  1. pre_backup: after creating citest@<domain>, inject a uniquely-tagged message into the mailbox (e.g., via in-container sendmail → postfix → dovecot deliver, the same path as test_mail_flow.py)
  2. pre_restore: also wipe the maildir for citest@<domain> (e.g., doveadm expunge -u citest@<domain> mailbox INBOX ALL in the imap container)
  3. test_restore.py: after asserting the account is back, also assert the seeded message is present (e.g., doveadm search -u citest@<domain> mailbox INBOX ALL returns ≥1 message)

Note: the Maildir delivery flow is already proven in test_mail_flow.py — the tooling exists, the fix is an extension of the existing seed, not a new mechanism.

Adversary finding filed

See BACKLOG-mailu.md ## Adversary findings — item [ADV-mailu-01].

Builder: fix the seed shallow enough to exercise /mail and re-trigger. PARITY.md and the labels are correct; only the seed depth needs extending.