- Rust 97%
- Shell 2%
- Docker <1%
- Ruby <1%
- CSS <1%
- 1 more language <1%
Coffee Money Pool Tool #
This is a tool to manage a coffee money pool (Kaffeekasse). It can either run locally or on a server.
Deployment #
A ready-to-use docker-compose.yml is provided. It builds the
server image from this repository, exposes the server on port 8080, and persists the
SQLite database and the session secret in a named coffee-data volume. Start it with:
docker compose up -d
Pass extra server options through the commented-out command: block in docker-compose.yml,
for example:
command:
- --ldap-url=ldap://ldap.example.org
- --ldap-search-base=ou=people,dc=example,dc=org
- --ldap-bind-dn=cn=coffee,ou=services,dc=example,dc=org
- --path-prefix=/coffee/
For more information checkout the HTML documentation which can be found after
starting the server. With the --path-prefix example above it will be at
http://localhost:8080/coffee/.
The first POST /tokens request against an empty database automatically creates a superuser
using the supplied credentials. With LDAP configured, the password is verified through LDAP
first and no local hash is stored; without LDAP (or when LDAP does not know the user), an
Argon2 hash is stored locally. Subsequent superusers can only be granted via direct SQL
(UPDATE accounts SET superuser = TRUE WHERE name = ?).
Authentication methods #
Each account's authentication methods are stored as explicit rows in the
account_identities table (method is one of password, ldap, oidc):
- password — the
secretcolumn holds an Argon2 hash, verified atPOST /tokens. - ldap — credentials are bind-checked against the configured LDAP server.
- oidc — OpenID Connect. Providers are registered at runtime with
coffee auth oidc add <slug> <issuer-url> <client-id>(admin-only, server must run with TLS) and take effect without a restart. List withcoffee auth oidc list, remove withcoffee auth oidc remove <slug>. A browser uses the authorization-code flow atGET /api/auth/oidc/<slug>/login; the CLI uses the device authorization grant (setauth-method: oidcin the client config, orcoffee config --auth-method oidc).
OIDC account resolution is subject-first: a returning user is matched by the
stable (issuer, subject) pair recorded in account_identities, so an email
change at the IdP does not lock them out. The email claim is only used on the
first-ever login to find and link the local account — set accounts.email for
each user that should be able to log in via OIDC (existing accounts have no email
until it is backfilled). Authorization is unchanged: the superuser flag stays a
local DB flag regardless of how the user authenticated.
The client secret of an OIDC provider is encrypted at rest with the server
secret file. Rotating that file makes stored provider secrets undecryptable —
the affected provider is logged and skipped at startup and must be re-added.
Development Testing #
Prerequisites #
Coffee uses SQLite for local storage and can operate in two modes:
- Standalone mode: Direct SQLite database access
- Client/Server mode: HTTP client communicating with a server
Configuration Files #
On macOS, configuration files are stored in:
- Config:
~/Library/Application Support/dev.coffee.coffee/config.yml - State (JWT tokens):
~/Library/Caches/dev.coffee.coffee/state.yml - Data:
~/Library/Application Support/dev.coffee.coffee/coffee.sqlite
On Linux:
- Config:
~/.config/coffee/config.yml - State:
~/.cache/coffee/state.yml - Data:
~/.local/share/coffee/coffee.sqlite
Testing Standalone Mode #
To test locally without interfering with production configuration, use the --db-path flag to specify a separate test database:
# Use a test database
cargo run -- --db-path /tmp/test-coffee.sqlite add alice bob
cargo run -- --db-path /tmp/test-coffee.sqlite balances
cargo run -- --db-path /tmp/test-coffee.sqlite cash -i 10 alice
Important: When --db-path is specified, the tool runs in standalone mode and ignores any server-url in your config file.
Testing Client/Server Mode #
The Justfile provides ready-made commands for local development. TLS certificates are generated automatically on first run (idempotent).
1. Start a local test server #
just run-server
The server will:
- Generate dev TLS certificates in
certs/if not already present - Create a test database at
/tmp/coffee-test-server/test.sqlite - Generate a secret key at
/tmp/coffee-test-server/secret - Listen on
https://127.0.0.1:18080with mTLS - Provide a health check at
https://127.0.0.1:18080/healthy
2. Create the first superuser #
On a fresh database the server bootstraps the first superuser automatically. Simply log in with the desired credentials:
just run --user admin sip admin
# The CLI will prompt for a password; that password is stored as the admin's Argon2 hash.
Or with curl directly:
curl -s -X POST https://127.0.0.1:18080/api/tokens \
--cacert certs/ca_cert.pem \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"mypassword"}'
# Returns a JWT on success.
Manual fallback — if you need a deterministic hash or are recovering a database,
you can insert the account directly via SQL. Generate a hash with the argon2 CLI tool:
HASH=$(echo -n "mypassword" | argon2 $(openssl rand -base64 16) -id -e)
sqlite3 /tmp/coffee-test-server/test.sqlite <<EOF
INSERT INTO accounts (name, superuser) VALUES ('admin', 1);
INSERT INTO account_identities (account_id, method, secret)
VALUES ((SELECT id FROM accounts WHERE name = 'admin'), 'password', '$HASH');
INSERT INTO balances (account_id, site_id)
VALUES ((SELECT id FROM accounts WHERE name = 'admin'),
(SELECT id FROM sites WHERE name = '/'));
EOF
Note: Each account must have a balances row for each site it participates in.
The login query checks for an active balance to confirm the account is configured.
3. Run CLI commands against the dev server #
just run run --user alice sip alice
The run recipe isolates config and token cache under /tmp/coffee-test-client/ and embeds the dev CA certificate so TLS is trusted automatically.
Environment Variables #
These environment variables are recognised by the CLI:
COFFEE_URL: Override server URL (highest priority, overrides config file)COFFEE_CONFIG_DIR: Override config directory location (storesconfig.yml)COFFEE_DATA_DIR: Override data directory location (storescoffee.sqlite, standalone mode only)COFFEE_CACHE_DIR: Override cache directory location (storesstate.ymlwith JWT tokens)COFFEE_LOG: Log filter for the CLI (e.g.COFFEE_LOG=coffee=debug)
The server uses RUST_LOG for its log filter and additionally supports COFFEE_DATA_DIR and COFFEE_SECRET_PATH.
Running Tests #
# Run all tests
cargo test --all-features
# Run specific test
cargo test test_name --all-features -- --nocapture
# Check compilation after dependency updates
cargo check
cargo check --all-features
Building #
Build the server image with:
just docker-build
This runs docker buildx build --platform linux/amd64 -t coffee-server:latest --load ..
Adjust the PLATFORM variable in the Justfile if you build for a different architecture.
Roadmap #
In order of importance.