A shared-expense tracker for people who already trust each other. Put a group together, log what everyone spent, and Zahlbob works out who owes what — netted down to the fewest transfers that square the group up, or left un-netted if you'd rather the payments match the debts people actually remember.
See docs/project.md for the motivation and design
decisions behind it.
docs/frontend.md.docs/backend.md.Prerequisites are managed via mise.
just setup # install required tools via mise
just start infra # start local infra (Postgres, Zitadel, Caddy) via podman compose
just start migrations # apply database migrations
Infra takes a bit to become healthy (Zitadel in particular) — wait and retry if the next step fails immediately.
Fresh infra needs a Project + OIDC Application (for a client_id) and at
least one login account before the app is actually usable - none of that
exists until you run:
just setup zitadel # or: just setup zitadel 192.168.50.82 for infra-devbox
This creates the app registration (writing client_id to
app/client/public/env.js, gitignored, picked up automatically by
app.config.ts - see index.html's env.js comment) and self-registers
dev@md.local / _Password1 (for you).
Run the backend and frontend in separate terminals:
just start backend # cargo run -p zahlbob, raw HTTP on 0.0.0.0:5421
just start frontend # ng serve, raw HTTP on 0.0.0.0:5431
Both run as plain native processes with no TLS of their own - Caddy is what
terminates HTTPS and proxies to them (host.containers.internal:5421/:5431,
see config/infra/caddy/Caddyfile), so the app itself is browsed at
https://localhost:5430, not :5431. The raw ports still work directly
over http://localhost:5421/:5431 for local-only debugging (e.g. the
"ng serve" launch config in .vscode/launch.json) - that's fine since
localhost is a secure context on its own, but don't use the raw ports from
another machine, since that's exactly the insecure-context case Caddy exists
to avoid.
just test # see config/scripts/test.just for available targets
app/ — application entry points (e.g. the backend server binary)domain/ — vertical slices (auth, group, landing, shared, tracing),
each with its own frontend/, backend/, and api/ as applicabledocs/ — architecture and design documentationconfig/ — local dev tooling (just recipes, infra compose files)1 commits
Rust
83.5%
TypeScript
12.7%
SCSS
1.9%
HTML
1.2%
A shared-expense tracker for people who already trust each other. Put a group together, log what everyone spent, and Zahlbob works out who owes what — netted down to the fewest transfers that square the group up, or left un-netted if you'd rather the payments match the debts people actually remember.
See docs/project.md for the motivation and design
decisions behind it.
docs/frontend.md.docs/backend.md.Prerequisites are managed via mise.
just setup # install required tools via mise
just start infra # start local infra (Postgres, Zitadel, Caddy) via podman compose
just start migrations # apply database migrations
Infra takes a bit to become healthy (Zitadel in particular) — wait and retry if the next step fails immediately.
Fresh infra needs a Project + OIDC Application (for a client_id) and at
least one login account before the app is actually usable - none of that
exists until you run:
just setup zitadel # or: just setup zitadel 192.168.50.82 for infra-devbox
This creates the app registration (writing client_id to
app/client/public/env.js, gitignored, picked up automatically by
app.config.ts - see index.html's env.js comment) and self-registers
dev@md.local / _Password1 (for you).
Run the backend and frontend in separate terminals:
just start backend # cargo run -p zahlbob, raw HTTP on 0.0.0.0:5421
just start frontend # ng serve, raw HTTP on 0.0.0.0:5431
Both run as plain native processes with no TLS of their own - Caddy is what
terminates HTTPS and proxies to them (host.containers.internal:5421/:5431,
see config/infra/caddy/Caddyfile), so the app itself is browsed at
https://localhost:5430, not :5431. The raw ports still work directly
over http://localhost:5421/:5431 for local-only debugging (e.g. the
"ng serve" launch config in .vscode/launch.json) - that's fine since
localhost is a secure context on its own, but don't use the raw ports from
another machine, since that's exactly the insecure-context case Caddy exists
to avoid.
just test # see config/scripts/test.just for available targets
app/ — application entry points (e.g. the backend server binary)domain/ — vertical slices (auth, group, landing, shared, tracing),
each with its own frontend/, backend/, and api/ as applicabledocs/ — architecture and design documentationconfig/ — local dev tooling (just recipes, infra compose files)1 commits
Rust
83.5%
TypeScript
12.7%
SCSS
1.9%
HTML
1.2%