Local Install — Staff Portal API

Step-by-step guide to run openg2p-registry-staff-portal-api on a local developer machine. Includes every fix needed beyond the upstream README, with macOS / Apple Silicon notes called out explicitly.

Why this guide exists

The upstream openg2p-registry-staff-portal-api repo has a one-line README. Getting it running locally requires a chain of repos installed in the right order, two Postgres extensions enabled, two cluster services port-forwarded, and a couple of macOS-specific quirks worked around. This page captures the full path so you don't have to rediscover any of it.

By the end you will have:

  • The Staff Portal API running on http://localhost:8001

  • /ping returning pong

  • Swagger UI at http://localhost:8001/docs

  • Cluster Keycloak and IAM port-forwarded to your laptop

  • A local Postgres holding registry_db + openg2p_gen2_master_data_db

You will not install MinIO. The document/template endpoints that need it are out of scope for the audit integration smoke test.


Prerequisites

Tool
Notes

Python 3.11

Tested on 3.11.9 (Homebrew on Apple Silicon). 3.12 may work but isn't tested here.

Docker

For the local Postgres container only.

kubectl configured against the cluster running OpenG2P

Used for port-forwarding Keycloak + IAM.

Read access to Keycloak admin in the cluster

Needed to fetch the client secret and confirm a test user.

Decisions to confirm before you start (you'll need them in Phase 4):

  • Cluster namespace and Service name of Keycloak (often commons-keycloak)

  • Cluster namespace and Service name of the IAM service (the auth_provider_api_url — exposes /user-access/get_permissions_for_roles)

  • Keycloak realm name + client_id used by the staff portal

  • A test user (and password) with at least one role on that client


Phase 1 — Python venv + install local repo chain

The order matters because the dependency graph is bottom-up.

Set a path variable to keep the install commands short:

Install in this exact order:

Server runtime + missing transitive dep:

Why farmer-extension is installed without -e

The farmer extension's pyproject.toml uses a hatchling source-rename trick so any concrete extension (farmer / family / NSR) exposes itself under the generic name openg2p_registry_extensions:

This rename only kicks in for wheel builds, not for pip install -e (editable mode keeps the original directory name). If you install editable, you'll get ModuleNotFoundError: No module named 'openg2p_registry_extensions' on migrate. Plain pip install <path> builds the wheel and applies the rename correctly.

If you ever need to edit the farmer extension code, re-run pip install <path> from that directory after each change.

macOS / Apple Silicon notes

  • zsh: no matches found: uvicorn[standard] — zsh treats [...] as a glob. Single-quote the spec: pip install 'uvicorn[standard]'. Or permanently fix in ~/.zshrc:

  • greenlet missing — pip on Apple Silicon + Python 3.11 sometimes doesn't pull greenlet in transitively from SQLAlchemy's async extra. Listed above explicitly so you don't have to discover this via the migrate stack trace.


Phase 2 — local Postgres for the two registry databases

Required Postgres extensions

The migration creates a trigram-based GIN index on intake form sections; that needs pg_trgm. Enable it (and a couple of others the registry uses in places) before running migrate:

(Skipping this step gets you the error: operator class "gin_trgm_ops" does not exist for access method "gin".)

The master_data_db can stay empty for now — it's seeded by a separate service (master-data-service) that we don't need for the audit smoke test. Calls that touch it will fail gracefully; other endpoints work.


Phase 3 — port-forward cluster services

Find the actual service names first:

Then keep two terminals running:

Verify both are responding:


Phase 4 — .env configuration

Edit .env:

Two values you must fill in:

  • <actual-client-id> — Keycloak admin → Clients → your client → Settings

  • <realm> — visible at the top-left of the Keycloak admin UI

Note: client_secret is not needed in .env. Staff Portal API is a resource server — it only validates incoming JWTs against the realm's public keys (via the OIDC discovery URL). The secret is only needed in Phase 6 to fetch a token, and even then only if the client is configured as confidential.

macOS / zsh note — loading the .env

You don't need to export the variables manually. Pydantic-settings reads .env from the current working directory automatically (configured in config.py). Just cd ~/sp-local before running and it picks them up.

If you do want them in your shell, don't use:

Use the set -a auto-export pattern instead:


Phase 5 — migrate + run

Migration output is verbose JSON-formatted log lines. The "Worker ID -1. Docker Pod ID" lines repeated many times are normal startup chatter from the controller post_init() chain — not errors. Wait for the process to exit cleanly. If it errors with the gin_trgm_ops message, you skipped the extension step in Phase 2.

Start the server:

Wait for:


Phase 6 — verification

In another terminal:

Both should work. At this point the service is up and you're ready to:

  • Fetch a Keycloak token (next step — see your runbook)

  • Add the AuditMiddleware (covered in upcoming sub-pages)


Common failures and fixes

Symptom
Cause
Fix

ModuleNotFoundError: No module named 'openg2p_registry_extensions'

Farmer extension installed editable

pip uninstall -y openg2p-registry-farmer-extension && pip install <path> (no -e)

ValueError: the greenlet library is required to use this function. No module named 'greenlet'

Missing transitive dep

pip install greenlet

operator class "gin_trgm_ops" does not exist for access method "gin"

pg_trgm extension not enabled

CREATE EXTENSION pg_trgm on registry_db

zsh: no matches found: uvicorn[standard]

zsh glob expansion

Quote: pip install 'uvicorn[standard]'

export: not valid in this context: &

xargs choking on & in env values

Use set -a; source .env; set +a instead

curl http://localhost:9090/ returns 404

IAM has no root route

Normal — hit a real endpoint to verify the forward

Server boots but /ping hangs / 500

Postgres container stopped, or port-forward died

docker ps and check the two kubectl port-forward terminals are still alive


What we deliberately did NOT install

Component
Why skipped

MinIO

Only needed for document/template upload endpoints, which are out of scope for the audit smoke test. The MinIO env vars in .env are placeholders — settings load fine, the controllers only fail if a document endpoint is actually called.

master-data-service

Provides the seed data for openg2p_gen2_master_data_db. Endpoints that depend on it will fail; everything else (register-data, change-requests, registry-config, …) works without it.

Keycloak (locally)

We use the cluster's via port-forward — tokens issued by cluster Keycloak must be validated against the same realm keys. Running a separate local Keycloak would require recreating the realm/client/user setup.

IAM service (locally)

Same reason — cluster's IAM holds the role-to-permission mappings.

Last updated

Was this helpful?