Developer Docs

Local Development Environment

This guide covers how to run the full Ngwenya Federation stack locally for development. The platform uses Podman as its container engine (with Docker as a fallback) to orchestrate 20+ microservices, databases, and infrastructure components.

Prerequisites

Tool Version Install
Podman 5.8+ brew install podman podman-compose
Node.js 25+ brew install node
pnpm 9+ npm i -g pnpm
Rust 1.89+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
SQLx CLI 0.8+ cargo install sqlx-cli --no-default-features --features postgres,native-tls

Podman Machine Setup

Podman on macOS runs containers inside a lightweight Linux VM. Configure it with enough resources for the full stack:

# Initialize with generous resources (one-time)
podman machine init --cpus 8 --memory 16384 --disk-size 200

# Start the VM
podman machine start

The VM is configured with:

  • 8 CPUs โ€” parallel builds across NestJS and Rust services
  • 16 GB RAM โ€” required for building 15+ services concurrently
  • 200 GB disk โ€” accommodates Docker images, build cache, and database volumes

Why Podman over Docker Desktop? Podman gives direct control over VM resources without the artificial memory/disk limits that Docker Desktop imposes. On a 4 TB SSD, Docker Desktop may still cap its virtual disk at 64โ€“100 GB. Podman also runs daemonless, reducing system overhead.

Switching Between Engines

The Makefile uses a configurable CONTAINER_CMD variable:

# Default: Podman
make dev

# Override to Docker
make dev CONTAINER_CMD=docker

# Check active engine
make help

Architecture Overview

The federation runs as a Docker Compose stack with three layers:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 ngwenya-gateway                  โ”‚
โ”‚              (Hive Gateway / Yoga)               โ”‚
โ”‚                  :30000                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ malets โ”‚productsโ”‚servicesโ”‚ ucart  โ”‚  ... 10 more โ”‚
โ”‚ :3001  โ”‚ :3004  โ”‚ :3003  โ”‚ :3005  โ”‚  NestJS svcs โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚     auth        โ”‚     uchat       โ”‚  Rust svcs   โ”‚
โ”‚    :3008        โ”‚   :3017/:3018   โ”‚              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  postgres  โ”‚  redis  โ”‚  mongo  โ”‚ meilisearch     โ”‚
โ”‚   :5432    โ”‚  :6379  โ”‚ :27017  โ”‚    :7700        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Service Categories

Layer Services Technology
Gateway ngwenya-gateway Hive Gateway, GraphQL Yoga, Federation
NestJS Subgraphs malets, products, services, ucart, media, blogs, community, organizations, nodes, murchases, payments, search, alerts, experiences NestJS + MongoDB
Rust Services auth, uchat Axum + PostgreSQL
Infrastructure postgres, redis, mongo, meilisearch, conduit (Matrix) Managed images

Database Separation

Each Rust service has its own PostgreSQL database to prevent migration collisions:

Service Database Migrations
auth ngwenya_auth Timestamp-based (SQLx)
uchat ngwenya_uchat Sequential (001โ€“008)

The ngwenya_uchat database is auto-created on first boot via an init script mounted at /docker-entrypoint-initdb.d/.

Make Targets

Run make help for the full list. Key targets:

Starting Services

# Start all services (Podman Compose)
make dev

# Full nuke + rebuild (wipes volumes)
make clean-dev

# Infrastructure only (DBs, Redis, Meilisearch, Conduit)
make infra-up

Native Development (Fastest Iteration)

For rapid iteration on Rust services, run them natively against Podman's databases:

# Start infra in Podman, NestJS services natively
make services

# Rebuild and restart just uchat (6s vs 90s Docker rebuild)
make restart-uchat

# Rebuild and restart just auth
make restart-auth

Testing

# All unit tests
make test

# E2E tests (all services)
make test-e2e

# E2E tests for a specific subgraph
make test-e2e PATTERN=payments

# Tests for a specific subgraph (unit + E2E)
make test-all-pattern PATTERN=payments

