cofr/
├── apps/
│ ├── server/ # FastAPI backend (Python, internal port 5784)
│ └── client/ # Web dashboard (React Router 7 / Bun, internal port 3000)
├── infra/ # Docker Compose, Caddyfiles, Cloudflare Tunnel config
└── scripts/ # Dev/prod setup helpers
- Docker & Docker Compose
- Environment files: copy
.env.examplevalues intoapps/server/.env,apps/client/.env - Generate a Fernet encryption key for the server:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
./scripts/setup_dev.shDev entrypoints:
- App:
http://localhost:8080 - API:
http://localhost:8080/api - API health:
http://localhost:8080/health
OAuth in Docker dev uses the public dev API URL from apps/server/.dev.env:
API_URL=http://localhost:8080/api- Google redirect URI:
http://localhost:8080/api/auth/oauth/google/callback
./scripts/setup_prod.sh| Service | Tech | Port | Description |
|---|---|---|---|
| server | Python / FastAPI | 5784 | REST API for expenses, auth, accounts |
| client | TypeScript / React Router 7 / Bun | 3000 | Web dashboard with SSR |
| caddy | Caddy 2 | 8080 in dev, 80/443 in prod | Reverse proxy (/api/* → server, /* → client) |
| cloudflared | Cloudflare Tunnel | — | Exposes services at cofr.cash |
All services share a PostgreSQL database, running as a Docker Compose service.
Connection configured via DATABASE_URL in each app's .env.
- UUID primary keys prevent ID enumeration
- PII encrypted at rest (Fernet symmetric encryption)
- OAuth email auto-linking disabled
- CORS restricted to
FRONTEND_URL