Getting Started

Prerequisites

  • Python 3.12+

  • Node.js 22+

  • Poetry

Backend Setup

cd backend
poetry install
poetry run uvicorn ccbenefits.main:app --reload

The backend runs on http://localhost:8000. On first startup, it creates the SQLite database and seeds card templates.

For production, set environment variables:

export CCB_SECRET_KEY="your-secret-key-here"
export CCB_ENV="production"
export CCB_ALLOWED_ORIGINS="https://yourdomain.com"
export DATABASE_URL="postgresql+psycopg://user:pass@localhost/ccbenefits"

Optional — email verification (requires Resend account):

export RESEND_API_KEY="re_xxxxx"
export CCB_EMAIL_FROM="noreply@yourdomain.com"
export CCB_FRONTEND_URL="https://yourdomain.com"

Optional — Google OAuth:

export GOOGLE_CLIENT_ID="your-web-client-id"
export GOOGLE_CLIENT_ID_ANDROID="your-android-client-id"
export GOOGLE_CLIENT_ID_IOS="your-ios-client-id"

For the frontend, create frontend/.env.local:

VITE_GOOGLE_CLIENT_ID="your-web-client-id"

Optional — observability (requires Grafana Cloud account):

export GRAFANA_OTLP_ENDPOINT="https://otlp-gateway-prod-xxx.grafana.net/otlp"
export GRAFANA_INSTANCE_ID="your-instance-id"
export GRAFANA_OTLP_TOKEN="glc_xxxxx"

Frontend Setup

cd frontend
npm install
npm run dev

The frontend dev server runs on http://localhost:5173.

Production Build

cd frontend && npm run build

Then start the backend — it serves the built frontend from frontend/dist/.

Mobile App Setup

cd mobile
npm install
npx expo start --dev-client --port 8081

In dev mode (__DEV__), the app automatically uses the local backend (http://10.0.2.2:8000 on Android emulator, http://localhost:8000 on iOS). In production builds, it uses https://ccb.kumaranik.com.

Android Emulator Setup

One-time setup (macOS with Homebrew):

# Install Java 17 and Android SDK
brew install openjdk@17
brew install --cask android-commandlinetools

# Symlink Java for system discovery (requires sudo)
sudo ln -sfn /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk \
  /Library/Java/JavaVirtualMachines/openjdk-17.jdk

# Accept SDK licenses
sdkmanager --licenses

# Install emulator + system image
sdkmanager "platforms;android-34" "build-tools;34.0.0" "emulator" \
  "platform-tools" "system-images;android-34;google_apis;arm64-v8a"

# Create virtual device
avdmanager create avd -n ccb_pixel \
  -k "system-images;android-34;google_apis;arm64-v8a" -d pixel_7

Add to ~/.zshrc:

export JAVA_HOME=$(/usr/libexec/java_home -v 17)
export ANDROID_HOME="/opt/homebrew/share/android-commandlinetools"
export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator

To run the emulator:

# Start emulator
$ANDROID_HOME/emulator/emulator -avd ccb_pixel &

# Start Metro dev server (auto-connects to local backend)
cd mobile && npx expo start --dev-client --port 8081

For full testing setup including building the debug APK, running screenshot tests, and seeding test data, see mobile/TESTING.md.

iOS Simulator

Requires Xcode (free from Mac App Store). Build via EAS:

eas build --profile simulator --platform ios

Or run directly with Expo:

cd mobile && npx expo start --ios

Building Standalone Apps

# Android APK
eas build --profile preview --platform android

# iOS Simulator .app
eas build --profile simulator --platform ios

Docker Deployment

For production deployment with Docker Compose:

# Copy and fill in environment variables
cp .env.example .env

# Start all services (app + postgres + caddy)
docker compose -f docker-compose.prod.yml up -d

See .env.example for all available configuration options.

Running Tests

# Backend lint
cd backend
poetry run ruff check ccbenefits/ tests/

# Backend tests (190+ tests, 87%+ coverage)
cd backend
poetry run pytest -v

# Frontend lint + tests (70+ tests)
cd frontend
npm run lint
npx vitest run

Backend tests include coverage reporting with an 80% minimum threshold. Ruff (Python) and ESLint (TypeScript) are enforced in CI — lint failures block PRs.

Integration Tests

Integration tests run against a Docker Compose stack (app + postgres):

# Build test image
docker build -t ccbenefits:test .

# Start test stack
docker compose -f docker-compose.test.yml up -d

# Wait for health
for i in $(seq 1 20); do curl -sf http://localhost:8080/api/health && break; sleep 3; done

# API smoke tests (5 tests)
cd backend && poetry run pytest tests/integration/ -v --no-cov

# Playwright E2E tests (3 flows)
cd tests/e2e && npm ci && npx playwright install chromium --with-deps && npx playwright test

# Tear down
docker compose -f docker-compose.test.yml down -v

Deploy Pipeline

Deploys are gated behind integration tests + manual approval:

  1. Push to master triggers integration tests (Docker build → API smoke → Playwright E2E)

  2. If tests pass, a “Review pending” approval button appears in GitHub Actions

  3. Click approve to deploy to production (GHCR push → SSH deploy to Oracle VM)

  4. Emergency deploys available via the “Deploy (Emergency)” workflow (manual trigger)