Alexandria
You come back from a trip with a pocket full of receipts. Filing them one by one is only worth it if you can ask something useful afterwards — like whether that pizza really was more expensive this year.
Alexandria stores a receipt as three things you can query separately:
Expenses and purchases land on your timeline under the moment of the transaction. A place does not: you did not do a restaurant on a date.
Expenses is a domain — a ready-made set of tables. In the app, open Settings → Domains and press Enable on Expenses. From the command line:
akb domain enable expenses
Afterwards they are ordinary tables. Rename them, add your own columns, filter and sort them like anything else. Enabling again writes nothing, and is also how a table you damaged gets repaired.
Open the Expenses table and press New receipt. The merchant, the date and the total are required — an expense with no cost cannot be compared to anything. Everything else is optional: subtotal, tax, tip, payment method, address, and a row per item you bought.
Amounts are typed the way you would say them, 36.00 EUR. They are stored as
exact decimals, never rounded, in a canonical form — so 36.00 EUR reads back as
EUR 36.
Check the vault before saving and Alexandria answers two questions without writing anything:
Attach photo archives the receipt image with it. The same photo attached twice is stored once.
akb rows Purchases --where "title~pizza" --sort unit_price
Pizza Margherita Pizzeria ai Baffetto — 2025-07-19 EUR 15 2 EUR 7.5
Pizza Margherita Pizzeria da Enzo — 2026-07-19 EUR 18 2 EUR 9
Money is compared exactly and only within one currency, so a euro total never sorts against a dollar one. See property types.
akb receipts commit takes a JSON draft you write by hand — editing the draft
is the review step, so a misspelled field is refused rather than silently
dropped.
akb receipts commit draft.json --image IMG_1234.jpg
{
"source": "manual",
"external_id": "rome-2026-07-19-enzo",
"transaction_datetime": "2026-07-19T20:35:00",
"merchant_name": "Pizzeria da Enzo",
"address": "Via dei Vascellari 29",
"city": "Roma",
"country": "IT",
"total": "36.00 EUR",
"payment_method": "card",
"purchases": [
{ "external_id": "l1", "name": "Pizza Margherita", "quantity": 2,
"unit_price": "9.00 EUR", "line_total": "18.00 EUR" }
]
}
source and external_id are the receipt's identity, so committing the same
pair again corrects the rows it already wrote instead of filing a second copy. It
corrects but never deletes: if you drop a line item and re-commit, the purchase
row from the earlier run stays until you delete it yourself.
Only total is required among the amounts. A receipt brings its tables with it,
so this works even before you have enabled the domain.
Nothing here is a separate database. An expense is a note; its values live in the table's frontmatter; the place and the photo are ordinary links. It stays plain Markdown you can read, edit, back up and sync like everything else.