Exporting Mailman3's mailing-list archives through HyperKitty on the CLI
Find a file
veg 8b00e239d2 Trim README to admin altitude, reframe as export-first
Drop the backup-and-destroy headline framing (the script only ever
exports; removal is the caller's opt-in) and cut the ELI5 hand-holding
aimed at someone who isn't the admin of this box. Collapse exit codes to
a table, demote removal to a short exit-code-gated snippet. No code change.
2026-05-31 14:23:15 +00:00
mm3-hk-export Normalize exporter failures, durable mkdir, reject swapped FIFO 2026-05-31 12:28:55 +00:00
README.md Trim README to admin altitude, reframe as export-first 2026-05-31 14:23:15 +00:00

mm3-hk-export

Export a private HyperKitty (Mailman 3) list archive to mbox from the command line — the CLI equivalent of the web UI's Download button.

Why

HyperKitty ships no hyperkitty_export command; the only built-in export is the web Download view. This drives the same Email.as_bytes() path through the mailman-web wrapper, so the output is byte-for-byte identical — no web login, no DB reconstruction. It has to be mailman-web, not mailman-wrapper: the archive lives in the HyperKitty DB, not core.

Requirements

Run as root — mailman-web drops to the web user to read the archive, then the script publishes the result as root. Uses mailman-wrapper to report a list's archive_policy when there's nothing to export.

Usage

mm3-hk-export [-z|--gzip] <list@domain> [output]

output defaults to ./<localpart>.mbox (.mbox.gz with -z). -z gzips the stream to match the web .mbox.gz byte-for-byte (deterministic header, mtime=0).

mm3-hk-export announce@lists.example.com /root/mm3/announce.mbox
mm3-hk-export --gzip announce@lists.example.com /root/mm3/announce.mbox.gz

Exit codes

Never writes a partial or empty file. The exit code reports whether a backup was produced:

Exit Meaning
0 archive written (wrote N messages to …)
3 nothing to archive — empty list, or no archive at all (reports archive_policy: never vs nothing-yet)
2 usage / bad address
1 error (e.g. mailman-web failed)

3 is distinct from 0 so a caller can tell "backed up" from "nothing to back up" and refuse to destroy an uncaptured list.

Gating a removal on the backup

The script only exports. To remove a list after a verified backup, gate it on the exit code yourself:

mm3-hk-export "$addr" "/root/mm3/$list.mbox" \
    || { echo "no backup; keeping $addr" >&2; exit 1; }
mailman-wrapper members "$addr" > "/root/mm3/$list.members"
mailman-wrapper remove "$addr"

Export before remove, never remove on a non-zero exit. (Core's remove leaves the HyperKitty archive untouched anyway — separate DB — but a failed export must still halt the destroy.)

Caveats

  • Reconstruction, not the original bytes. HyperKitty rebuilds each message from parsed fields (From, To, Subject, Date, Message-ID, In-Reply-To, body, attachments); Cc, References, List-*, and X-* are lost. Inherent to HyperKitty — the web download is identical.
  • Large lists. Streamed via a DB iterator, so no memory blow-up like the web export.
  • Re-import. Valid mbox; re-importable with hyperkitty_import.
  • Handoff. The web user builds the archive in /var/tmp; root verifies it by SHA-256 and publishes atomically, so a swap, truncation, or corruption before publish is caught and nothing is published. A compromised mailman-web is out of scope.