This is web application for tracking shared finances within a small, mutually trusting group.
You will need:
To build and self-test the software:
$ git clone https://github.com/ewxrjk/bank.git
$ cd bank
$ make
To install the bank
command in /usr/local/bin
:
$ sudo make install
Bank uses Sqlite as its database, meaning that all data is stored in a single file. This file must have read-write permission for the server.
To create an empty database,
supposing that it is called bank.db
and located in the current directory:
$ bank -d bank.db init
You will need to create an initial user:
$ bank -d bank.db user add rjk
Enter password:
Confirm password:
You can create additional users at this stage, or via the web interface.
Passwords may subsequently be changed either with bank user pw
or via the web interface.
You can set the name of the bank. This is used in the web interface.
$ bank -d bank.db config set title 'Example Bank'
To run a server without TLS:
$ bank -d bank.db server --address :8080
You can use the --lifetime
option to set the maximum caching period for static content
(which currently means the CSS and JavaScript).
The default is 60 seconds.
For a production system, where these files may be unchanged for weeks or months on end,
a larger value may be suitable.
You can use the --cert
and --key
options to specify a TLS certificate and key.
Let's Encrypt is the best way to acquire a certificate,
but you can get started with a self-signed certificate as follows:
$ openssl req -new -newkey rsa:2048 -x509 -nodes -subj /CN=www.example.com -keyout key.pem -out cert.pem
$ bank -d bank.db server --address :8080 --cert cert.pem --key key.pem
I run the service as its own user/group:
# useradd -Urmd/var/lib/bank bank
# su -lcbash bank
$ bank init
$ bank user add rjk
Enter password:
Confirm password:
Administrative users may be added to the bank
group.
To run it as a daemon,
edit bank.service
and install and enable it:
# install -m644 bank.service /usr/local/lib/systemd/system/bank.service
# systemctl enable --now bank.service
Created symlink /etc/systemd/system/multi-user.target.wants/bank.service → /usr/local/lib/systemd/system/bank.service.
I front-end the service with Apache, so I can take care of TLS in a uniform way with other services. The following directives forward requests to the service:
ProxyPass "/" "http://localhost:8344/"
ProxyPassReverse "/" "http://localhost:8344/"
mod_proxy
and mod_proxy_http
must be enabled.
A typical setup would have:
- an account for each user, reflecting how much they are owed
- an account called
house
reflecting how much the collective of users are owed. Normallyhouse
would have a negative balance, reflecting that the collective owes money to its individual members.
The common actions in this model are:
- payments from
house
to some individual's account, reflecting that the individual has made a payment on behalf of the collective. This can be done via the New Transaction page, or via the form on the front page. - distribution of the
house
balance among the other accounts, reflecting that the collective's obligations full upon to its members. This can be done via the Distribute page. - payments between individual users, reflecting offline resolution of these obligations. This can be done via the New Transaction page, or via the form on the front page.
Suppose the users are fred
and bob
, and they are populating their kitchen.
fred
buys a toaster for £20. He fills in the details of this real-life transaction on the front page, causing a payment fromhouse
tofred
of £20 to be recorded.bob
buys a microwave oven for £40. He fills in the details on the front page, causing a payment fromhouse
tobob
of £60 to be recorded.fred
now has a balance of £20 andbob
of £40.house
has a balance of -£60.- Any user uses the Distribute page
to distribute from
house
tofred
andbob
. This divides its balance into two and transfers each half to one of the human users. The effect is thatfred
has a balanced of -£10 andbob
of £10. fred
owes £10, andbob
is owed £10, so (in real life) Fred hands Bob £10.- Either user enters the details of this real-life transaction via the front page,
causing a payment of £10 from
bob
tofred
to be recorded, leaving each with a balance of £0.
All legitimate users are treated equally and can change one another's passwords, enter transactions on behalf of each other, etc.
Passwords are obscured with scrypt. The parameters are stored in the database to make changing them easy. Please see the source code for the current default parameters.
The server is implemented in Go, a memory-safe language. Values are substituted into HTML pages using html/template and into database queries using placeholder parameters as described in the database/sql API.
User logins are tracked with a cookie and an associated token. The token is embedded into HTML pages and must be presented with all mutating operations. Non-mutating requests require only the cookie.
Usernames and session IDs are only ever logged by the server if the --debug
option is used.
The JavaScript UI uses jQuery heavily. It constrains the jQuery code using sub-resource integrity.
Release tags are signed by PGP key 9006B0ED710DB81B76D368D9D3388BA18A741BEF.