# All Pact contract tests
make test-pact

# Soak test (sustained load + memory monitoring)
make soak-test                     # Full 30-min run
SOAK_DURATION_MINS=5 make soak-test  # Quick 5-min validation

Database Operations

# Seed with sample data
make seed

# Bulk seed (10 Malets ร— 100 items)
make seed-bulk

# Clean + re-seed
make seed-reset

# Rebuild Meilisearch index
make search-reindex

Development Workflows

Workflow 1: Full-Stack (New Developer)

Best for first-time setup or integration testing:

# 1. Clone and set up
git clone <repo> && cd ngwenya-federation
cp apps/auth/.env.example apps/auth/.env

# 2. Initialize Podman (one-time)
podman machine init --cpus 8 --memory 16384 --disk-size 200
podman machine start

# 3. Build and start everything
make clean-dev

# 4. Verify
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"query":"{__typename}"}' \
  http://localhost:30000/graphql

Best for active development on uchat or auth:

# 1. Ensure infra is running (Podman)
make infra-up

# 2. Make your code changes
# ... edit apps/uchat/src/...

# 3. Rebuild and restart natively (fast!)
make restart-uchat

# 4. Test
cargo test -p uchat

Workflow 3: NestJS Subgraph Work

# 1. Start everything
make dev

# 2. Watch logs for your service
podman compose -f docker-compose-dev.yaml logs -f malets

# Or run natively with hot-reload
make services

Workflow 4: Frontend Development (All 5 Apps)

The frontend uses a pnpm workspace that links the Storefront, Tower, Deck, and shared packages:

# 1. Install workspace dependencies from the monorepo root
cd ngwenya && pnpm install
# Or from ngwenya-front:
cd ngwenya-front && make install

# 2. Start all 5 frontend apps simultaneously
make ui
# This starts:
#   - Storefront      localhost:5173
#   - Tower            localhost:5170
#   - Deck             localhost:5171
#   - Developer Portal localhost:4321
#   - Support Center   localhost:4322

# 3. Or start individual apps
make tower-ui         # Tower only
make deck-ui          # Deck only
make dev-portal        # Developer docs only

All three SvelteKit apps (Storefront, Tower, Deck) share the @ngwenya/queries workspace package for GraphQL operations. The make install target runs pnpm install from the workspace root to resolve workspace:* dependencies.

IMPORTANT

All three frontend apps run on Vite 8 (Rolldown) with Svelte 5 and SvelteKit 2. Version parity is critical โ€” do not upgrade individual app versions without aligning all three.

Troubleshooting

`no space left on device`

Docker/Podman VM disk is full. Prune unused images and build cache:

podman system prune -a --volumes -f

If it persists, increase the VM disk size:

podman machine rm -f
podman machine init --cpus 8 --memory 16384 --disk-size 300
podman machine start

`cannot allocate memory` during builds

The VM doesn't have enough RAM. The default Podman machine ships with only 2 GB. Recreate with at least 16 GB:

podman machine rm -f
podman machine init --memory 16384
podman machine start

Conduit shows `unhealthy`

The Matrix homeserver (Conduit) may show as unhealthy if curl or wget is not available in its container image. This is cosmetic โ€” Conduit itself is running fine. The uchat service handles Matrix disconnection gracefully and will retry on startup.

uchat migration errors (`VersionMissing`)

This happens when uchat connects to the wrong database (e.g., ngwenya_auth instead of ngwenya_uchat). Verify the DATABASE_URL in docker-compose-dev.yaml points to ngwenya_uchat:

uchat:
  environment:
    DATABASE_URL: postgres://postgres:password@postgres:5432/ngwenya_uchat

`.dockerignore` and Build Context

Each Rust service has its own .dockerignore to exclude the multi-GB target/ directory from the build context. Without this, Docker/Podman tries to upload 5โ€“14 GB of build artifacts into the container, causing disk exhaustion:

# apps/uchat/.dockerignore
target/
.env
.env.local
*.log