How can I rename folders when using vandelay

Your question

In Dovecot we have all folders below INBOX e.g. INBOX/Sent. When I migrate using vandelay, I want to change that so INBOX/Sent becomes just Sent under Stalwart.

How do I archive that?

Ralf

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Vandelay doesn’t have a folder-rename or re-parent option; it mirrors the source hierarchy one-to-one, so Dovecot’s INBOX.Sent comes across as Sent nested under the Inbox, which is the layout you’re trying to avoid. The nesting comes from Dovecot’s personal namespace using INBOX. as its prefix.

The cleanest fix is on the Dovecot side: adjust the namespace so the prefix is empty (folders become siblings of INBOX rather than children) before you migrate, and you’ll get a flat tree. If you’d rather not touch Dovecot, the archive Vandelay produces is a self-contained SQLite file for the account, so between import and export you can edit the mailboxes table to set the Sent/Drafts/etc. parent_id to NULL so they come out top-level (vandelay inspect mailbox shows the tree first); that’s a manual workaround rather than a supported flag. Simplest of all, if you don’t mind doing it after: migrate as-is and just move the folders in Stalwart afterward.

Ok, I know where the INBOX prefix comes from, I want to get ride of it :wink:

But I also don’t want to do that with all hosted domains at the same time.

So, I don’t want to change/touch the Dovecot config.

As I need to run the migration twice (before and after changing the delivery to Stalwart), I need to run my SQL modifications, do the first vandelay export, reset everything, run vandelay import again and run my modification a 2nd time before the final export / migration.

I also need to make these folder-name changes in:

  • subscriptions
  • Sieve scripts
  • IMAP ACL

A simple flag to just transform folder names on export like imapsync’s --regextrans2 would be nice, but I can work with a SQLite database.

Btw. is IMAP ACL stored in the SQLite DB and exported to Stalwart?

Ralf

There’s no rename or re-parent option in vandelay today, nothing like imapsync’s --regextrans2. Editing the SQLite archive between import and export is the workable route, and it’s less work than it looks.

The standard special folders don’t need any editing: on export, role-tagged mailboxes (Sent, Drafts, Trash, Junk, Archive) are matched by their role rather than their path, and the INBOX. prefix is stripped when the role is detected, so they land in Stalwart’s top-level Sent/Drafts/etc. on their own. The SQL is only needed for your own custom subfolders of INBOX. The archive has a mailboxes table with an id/name/parent_id layout, and re-parenting is just setting parent_id to NULL:

UPDATE mailboxes SET parent_id = NULL WHERE parent_id = (SELECT id FROM mailboxes WHERE role = ‘inbox’);

The name column is already the leaf name (Sent, not INBOX.Sent), so you leave it alone. Run this after each import and before the export; a re-import can recreate the folder rows, so re-apply it on your second pass, which lines up with the two-pass plan you described. Subscriptions ride along on the mailbox row, so they stay consistent automatically after the re-parenting.

On your other questions: IMAP ACLs are not stored in the archive and not exported (vandelay treats every imported resource as user-owned), so you’ll need to re-grant any sharing in Stalwart after the migration. Sieve scripts are migrated, but vandelay doesn’t rewrite folder names inside them, and editing the script bytes in SQLite would break their content hash, so fix any fileinto paths afterward via ManageSieve or the admin UI rather than in the archive. The migration tool docs are at Usage | Stalwart.

Hi Mauro,

I already implemented the folder renaming via SQL, so that’s working fine :slight_smile:

I also changed the blob of the sieve script, but have not checked it’s working.
Can I fix the content_hash, what kind of hash is it?

Ralf

It’s a BLAKE3 hash of the blob’s contents, stored as the raw 32 bytes in the blobs.hash column (referenced by sieve_scripts.blob_id). The good news is that you almost certainly don’t need to fix it: on export vandelay reads the sieve blob by its blob_id and doesn’t verify the hash, so your edited script will migrate correctly as-is. The hash is only used at import time for deduplication, so a stale hash is harmless for a one-shot migration (it would only matter if another blob with the identical new content were interned afterwards). That softens what I said earlier about it breaking the content hash: it’s technically stale, but not a problem on export.

If you’d rather keep it consistent anyway, recompute the BLAKE3 digest of the new content (b3sum) and update it as raw bytes, e.g. UPDATE blobs SET hash = X’<64-hex-digest>’ WHERE id = <blob_id>.

My patched vandelay checks now for blobs that are text (because of the SQL update of the Sieve script) and no longer chokes on the wrong type (text instead of blob, that was the SQL error we saw) and also fixes the blake3 hash.

With that SQL modified Sieve scripts are imported fine.

Ralf