Uses Plaid to fetch info from your bank(s), and store balances in an encrypted database. Includes a rudimentary frontend with Plotly to show a pretty graph.
-
Put your Plaid credentials in your Bitwarden vault. It needs fields named
pubkey
,clientid
, andsecret
. -
Generate a sufficiently long random string and store it in your Bitwarden vault under URI
money.r0k
. This will be used to encrypt the sqlite database. -
The plaid quickstart is a submodule of this repo. Clone it by running:
git submodule init
git submodule update
-
Run
bin/quickstart.sh
to get your bank keys. -
Initialize the database. This will be encrypted after the first run.
sqlite3 money.db
CREATE TABLE IF NOT EXISTS keys (
name TEXT NOT NULL PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS accounts (
id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
type TEXT NOT NULL,
subtype TEXT NOT NULL,
key_name TEXT NOT NULL,
FOREIGN KEY (key_name) REFERENCES keys(name)
);
CREATE TABLE IF NOT EXISTS cur (
account_id TEXT NOT NULL,
amount INTEGER NOT NULL,
at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (account_id) REFERENCES accounts(id)
);
CREATE TABLE IF NOT EXISTS avail (
account_id TEXT NOT NULL,
amount INTEGER NOT NULL,
at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (account_id) REFERENCES accounts(id)
);
CREATE TABLE IF NOT EXISTS lim (
account_id TEXT NOT NULL,
amount INTEGER NOT NULL,
at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (account_id) REFERENCES accounts(id)
);
- Add your plaid keys to the database:
INSERT INTO keys (name, value) VALUES
('bank1','access-123'),
('bank2','access-456'),
('bank3','access-789');
-
Start the server:
bin/money.sh
-
Visit http://127.0.0.1:8888/refresh to get the latest balances and http://127.0.0.1:8888/ to see the pretty graph
After some time you might see an error like this:
plaid.errors.ItemError: the login details of this item have changed (credentials, MFA, or required user action) and a user login is required to update this information
Run bin/quickstart.sh
again to get a new key.
Then, while mon.py is running (so the database can be decrypted), use sqlite to
update the key for <bank>
:
UPDATE keys set value = '<access_token>' WHERE name = '<bank>';
Get the old item ID. We want to replace it with the new ID, or our charts will not look right.
SELECT id FROM accounts WHERE key_name = '<bank>';
And then update the balance tables (avail
, cur
, and lim
(cur
is the only
table currently used, but might as well update the other as well, maybe we will
use them one day ¯\_(ツ)_/¯ )):
UPDATE avail SET account_id = '<new_id>' WHERE account_id = '<old_id>';
UPDATE cur SET account_id = '<new_id>'WHERE account_id = '<old_id>';
UPDATE lim SET account_id = '<new_id>' WHERE account_id = '<old_id>';
Then you probably want to delete the old unused account key:
DELETE FROM accounts WHERE id = '<old_id>';
- make this easier to update :)