Skip to content

Repository files navigation

Utility Kitty 🐈‍⬛

A tiny shared utility tracker for roommates.

Utility Kitty makes it easy for housemates to manage their monthly utilities:

  1. Add the bills.
  2. Split the total.
  3. Collect everyone’s share.
  4. Pay the providers.
  5. Kitty is full.

The problem

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

Status

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

How it works

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.

Stack

  • Django
  • PostgreSQL
  • Server-rendered HTML
  • Render

Why this exists

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.

Development

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 test

The 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

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:8000

make 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.

Render

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.

Delivery workflow

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.

About

A tiny shared utility tracker for roommates. Add the bills, split the total, and feed the kitty. 🐈‍⬛

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages