The account export layout
A directory that holds a whole mail account in open formats: mail as
Maildir++, contacts as vCard, calendars as iCalendar, filters as Sieve and
files as files, with one manifest.json for what those formats
don't hold. Any server or app can read it, and it needs no tool to open.
| Version | 1 |
|---|---|
| Date | 2026-10-30 |
| Status | Stable. It is a layout of files, not a protocol element, so no registry is involved. |
| Change controller | Coffey Labs LLC |
| Contact | specs@coffeylabs.org |
1. Why
Every piece of a mail account already has an open format, but an account
is more than its pieces. A folder of .vcf and .ics
files doesn't say which address book was the default, which Sieve script was
running, what a folder was really called before its name was made safe for
a disk, or what the sender identities and signatures were. Without that, a
move to another server loses the parts people notice.
This specification fixes where each piece goes and adds one small JSON file for the rest. A server or app that can read Maildir, vCard and iCalendar can take most of an account without reading the manifest at all, and one that reads the manifest can take all of it.
The key words “MUST”, “MUST NOT”, “SHOULD”, “SHOULD NOT” and “MAY” are to be read as described in BCP 14 (RFC 2119 and RFC 8174) when, and only when, they appear in all capitals.
2. The tree
export/
mail/ Maildir++: the inbox here, other folders as .Parent.Child
contacts/*.vcf one file per address book
calendars/*.ics one file per calendar
sieve/*.sieve one file per script
files/ the file tree
manifest.json what the formats above don't hold
- An export holds one account. A writer MAY leave out any of the five parts; the manifest says which ones it wrote (section 4).
manifest.jsonMUST be written last. A tree without one is unfinished, and a reader MUST NOT treat it as an export.- A writer SHOULD start in a new or empty directory, so nothing from an earlier run is mixed in, and SHOULD make the directory readable by its owner only. It holds someone's mail.
3. The parts
3.1 Mail
mail/ is a Maildir++ tree, the layout Dovecot and Courier
serve, so either can serve it as it is.
mail/itself is the inbox. Every other folder is a directory beside it named.followed by its path, levels joined with.:.Archive,.Archive.2025. A folder under the inbox has the prefixINBOX., as in.INBOX.Receipts. Every folder directory holdscur,newandtmp, and every one but the inbox holds an emptymaildirfolderfile.- Each level of a folder's name is in modified UTF-7 (RFC 3501, section
5.1.3). A
.or/inside a name, and any character a directory name can't hold, becomes_. Names that clash, even only by case, are numbered:Work,work (2). The manifest keeps every real name. - Messages are in
cur/, one RFC 5322 message per file, exactly as the source held it. A file's modification time is when the message arrived. Readers MUST NOT depend on the rest of a file's name beyond the Maildir info after:2,. - The flags are the standard Maildir letters:
Ddraft,Fflagged,Pforwarded,Ranswered,Sseen andTdeleted. Other keywords, such as$junkor a label, are the lettersatoz, named in the folder'sdovecot-keywordsfile as lines of an index and a keyword (0 $junk). That allows 26 per folder. The manifest counts any that didn't fit. - A message kept in several folders is written to each. Nothing links the copies, so a reader merging folders into one store sees them as separate messages, as it would reading the same account over IMAP.
3.2 Contacts
contacts/ holds one vCard 4.0 file (RFC 6350) per address book,
named after it, with every card in that book. A card in two address books is
in both files. Photos and other media the card had are inside it as
data: URIs (RFC 2397), so each file stands on its own.
3.3 Calendars
calendars/ holds one iCalendar file (RFC 5545) per calendar,
named after it, with one VCALENDAR holding every event and each
time zone once. The calendar's name, description, time zone and color are
the X-WR-CALNAME, X-WR-CALDESC,
X-WR-TIMEZONE and X-APPLE-CALENDAR-COLOR properties
calendar apps read; the manifest has them too. Attachments are inside as
data: URIs. An event in two calendars is in both files.
3.4 Sieve scripts
sieve/ holds each Sieve script (RFC 5228) as it was, one per
file, named after the script with .sieve added. The manifest
says which one was active.
3.5 Files
files/ is the account's file storage as directories and files,
with their names and modification times. Links are listed in the manifest
and never made in the tree: the source chose where they point, and a link
could point anywhere on the machine that unpacks it.
3.6 Names on disk
Every name comes from the source, so none is used as a path as it stands.
Mail folders follow section 3.1. In every other file or directory name the
writer turns / \ : * ? " < > |
and control characters into _, drops trailing dots and spaces,
and puts _ in front of a name that is empty or starts with a
dot. Names Windows reserves (CON, NUL,
COM1 and the rest) get a trailing _. Names that
clash in one directory, compared without case, are numbered, so the tree
unpacks the same on any file system.
4. The manifest
manifest.json is a JSON object (RFC 8259), in UTF-8. Paths in it
are relative to the export's root and use /.
| Member | Type | What it holds |
|---|---|---|
| format | string, required | Always "https://coffeylabs.org/specs/account-export/", the address of this page. inbuxa-migrate 27.1.0-rc.2 wrote "inbuxa-migrate-files" instead, and a reader MAY accept that as the same format. |
| formatVersion | number, required | The version of this specification the export follows. This page defines 1. |
| createdBy | string, required | The program that wrote the export and its version. |
| created | string, required | When it was written, as an RFC 3339 date-time. |
| sources | array, required | Where the account came from (below). |
| object, optional | The mail tree and its folders. | |
| contacts | array, optional | One entry per address book. |
| calendars | array, optional | One entry per calendar. |
| sieve | array, optional | One entry per script. |
| files | object, optional | Media types and links for the file tree. |
| identities | array, optional | The addresses the account sends as, with their signatures. |
| participantIdentities | array, optional | The addresses the account uses in calendar invitations. |
A part the writer didn't export is left out of the manifest. A part it exported and found empty is there, as an empty array or an object with no entries. A reader MUST tell the two apart: the first says nothing about the account, the second says it had none.
Members of each part
sources: objects withkind(how the account was read, such as"jmap","imap"or"maildir"),url,accountId,accountName(ornull) andusername. Readers MUST accept kinds they don't know.mail:layout("maildir++"),path("mail") andfolders, one object per folder:name, its real name;path, an array of the real names from the top level down to this folder;dir, its directory;role, a mailbox role as JMAP names them ("inbox","sent","trash"and so on) ornull;subscribed;messages, the number of files in it; and, only when some didn't fit,keywordsDropped.contacts:name,description(ornull),file,defaultandcards, the number of cards.calendars:name,description,colorandtimeZone(each ornull),file,defaultandevents.sieve:name,fileandactive. At most one script is active.files:path("files"),mediaTypes, an object from each file's path to its media type where the source gave one, andlinks, an array of{path, target}wheretargetis the link's target as the source described it, ornull.identities:name,email,replyToandbcc(arrays of{name, email}, ornull),textSignatureandhtmlSignature, as in a JMAP Identity (RFC 8621, section 6).participantIdentities:name,calendarAddress(a URI, usuallymailto:) anddefault.
A reader MUST ignore members it doesn't recognize, so a later writer can add
members without breaking it. A change a reader has to know about, such as a
part moving or a member changing meaning, comes with a new
formatVersion. A reader SHOULD refuse a
formatVersion it doesn't support rather than guess.
Example
{
"format": "https://coffeylabs.org/specs/account-export/",
"formatVersion": 1,
"createdBy": "example-exporter 1.0",
"created": "2026-10-30T16:04:11Z",
"sources": [
{
"kind": "jmap",
"url": "https://mail.example.org/.well-known/jmap",
"accountId": "c3a",
"accountName": "alice@example.org",
"username": "alice@example.org"
}
],
"mail": {
"layout": "maildir++",
"path": "mail",
"folders": [
{ "name": "Inbox", "path": ["Inbox"], "dir": "mail",
"role": "inbox", "subscribed": true, "messages": 1204 },
{ "name": "v1.0", "path": ["Projects", "v1.0"], "dir": "mail/.Projects.v1_0",
"role": null, "subscribed": true, "messages": 37 }
]
},
"contacts": [
{ "name": "Personal", "description": null, "file": "contacts/Personal.vcf",
"default": true, "cards": 212 }
],
"calendars": [
{ "name": "Work", "description": null, "color": "#3a7bd5",
"timeZone": "America/Phoenix", "file": "calendars/Work.ics",
"default": true, "events": 418 }
],
"sieve": [
{ "name": "filters", "file": "sieve/filters.sieve", "active": true }
],
"files": {
"path": "files",
"mediaTypes": { "files/Taxes/2025.pdf": "application/pdf" },
"links": []
},
"identities": [
{ "name": "Alice Smith", "email": "alice@example.org", "replyTo": null,
"bcc": null, "textSignature": "Alice", "htmlSignature": "" }
],
"participantIdentities": [
{ "name": "Alice Smith", "calendarAddress": "mailto:alice@example.org",
"default": true }
]
}
5. Security considerations
- Paths in the manifest are data. A writer only names
paths inside the tree, but a reader can't know who wrote the export. A reader
MUST check that every
dirandfileresolves inside the export's root before opening it, and MUST NOT follow a symbolic link it finds in the tree. - Links are never made. Their targets came from the source and could point anywhere. A reader that recreates them SHOULD do so only inside its own store.
- The content is untrusted. Messages, cards, events, signatures and files are what the source held, including whatever a sender put in them. HTML signatures in particular SHOULD be sanitized like any other HTML before they are shown.
- No secrets go in. The format has no place for passwords, tokens, app passwords or keys, and a writer MUST NOT add them. The new server sets its own.
6. Privacy considerations
An export is a complete copy of one person's mail, contacts, calendars and files, and the manifest names the server and the username they came from. It deserves the care the account did: readable by its owner only, and encrypted wherever it is stored or sent.
7. Relation to other formats
- Maildir and Maildir++ have no RFC. This follows the
conventions Courier and Dovecot share, including Dovecot's
dovecot-keywordsfile. - mbox (RFC 4155) puts a folder in one file and has no standard place for flags or keywords, so it is not used here.
- JMAP (RFC 8620, RFC 8621) moves the same data between live servers. This format is for when there is no live server on one side, or for a copy that has to outlast both.
8. Implementations
inbuxa-migrate (opens in a new tab)
writes it with export-files, from 27.1.0-rc.2 (which named the
format inbuxa-migrate-files), and its
import maildir and import takeout commands read the
mail, contacts and calendars back. It is open source under Apache-2.0 or MIT.
Changes
- Version 1, 2026-10-30: the format is named by this
page's address instead of
inbuxa-migrate-files, so the name belongs to no one tool. Readers may accept the old name. - Version 1, 2026-10-30: first version, as inbuxa-migrate 27.1.0-rc.2 writes it.