tpt-tenant-verify-nz
GoTenant reference verification platform for New Zealand. Tenants build government-verified application packs using RealMe identity and share them with landlords. Go + Next.js 14 + PostgreSQL. Supports group applications, referee consent flows, and LINZ address autocomplete.
Languages
tpt-tenant-verify-nz
Tenant reference verification and rental application system for the New Zealand market. Landlords create rental listings; tenants build shareable verified packs using RealMe identity and submit them to listings directly or as a group.
Features
- RealMe identity — SAML 2.0 verified identity (MTS / ITE / Production)
- Application packs — Tenants compile a reference pack (personal info, address, employment, references) and share it via a unique token link
- Reference consent — Each referee receives a consent confirmation link before their details are shared
- Pack expiry — Packs expire after a configurable number of days (default 60); background sweep keeps data clean
- View tracking — Privacy-safe view count (SHA-256 hash of IP + User-Agent, no PII stored)
- Listings browse — Public listing board; tenants can apply directly with their pack
- Group applications — Multiple tenants (flatmates, couple) can link their packs into a group and apply together
- Address autocomplete — LINZ Data Service integration for NZ address search (optional, degrades gracefully)
- NATS events — Pack, application, and group lifecycle events published to JetStream (optional, no-op if unconfigured)
Architecture
┌─────────────────────────────────────────────────────┐
│ Next.js 14 Frontend │
│ (TypeScript, Tailwind CSS, App Router) │
└──────────────────┬──────────────────────────────────┘
│ /api/* → :8080
┌──────────────────▼──────────────────────────────────┐
│ Go 1.23 Backend (Chi) │
│ ┌────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Handlers │ │ Services │ │ Repository │ │
│ └─────┬──────┘ └──────┬──────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ RealMe SAML Pack / Group PostgreSQL 16 │
│ (SAML 2.0) / Listing svc (pgx v5) │
└──────────────────────┬──────────────────────────────┘
│
┌────────────┴──────────┐
▼ ▼
NATS JetStream LINZ Data Service
(optional events) (address autocomplete)
Tech Stack
| Layer | Technology | |---|---| | Backend | Go 1.23, Chi v5, pgx v5 | | Frontend | Next.js 14, TypeScript, Tailwind CSS 3 | | Database | PostgreSQL 16 | | Identity | RealMe SAML 2.0 (DIA) | | Events | NATS 2.10 JetStream (optional) | | Address | LINZ Data Service API (optional) |
Quick Start
Prerequisites
- Go 1.23+
- Node.js 20+ and npm
- Docker + Docker Compose
- RealMe certificates (dev uses the bundled mock IdP — no real certs needed)
1. Start infrastructure
make dev
# starts PostgreSQL 16, Redis 7, NATS 2.10
2. Apply database migrations
psql "postgres://tptnz:tptnz_dev@localhost:5432/tptnz?sslmode=disable" \
-f migrations/001_init.sql \
-f migrations/002_features.sql
3. Generate a dev certificate
mkdir -p certs
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout certs/sp.key -out certs/sp.crt \
-subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"
4. Start the mock RealMe IdP
cd packages/realme-go
go run ./cmd/mock-idp -addr :8081
5. Run the backend
go run ./cmd/server
# → http://localhost:8080
6. Run the frontend
cd web
npm install
npm run dev
# → http://localhost:3000 (proxies /api/* to :8080)
All-in-one (Docker Compose)
cp .env.example .env # fill in REALME_* paths
docker compose up
Environment Variables
| Variable | Default | Description |
|---|---|---|
| LISTEN_ADDR | :8080 | Server listen address |
| DATABASE_URL | postgres://tptnz:tptnz_dev@localhost:5432/tptnz?sslmode=disable | PostgreSQL connection string |
| NATS_URL | (empty — events disabled) | NATS server URL e.g. nats://localhost:4222 |
| LINZ_API_KEY | (empty — autocomplete disabled) | LINZ Data Service API key |
| PACK_EXPIRY_DAYS | 60 | Days before an application pack expires |
| REALME_ENVIRONMENT | mts | mts, ite, or production |
| REALME_CERT_FILE | certs/sp.crt | Path to SP certificate |
| REALME_KEY_FILE | certs/sp.key | Path to SP private key |
| REALME_ENTITY_ID | http://localhost:8080/auth/metadata | SAML entity ID |
| REALME_ACS_URL | http://localhost:8080/auth/callback | SAML assertion consumer URL |
| REALME_IDP_METADATA_FILE | (empty) | Local IdP metadata XML file |
| REALME_IDP_METADATA_URL | http://localhost:8081/metadata | IdP metadata URL (mock default) |
| COOKIE_DOMAIN | (empty) | Session cookie domain |
API Reference
Public (no authentication)
| Method | Path | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /listings/active | Browse active listings |
| GET | /address/suggest?q=... | NZ address autocomplete (LINZ) |
| GET | /consent/{token} | Referee consent confirmation page |
Authentication
| Method | Path | Description |
|---|---|---|
| GET | /auth/login | Initiate RealMe SAML login |
| GET | /auth/callback | RealMe SAML ACS callback |
| GET | /auth/logout | Clear session |
| GET | /auth/metadata | SAML SP metadata XML |
| GET | /auth/status | Current session status |
Tenant (requires RealMe Verified session)
| Method | Path | Description |
|---|---|---|
| POST | /application-packs | Create an application pack |
| GET | /packs/{token} | View a pack by shareable token |
| GET | /my-packs | List your packs |
| POST | /listings/{id}/applications | Apply to a listing with a pack |
Pack Groups
| Method | Path | Description |
|---|---|---|
| POST | /pack-groups | Create a group |
| GET | /pack-groups/{id} | Get group details and members |
| POST | /pack-groups/join/{token} | Join a group via invite token |
| POST | /pack-groups/{id}/invite | Regenerate invite token |
| POST | /pack-groups/{id}/apply | Submit group application to a listing |
Landlord (requires RealMe Verified session)
| Method | Path | Description |
|---|---|---|
| POST | /listings | Create a rental listing |
| GET | /listings | List your properties |
| GET | /listings/{id} | Get listing details |
| GET | /listings/{id}/applications | View applications for a listing |
| PATCH | /listings/{id}/applications/{appId} | Approve or reject an application |
RealMe Setup
MTS — Local Development (no registration required)
# Start the bundled mock IdP
cd packages/realme-go && go run ./cmd/mock-idp -addr :8081
# Backend picks it up automatically via the default REALME_IDP_METADATA_URL
ITE / Production
- Register a Service Provider at the RealMe Developer Portal
- Submit your SP metadata XML (
GET /auth/metadata) to DIA - DIA provides IdP metadata URL and environment-specific certificate requirements
- Set
REALME_ENVIRONMENT=ite(orproduction) and pointREALME_IDP_METADATA_URLto the DIA-provided URL
Database Migrations
Migrations are plain SQL files in migrations/. Apply in order:
# 001 — core schema (packs, references, listings, applications)
psql "$DATABASE_URL" -f migrations/001_init.sql
# 002 — feature additions (expiry, view tracking, consent tokens, pack groups)
psql "$DATABASE_URL" -f migrations/002_features.sql
Using Atlas:
make migrate DATABASE_URL="$DATABASE_URL"
Testing
# Backend
go test ./...
go test -race ./...
# Frontend (type-check + build)
cd web && npm run build
Repository Layout
.
├── cmd/server/ — main entrypoint
├── internal/
│ ├── events/ — NATS publisher
│ ├── handlers/ — HTTP handlers (+ tests)
│ ├── models/ — domain types
│ ├── repository/ — PostgreSQL queries
│ └── services/ — business logic
├── migrations/ — SQL migration files
├── packages/
│ ├── nz-common/ — shared NZ utilities (LINZ, MBIE, money, health)
│ └── realme-go/ — RealMe SAML 2.0 provider library
├── scripts/db/ — Docker init SQL
└── web/ — Next.js 14 frontend
├── app/ — App Router pages
└── components/ — shared React components
Contributing
See CONTRIBUTING.md.
Security
See SECURITY.md for responsible disclosure information.
License
MIT — © 2026 TPT NZ Public Contributors