1 # Addrbook - a contact management program
2
3 Created to work with the Aerc email client, but is completely useable from the
4 command line and might also work within other email clients so long as they
5 can launch terminal applications.
6
7 ## Text files _and_ a database?
8
9 Relational databases are great and text files are great. So which did I choose
10 for Addrbook? Both! I chose a database (SQLite) to store the address book
11 entries as proper relation database. But I also chose to interact with the
12 entries as text files. It turns out we can have the best of both worlds if we
13 treat the text as the _interface_ for the database.
14
15 Describing a text file as an "interface" probably sounds unusual. All it means
16 in this case is that any time you interact with an address book entry, it will
17 be in the form of a text file.
18
19 When you view an entry, you are looking at the contents of a text file that was
20 created for you that very instant from the contents of the address book
21 database.
22
23 If you edit an entry, you do so by editing the text file in your favorite text
24 editor. Then Addrbook imports your changes back into the database.
25
26 If you didn't know better, you would think the text files are the only storage
27 used by Addrbook. You wouldn't even know the database existed. But it does
28 and Addrbook won't work without it.
29
30 The text files are retained after they have been used, which means that the
31 database and text files are always 100% in sync with each other. (The only
32 reason this would not be true is if you've circumvented Addrbook's interface,
33 or there was an error in Addrbook, or your computer crashed while you were
34 editing an entry.)
35
36 The "help" message that is printed when you run Addrbook with no arguments
37 explains how editing works in the form of a list:
38
39 1. Writes the database contents of the entry to a text file.
40 2. Opens the text file in your editor.
41 3. Imports the edited contact information back to the database.
42
43 After using this program for a while, I am convinced that the database + text
44 file method not only works reliably, but gives the "best of both worlds" by
45 having plain text input and output in the Unix fashion, while retaining the
46 full power and reliability of a proper industrial-grade database (SQLite).
47
48 To query your address book entries, you can query the database with SQL _or_
49 query the text files with your favorite command line tools. Use whichever
50 method is the most convenient or comfortable!
51
52
53 ## Configuration
54
55 First, to run `addrbook` at all, you'll need to install the `sqlite3` gem:
56
57 gem install sqlite3
58
59 There is no installer, so you'll need to prepare the directories for
60 addrbook:
61
62 mkdir ~/beans/addrbook
63 mkdir ~/beans/addrbook/txt
64
65 Create the `addrbook.db` database from the `make_db.sql` schema file. I like to
66 do this interactively in the sqlite3 shell to confirm everything is correct.
67
68 Example SQLite session (silently creates the required tables):
69
70 $ sqlite3 ~/beans/addrbook/addrbook.db
71 SQLite version 3.50.4 2025-07-30 19:33:53
72 sqlite> .read addrbook-repo/make_db.sql
73
74 While you're in there, verify the schema:
75
76 sqlite> .schema
77 CREATE TABLE contact (
78 contact_id INTEGER PRIMARY KEY,
79 name,
80 added,
81 notes
82 );
83 CREATE TABLE email ( contact_id INTEGER, email);
84 CREATE TABLE phone ( contact_id INTEGER, phone);
85 CREATE TABLE url ( contact_id INTEGER, url);
86 sqlite> .quit
87
88 Specify the path to the base directory in an environment variable like so:
89
90 ADDRBOOK_PATH=$HOME/beans/addrbook
91 export ADDRBOOK_PATH
92
93 _Or_ you can hard-code your own path in the addrbook source like I did!
94 (See the source, near the top.)
95
96 Note: You can test addrbook with a temporary path like I often did
97 while developing, like so:
98
99 $ ADDRBOOK_PATH=/tmp/addr_test/ ./addrbook list
100
101 In summary, the address book directory should contain the following structure:
102
103 [dir]/addrbook.db - sqlite3 database, init with make_db.sql
104 [dir]/txt/ - empty directory, will store text files
105
106 The editor used to edit contacts is determined in the following order:
107
108 * Environment variable `$VISUAL`
109 * Or evironment variable `$EDITOR`
110 * Or Vim
111
112 A viewer is determined likewise:
113
114 * Environment variable `$PAGER`
115 * Or less
116
117 The 'cat' command is hard-coded to run whatever executable is named 'cat'
118 on your system.
119
120 ## Aerc quickstart
121
122 I wrote Addrbook for use within the Aerc terminal email client.
123
124 I've got this in `~/.config/aerc/binds.conf`:
125
126 [view]
127 # Address Book: av,ae,aa = "Address View, Edit, Add"
128 av = :term addrbook view '{{(index .From 0).Address}}' '{{(index .From 0).Name}}'<Enter>
129 ae = :term addrbook edit '{{(index .From 0).Address}}' '{{(index .From 0).Name}}'<Enter>
130 aa = :term addrbook add '{{(index .From 0).Address}}' '{{(index .From 0).Name}}'<Enter>
131
132 For a full explanation of these bindings, see the "Aerc binding details"
133 section below.
134
135 ## General usage
136
137 When you run addrbook without any arguments, it prints a usage summary which
138 will always be current. (I'll try to keep it current here too, but it's easy
139 to forget.)
140
141 Usage: addrbook <command> [params]
142
143 Available commands:
144 add [email addr] [name] Create new contact entry
145 edit <email addr | ID> Update contact as text, import changes.
146 view <email addr | ID> Display contact as text with pager
147 cat <email addr | ID> Output contact as text (to 'cat')
148 reimport <contact number> Updates contact DB entry from text.
149 list Display list of current contacts.
150 autocomplete-email <fuzzy> TODO
151
152 The 'edit' command is the normal way to update a contact. It:
153
154 1. Writes the database contents of the entry to a text file.
155 2. Opens the text file in your editor.
156 3. Imports the edited contact information back to the database.
157
158 The 'reimport' command performs only Step 3 and exists mostly for
159 recovery if the edit process was interrupted.
160
161 The three steps listed above are the most concise explanation I've managed to
162 write explaining how the edit process makes a round-trip from the database to
163 text files and back again.
164
165 ## Aerc binding details
166
167 Here's an explanation of the Aerc quickstart configuration above so you'll be
168 armed with the ability to modify mine or make your own shortcut bindings.
169
170 To quote `man aerc`:
171
172 To execute a command, press : to bring up the command
173 interface. Commands may also be bound to keys, see
174 aerc-binds(5) for details.
175 [...]
176 Dynamic arguments are expanded following aerc-templates(7)
177 depending on the context.
178
179 Creating new contacts in the address book from an email client will require
180 just the email address. Optionally, it can take the contact's name as well.
181
182 To test the value of an Aerc 'template' with a message selected in various Aerc
183 contexts (list, compose, view, etc.), you can use the built-in Aerc `echo`
184 command:
185
186 :echo {{.From}}
187
188 This was news to me until I started reading RFCs on my first attempt at this,
189 but an email's "From:" header can contain more than one sender for some dang
190 reason. So the `From` property actually returns a list.
191
192 To get the first email address and name to addrbook, I'm using the following
193 pair of templates as parameters. You can try these out with `echo`:
194
195 :echo {{(index .From 0).Address}}
196 :echo {{(index .From 0).Name}}
197
198 To open a new terminal emulator in a "tab" in Aerc, use the `term` command:
199
200 :term <cmd> [<arguments>]
201
202 (You can also leave off the command and Aerc will run your shell instead.)
203
204 When the command exits, the terminal will close.
205
206 The bindings config file is broken into sections. I've added mine to the
207 `[view]` section, which is when you're looking at an email, but not
208 composing a reply. Be careful about using a binding that starts with the
209 letter 'a' in the list context because that's bound by default to the
210 "archive" action!
211
212 As you can see in my 'aa' and 'ae' key binding examples, I put quotes
213 around the arguments as they're passed to addrbook. Not strictly needed
214 for the email address (which shouldn't have spaces!), but definitely
215 needed to pass a name with spaces like "John Smith" as a single argument.
216
217
218 ## Backing up your contact data
219
220 One of the problems with storing information in a binary format
221 (even a very open and reliable one like SQLite) is that it's hard
222 to track it with standard source control tools.
223
224 The mechanism for editing addrbook contact entries is writing them to
225 plain text files. So the data is always kept in perfect sync between text
226 and database without any additional effort! (You _can_ trick it, of
227 course, but that's on you.)
228
229 Therefore, it is completely reasonable to put the `txt` directory
230 under standard textual version control to keep track of changes
231 to your contact data.
232
233 Version control plus a standard automated backup system (you do have
234 a backup system, right?) should be quite good protection against
235 mistakes and equipment failures.
236
237
238 ## Inspiration
239
240 This was initially inspired by Max Schillinger's
241 [emailbook](https://sr.ht/~maxgyver83/emailbook/).
242 It's less than 150 lines of commented shell script (and nearly half are for
243 handling the command-line interface).
244
245 I'd intended to re-implement emailbook in Ruby (so it would run on OpenBSD).
246 After I made some good headway on my clone (and had added a 'notes' field), I
247 was reading the Aerc man pages and realized there was another way to send the
248 the current contact's email address to the application from Aerc while leaving
249 STDIN available for interactive use.
250
251 So at this point, Addrbook bears zero resemblance to emailbook. Nevertheless,
252 without the inspiration, I never would have thought to make this at all. So
253 thanks Max!
254
255 ## License
256
257 I still believe in the original mission of libre software. I honestly
258 don't know how I should be licensing things in 2026. If pressed, probably
259 a GNU license.