Skip to content

Repository files navigation

TPT POA Registry NZ

A digital registry for managing Power of Attorney (POA) instruments under the New Zealand Protection of Personal and Property Rights Act 1988. Both the granter and attorney must hold a RealMe Verified identity.

License: MIT Go Next.js


Features

Feature Description
Digital POA Granting Create enduring, general, property, personal/welfare, or specific powers of attorney
RealMe Verified Identity Granter and attorney identities verified via RealMe SAML 2.0
Attorney Acceptance Workflow POAs start as pending_acceptance; attorneys receive a one-time link to accept or decline
QR Code Verification Active POAs generate a scannable QR code linking to the public verification page
Public Verification Page Anyone can verify a POA at /verify/:registryId — no login required
Full Audit Trail Every lifecycle event (grant, accept, decline, amend, revoke, view) is logged with timestamp and actor
Institution Verification API Banks and institutions verify POA validity via REST API with API-key auth and rate limiting
Bulk Verification Verify up to 100 registry IDs in a single API call
Webhook Notifications HMAC-SHA256-signed webhook delivery to subscribed institutions
Automatic Expiry Background job marks POAs expired hourly when their expiry date passes
Human-Readable IDs Sequential registry identifiers — e.g. POA-2026-00042

Architecture

┌─────────────────────────┐     ┌──────────────────────┐
│  Next.js Web Frontend   │     │  Institution Portal  │
│  (RealMe via backend)   │     │  (API Key auth)      │
└───────────┬─────────────┘     └──────────┬───────────┘
            │ HTTP (proxy /api/*)           │ HTTP
            ▼                              ▼
┌─────────────────────────────────────────────────────────┐
│                  Go API Server (:8082)                  │
│  ┌──────────┐ ┌────────────┐ ┌────────────────────────┐ │
│  │  Auth    │ │ POA CRUD + │ │  Institution Registry  │ │
│  │ Handlers │ │ Acceptance │ │  Bulk Verify + Webhook │ │
│  └────┬─────┘ └─────┬──────┘ └───────────┬────────────┘ │
│       │             │                     │              │
│  ┌────▼─────────────▼─────────────────────▼──────────┐  │
│  │                 Services Layer                     │  │
│  │    POAService · WebhookService · ExpiryJob        │  │
│  └────────────────────────┬───────────────────────────┘  │
│                           │                              │
│  ┌────────────────────────▼───────────────────────────┐  │
│  │              Repository Layer (pgx/pgxpool)        │  │
│  └────────────────────────┬───────────────────────────┘  │
└───────────────────────────┼──────────────────────────────┘
                            │
                     ┌──────▼──────┐
                     │ PostgreSQL  │
                     └─────────────┘

API Endpoints

RealMe Authentication

Method Path Description
GET /auth/login Initiate RealMe login
GET /auth/callback RealMe ACS callback
GET /auth/logout Clear session
GET /auth/metadata SP metadata XML
GET /auth/status Auth status (requires login)

POA Management (RealMe Verified required)

Method Path Description
POST /poa/grant Create new POA (returns acceptance token)
GET /poa/granted List POAs where user is granter
GET /poa/held List POAs where user is attorney
GET /poa/search?q=... Search POA records
GET /poa/registry/{registryId} Get POA by registry ID (e.g. POA-2026-00042)
GET /poa/{id} Get POA by UUID
PUT /poa/{id}/amend Amend POA scope/details
POST /poa/{id}/revoke Revoke POA
GET /poa/{id}/audit Audit log for POA
GET /poa/{id}/acceptance-link Get active acceptance link (granter only)

Attorney Acceptance (public — no auth)

Method Path Description
GET /poa/accept/{token} View POA invitation details
POST /poa/accept/{token} Accept POA — transitions status to active
POST /poa/decline/{token} Decline POA (with reason)

Institution Verification (API key required — X-API-Key header)

Rate limited to 100 requests / minute per API key.

Method Path Description
GET /api/v1/registry/lookup/{registryId} Look up POA details
GET /api/v1/registry/verify?registryId=...&granterName=... Verify POA validity
POST /api/v1/registry/verify/batch Bulk verify up to 100 registry IDs

Webhook Management (API key required)

Method Path Description
POST /webhook/subscribe Create webhook subscription
GET /webhook/subscriptions?institutionId=... List subscriptions

POA Lifecycle

[Grant]
   │
   ▼
pending_acceptance  ──accept──▶  active  ──amend──▶  amended
   │                               │                    │
   └──decline──▶  declined         └──revoke──▶  revoked
                                   │
                               (expiry date passes)
                                   │
                                   ▼
                                expired
Status Description
pending_acceptance POA granted; awaiting attorney acceptance
active Attorney accepted; POA is valid
amended Granter modified scope/details
declined Attorney declined the appointment
revoked Granter revoked a revocable POA
expired Past the specified expiry date (set automatically)
suspended Temporarily suspended by admin

Webhook Events

Webhooks deliver HMAC-SHA256-signed payloads to subscribed institution URLs via the X-TPT-Signature header.

Event Trigger
poa.granted New POA created (status: pending_acceptance)
poa.accepted Attorney accepted — POA now active
poa.declined Attorney declined
poa.amended Granter amended scope or details
poa.revoked Granter revoked the POA
* Subscribe to all events

Getting Started

Prerequisites

1 — Clone and configure

git clone https://github.com/tpt-nz/tpt-poa-registry-nz.git
cd tpt-poa-registry-nz
cp .env.example .env
# Edit .env with your values

2 — Database

# Start PostgreSQL (or use Docker: make dev)
createdb tptnz

# Run migrations
psql "$DATABASE_URL" -f migrations/001_init.sql
psql "$DATABASE_URL" -f migrations/002_attorney_acceptance.sql

3 — Start the Go API server

go mod tidy
go run ./cmd/server
# Listening on :8082

4 — Start the mock IdP (for local RealMe)

cd packages/realme-go
go run ./testenv/ -addr :8081

5 — Start the Next.js frontend

pnpm install
pnpm --filter web dev
# Listening on :3000

Docker (all-in-one)

make dev       # docker compose up -d (PostgreSQL + app)
make stop      # docker compose down
make migrate   # run Atlas migrations

Environment Variables

Go API Server

Variable Default Description
LISTEN_ADDR :8082 HTTP listen address
DATABASE_URL postgres://tptnz:tptnz_dev@localhost:5432/tptnz?sslmode=disable PostgreSQL DSN
REALME_ENVIRONMENT mts RealMe environment (mts | ite | production)
REALME_CERT_FILE certs/sp.crt SP certificate path
REALME_KEY_FILE certs/sp.key SP private key path
REALME_ENTITY_ID http://localhost:8082/auth/metadata SP entity ID
REALME_ACS_URL http://localhost:8082/auth/callback ACS callback URL
REALME_IDP_METADATA_URL http://localhost:8081/metadata IdP metadata URL
POA_API_KEY dev-api-key API key for institution endpoints

Next.js Frontend

Variable Description
BACKEND_URL Base URL of the Go API — server-side only, never sent to the browser
BACKEND_API_KEY API key for the server-side public verification page (matches POA_API_KEY)
NEXT_PUBLIC_SITE_URL Public URL of the frontend — used for generating absolute QR code URLs

RealMe Registration

MTS (Development / Mock IdP)

  1. Generate a self-signed SP 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"
  2. Start the mock IdP bundled in this repo:

    cd packages/realme-go
    go run ./testenv/ -addr :8081
  3. Set REALME_IDP_METADATA_URL=http://localhost:8081/metadata in your .env.

ITE (Pre-Production)

  1. Register at the RealMe Developer Portal.
  2. Submit your SP metadata (available at GET /auth/metadata) to DIA.
  3. DIA will provide the ITE IdP metadata URL.
  4. Use a properly named certificate: ite.{service-name}.{org-domain}.nz
  5. Set REALME_ENVIRONMENT=ite and update REALME_IDP_METADATA_URL.

Production

Same as ITE with REALME_ENVIRONMENT=production and the production IdP metadata URL from DIA.


Development

make test        # go test ./...
make test-race   # go test -race ./...
make lint        # golangci-lint run ./...
make vet         # go vet ./...
make build       # build binary to bin/
make tools       # install air, atlas, golangci-lint

Contributing

See CONTRIBUTING.md.


License

MIT — see LICENSE.

About

Digital Power of Attorney registry for New Zealand, compliant with the Protection of Personal and Property Rights Act 1988. RealMe Verified identity, attorney acceptance workflow, institution verification API, HMAC-signed webhooks, and public QR-code verification. Built with Go + Next.js.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages