A tiny shared utility tracker for roommates.
Utility Kitty makes it easy for housemates to manage their monthly utilities:
- Add the bills.
- Split the total.
- Collect everyone’s share.
- Pay the providers.
- Kitty is full.
Managing shared utilities usually means repeating the same awkward process every month: gather the bills, calculate what everyone owes, collect the money, and keep track of what has actually been paid.
Utility Kitty provides one simple place to see:
- This month’s utility bills
- How much each roommate owes
- Who has contributed
- Which providers have been paid
- Whether the kitty is full
Utility Kitty is currently under development. The public landing page and invite-only account/household foundation are implemented; utility tracking is next. Follow the MVP tracking epic.
The planned MVP splits bills equally among active household members, initially for:
- Water and sewage
- Electricity
- Internet
A billing month moves through four states:
- Draft — bills are still being entered
- Collecting — the kitty is waiting for contributions
- Ready to pay — everyone has contributed
- Paid — all utility providers have been paid
Utility Kitty tracks payments; it does not process or transfer money. Roommates can continue using Venmo, Zelle, Apple Cash, or whatever method they already prefer.
- Django
- PostgreSQL
- Server-rendered HTML
- Render
Utility Kitty began as a small tool for solving a real household annoyance—and as an opportunity to build and operate a complete Django application on Render.
The goal is intentionally modest: make shared utility bills boring again.
Install uv and Python 3.13
(uv can provision Python). Exact dependencies are committed in uv.lock.
uv sync --locked
DJANGO_DEBUG=true uv run python manage.py migrate
make run
# Open http://127.0.0.1:8000/
make check testThe app uses Django templates, local CSS, WhiteNoise, Gunicorn, and Django's
database-backed sessions. Local development defaults to SQLite; set DATABASE_URL
for PostgreSQL. The first migrations establish the custom email-based user model,
households, and memberships. Never swap in Django's default user model or edit
applied migrations.
Environment variables are read from the process; .env files are not loaded
automatically. Never commit secrets.
| Variable | Purpose |
|---|---|
DJANGO_DEBUG |
Set to true only for local development; defaults to false. |
DJANGO_SECRET_KEY |
Required secret in production; local debug uses a development-only fallback. |
DJANGO_ALLOWED_HOSTS |
Comma-separated hostnames, without schemes. Required in production outside Render. |
DJANGO_CUSTOM_DOMAINS |
Optional comma-separated custom domains; defaults to utilitykitty.com,www.utilitykitty.com. |
RENDER_EXTERNAL_HOSTNAME |
Render adds its service hostname to allowed hosts automatically. |
PORT |
Listening port in the Render start command. |
DATABASE_URL |
PostgreSQL URL in production; defaults to local SQLite only in debug. |
ACCOUNT_ACCESS_ENABLED |
Defaults to debug's value; explicitly false on Render until launch prerequisites are complete. |
PUBLIC_ORIGIN |
Canonical origin for reset emails, e.g. https://your-service.onrender.com. Must be an allowed HTTPS host for production account access. |
DJANGO_CSRF_TRUSTED_ORIGINS |
Optional comma-separated trusted HTTPS origins; normally unnecessary for same-origin forms. |
EMAIL_HOST, EMAIL_PORT |
SMTP server and STARTTLS port (default 587). |
EMAIL_HOST_USER, EMAIL_HOST_PASSWORD |
SMTP credentials supplied through the environment. |
DEFAULT_FROM_EMAIL |
Verified sender address; required for production account access. |
Production allows utilitykitty.com, www.utilitykitty.com, and
utilitykitty.onrender.com by default. DJANGO_CUSTOM_DOMAINS and
RENDER_EXTERNAL_HOSTNAME remain environment overrides for other domains.
Production uses Resend's SMTP endpoint by default (smtp.resend.com) with the
sender Utility Kitty <notifications@utilitykitty.com>. Set
EMAIL_HOST_PASSWORD to the Resend SMTP token only in the Render environment;
never commit it.
Production enables HTTPS redirects, secure-cookie settings, HSTS, clickjacking
protection, and MIME sniffing protection. Only /health/ is exempt from HTTPS
redirects for Render probes; it returns just {"status":"ok"}. The proxy SSL
header assumes a trusted TLS-terminating proxy such as Render. Do not expose the
application server directly to the internet.
To exercise the production server locally (HTTP development configuration):
DJANGO_DEBUG=true uv run python manage.py collectstatic --noinput
DJANGO_DEBUG=true uv run gunicorn utilitykitty.wsgi:application --bind 127.0.0.1:8000make check runs Ruff, Django system checks, missing-migration checks, and strict
deployment checks with non-secret, production-style test configuration. make test
runs the complete automated suite. CI runs both and verifies static collection.
After the PR is merged, create a Blueprint from this repository and review
render.yaml. It defines one free Python web service on main, locked dependency
installation, static collection, Gunicorn, a generated secret key, and /health/.
The start script applies committed migrations if DATABASE_URL is present, then
starts workers. Back up any existing database and inspect migrate --plan before
deploying migrations. There is no automatically provisioned database yet. The
Blueprint does not configure DNS or a custom domain. Review service costs and
settings before applying it.
After deployment, visit the assigned HTTPS onrender.com URL, check the landing
page and account-unavailable state, and confirm /health/ returns {"status":"ok"}.
Check static CSS loads and HTTP redirects to HTTPS. Real accounts and household
data must wait for authentication, PostgreSQL, privacy, and production-readiness
work in the epic. See account operations for the invitation,
authorization, email, and production launch procedure.
The public footer and account-entry pages link to /privacy/. The supplied
operator, contact, Resend, Cloudflare, and retention details are configured as
environment-overridable defaults. See privacy configuration
for the required PRIVACY_* variables and review process. Production account
access remains disabled until email delivery, the privacy contact address, and
the production database/recovery configuration are verified.
Each child issue gets a focused feat/<issue>-<description> branch and PR.
Dependent PRs may target the preceding slice's branch so each diff stays focused;
review and merge them sequentially, retargeting the next PR to main. Each PR
records validation and deployment considerations. Use a separate worktree for any
concurrent task. The epic tracks the release; an open PR does not mark a slice done.