Skip to main content

Local Development

This page covers three local-dev modes — choose based on what you’re iterating on. The Quickstart uses Kind; this page also covers the docker-compose loop (faster) and port-forward to a shared dev cluster (slowest, but real platform).

Pick a mode

ModeUse whenSetup timeIteration time
docker-composeIterating on your service code only; platform services can be mocked or stubbed~2 min<5s (hot reload)
KindYou need a real Kubernetes context (probes, configmaps, ingress)~5 min~30s per redeploy
Port-forward to shared dev clusterYou need real platform services (real identity/authorization services, real Connections)<1 min~30s per redeploy
Most loops are docker-compose. Move to Kind for the integration-test pass. Use the shared dev cluster only when you need cross-service coordination.

Mode 1: docker-compose with hot reload

This is the daily-driver loop. Your service runs on the host with uvicorn --reload; supporting services (Postgres for your DB, MinIO for shared storage, a local identity provider) run in containers.
1

Write the compose file

docker-compose.yaml
2

Write a .env.example

.env.example
Copy to .env (which should be in .gitignore) before running.
3

Boot supporting services and run your app with hot reload

Editing packages/api/src/api/*.py reloads the server inside ~1 second. Hit http://localhost:8000/echo?msg=hi to confirm.
4

Tear down

Mode 2: Kind cluster

Use Kind when you need real Kubernetes semantics (probes firing, ConfigMaps mounting, Ingress routing). The Quickstart walks through this end-to-end. Make iteration faster with this rebuild-and-load script:
kind-load.sh
Run ./kind-load.sh after each code change. ~30 seconds per cycle.

Mode 3: Port-forward to shared dev cluster

Use this when you need real platform behavior — real identity provider realms, real authorization data, real Connections. Run your code locally and forward platform services from a shared dev cluster.
Useful pitfall to know about: when port-forward drops (any error in the cluster), curl from your service will fail until you restart the forward. Wrap forwards in a tiny supervisor script (while true; do kubectl port-forward ... ; done) for long-running loops.

Mocking platform services

Three things are commonly mocked in unit tests:
  1. JWT verification — generate a signed test token with a fixture private key; load the matching public key as KEYCLOAK_JWKS_OVERRIDE (a path to a JSON file your auth code prefers over the JWKS endpoint when set).
  2. Permission checks — patch the Governance SDK’s permission method to return True (or the value under test).
  3. LiteLLM gateway — point LLM_GATEWAY_URL at a stub HTTP server that returns canned completions; or use litellm’s built-in mock provider (model="mock-openai/...").
See SDKs › Python for SDK extension points and Authenticate Users for the JWT fixture pattern.

Common pitfalls

Containers in Kind cannot use localhost to reach your host machine. Use host.docker.internal (macOS/Windows) or the host’s LAN IP (Linux). Or — better — run dependencies inside Kind with another helm install.
MinIO requires path-style addressing. Set OBSTORE_PATH_STYLE=true (or the equivalent in your S3 client config). Virtual-hosted style works on real S3 but not on MinIO without DNS magic.
uvicorn --reload watches the working directory by default. If your source is in a non-default location (e.g., packages/api/src/api/), pass --reload-dir packages/api/src. For container-based reload, mount the source directory as a volume and run uvicorn inside the container.
Every call to Governance, Assets, or Utils requires X-Project-ID. In local dev, hardcode a fixture project ID (e.g., 00000000-0000-0000-0000-000000000001) until you wire user-driven project switching.

CI smoke test

The Quickstart runs end-to-end in CI weekly via .github/workflows/quickstart-smoke.yml. The workflow provisions a Kind cluster, builds the example image, installs the chart, port-forwards, and asserts both endpoints respond. If it fails, an issue is opened automatically with the label solution-dev-guide — that is your signal to update the guide. This is the strongest possible self-sustainability signal: the quickstart cannot rot silently.

Next steps

Authenticate users

Wire JWT validation into your local app.

Manage secrets

Move secrets out of .env into the platform pipeline.

Use shared storage

Replace MinIO test code with the obstore client.

Troubleshooting

Stuck? Check known causes by error symptom.