**Property management SaaS for independent landlords.** Track properties, tenants, rent, maintenance, leases, and expenses — all in one clean dashboard.
Built with Next.js 16, PostgreSQL (Drizzle ORM), Better Auth, Stripe, and OpenAI. Deploys to DigitalOcean App Platform (see [DIGITALOCEAN.md](DIGITALOCEAN.md)).
The schema is managed with Drizzle ORM (see `drizzle.config.ts`). Point `DATABASE_URL` at your PostgreSQL instance in `.env.local`, then apply the migrations from `lib/db/migrations`:
```bash
npm run db:migrate
```
To regenerate migrations after changing the schema, use `npm run db:generate`. For quick local prototyping you can push the schema directly with `npm run db:push`.
Add your API keys (`STRIPE_SECRET_KEY`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`) — that's it. Products and prices are resolved by stable **lookup keys** and auto-created on first checkout (Pro $29/mo, Landlord $59/mo, Lifetime $199, plus annual), so there are **no price IDs to configure** and going live is just an API-key swap. To pre-create the catalog, optionally run `node scripts/stripe-setup.mjs`.
Use any SMTP provider (e.g. SMTP2GO). Verify your sending domain with the provider, then set `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, and `EMAIL_FROM`.
The repo ships a production `Dockerfile` (Next.js standalone output), an App Platform spec at [`.do/app.yaml`](.do/app.yaml), DO Functions cron under [`functions/`](functions/), and a `/api/health` liveness probe. See **[DIGITALOCEAN.md](DIGITALOCEAN.md)** for the full walkthrough: build/push the image to DOCR, create the app, wire up Managed Postgres + Spaces, and deploy the scheduled cron functions.
Data isolation is enforced in the application layer: every API route authenticates via `getSessionUser()` and scopes its queries by `user_id`. There is no database-level RLS, so this query scoping must be maintained carefully on every new route and query.
---
## Cron jobs
| Job | Schedule | What it does |
|-----|----------|--------------|
| Rent reminders | Daily 9am UTC | Marks overdue payments, sends 3-day reminder emails |
| Lease expiry | Daily 10am UTC | Sends 60/30/7-day expiry alerts to landlord |
Cron routes are protected with `CRON_SECRET` (Bearer token in `Authorization` header).