This is a card in Dave's Virtual Box of Cards.

Contacts - ideas and notes

Page created: 2025-09-15

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:

And the following export options:

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

history.sqlite

Personal Address Book

abook.sqlite

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:

Screenshot from Thunderbird address book shows an entry for the hobbit 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.