Contacts - ideas and notes
I want to be able to make better notes about my email contacts like the way Derek Sivers does: https://sive.rs/dbt
I tried this in a text file (well, in my VViki wiki which is plaintext), but even that is too cumbersome to open separately - I really want to be able to use this in the email application itself. I also want to keep it in sync across machines.
All those *DAV solutions like CardDAV are so heavy.
Import/Export (and sync via dotfiles with Git + Gnu Stow) my contacts between Thunderbird somehow - vCard or another common interchange format?
As of Thunderbird version 108-something, the following import options are available:
-
CSV (and TSV) - Okay, but long-ass lines are not actually that great for record-based data…
-
LDIF (wikipedia.org) - plain text, but nasty, crusty corporate stuff
-
vCard (wikipedia.org) - small and readable. Properly record-based.
-
SQLite - neat. I’d prefer plaintext for this, but SQLite is great.
-
.mab Mork database (wikipedia.org) - run for the hills!!!
And the following export options:
-
CSV (and TSV)
-
vCard - this looks great, when I did it, a single
.vcf
file was written with all of the contacts concatenated together. -
LDIF - who cares
vCard looks very promising
Plaintext, record-based. To pass more than one in a single file or stream, you just concatenate them. I like it. (Though very similar, I much prefer the vCard format to iCal (at least as it is practiced in the wild).)
Also, according to this:
"Since Thunderbird 102, contact details are stored as vCards…"
(Fun fact: It also mentions that Thunderbird uses ical.js
to parse vCard
data, which is a library I also happen use on my
iCalendar Viewer page.)
Thunderbird stores the vCards as entries in SQLite database(s).
Let’s look at the address book DBs:
$ thunderbird --version Thunderbird 102.8.0 $ ag -g '\b(abook|history)\.sqlite' ~/.thunderbird /home/dave/.thunderbird/xmcghbrl.default-default/history.sqlite /home/dave/.thunderbird/xmcghbrl.default-default/abook.sqlite
Default address books in Thunderbird:
Name in the UI | File |
---|---|
Collected Addresses |
|
Personal Address Book |
|
All Address Books |
No file, this exists only in memory at runtime. |
To examine the Thunderbird address book SQLite schema and storage, I’ll make an example entry in my (currently empty) Personal Address Book.
I present Bilbo Baggins:

Let’s open abook.sqlite
and list the structure with the SQLite command .schema
.
This prints the SQL statements that would create the tables and indexes in the
address book database:
$ sqlite3 .thunderbird/xmcghbrl.default-default/abook.sqlite sqlite> .schema CREATE TABLE properties (card TEXT, name TEXT, value TEXT); CREATE TABLE lists (uid TEXT PRIMARY KEY, name TEXT, nickName TEXT, description TEXT); CREATE TABLE list_cards (list TEXT, card TEXT, PRIMARY KEY(list, card)); CREATE INDEX properties_card ON properties(card); CREATE INDEX properties_name ON properties(name);
Both lists
and list_cards
are presumably for making mailing lists from your
contacts (I noticed this feature while exploring the address book interfaces).
There are also two indexes for the properties table.
So, this leads us to conclude that all of the contact information is split
into entries in the properties
table.
Also note that the DB file is locked when Thunderbird is running:
$ sqlite3 .thunderbird/xmcghbrl.default-default/abook.sqlite SQLite version 3.37.2 2022-01-06 13:25:41 Enter ".help" for usage hints. sqlite> select * from properties; Error: in prepare, database is locked (5)
Having closed Thunderbird, now I can view the contents of the properties
table:
sqlite> select * from properties; card name value ------------------------------------ ----------------- ------------ fbed0feb-dee5-4117-ac0c-efb154b35e09 PopularityIndex 0 fbed0feb-dee5-4117-ac0c-efb154b35e09 LastModifiedDate 1757978585 fbed0feb-dee5-4117-ac0c-efb154b35e09 _vCard BEGIN:VCARD VERSION:4.0 N:Baggins;Bilbo;;; FN:Bilbo Baggins NICKNAME:Filthy Bagginses EMAIL;PREF=1:bilbob@example.com URL:https://there-and-back-again.lotr ADR:;;Bag End;The Hill\, Hobbiton;West Farthing;;The Shire\, Middle Earth UID:fbed0feb-dee5-4117-ac0c-efb154b35e09 NOTE:This is my dear old friend Bilbo. He likes to adventure\, which is unu sual for hobbits.\n\nWe last talked in the Party Field\, but I don't rememb er what it was about.\nDon't forget to bring him some cheese.\nDon't mentio n The Ring. BDAY;VALUE=DATE:28900922 END:VCARD fbed0feb-dee5-4117-ac0c-efb154b35e09 PreferDisplayName 1 fbed0feb-dee5-4117-ac0c-efb154b35e09 DisplayName Bilbo Baggins fbed0feb-dee5-4117-ac0c-efb154b35e09 FirstName Bilbo fbed0feb-dee5-4117-ac0c-efb154b35e09 LastName Baggins fbed0feb-dee5-4117-ac0c-efb154b35e09 PrimaryEmail bilbob@example.com fbed0feb-dee5-4117-ac0c-efb154b35e09 NickName Filthy Bagginses
If you’re wondering how I got those nice headers for the table columns, see my SQLite3 CLI notes.
So a "card" is given a unique ID and each property of a card is just a name/value pair.
But the really interesting thing to me is that the entire contact is,
in fact, a _vCard
entry as multiple lines of text in a single field.
This is wonderful and means I can get all of the contacs as vCards out of Thunderbird with just:
select value from properties where name='_vCard';
Okay, this is looking very good indeed.
Now, syncing items from vCards back to Thunderbird will be more interesting (for example, I’ll want to update existing records, so I’ll probably use the email address as a unique key for each contact and find the "card" key from that and update the other properties as needed). But at least I know this is very, very possible.