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:
Push to master triggers integration tests (Docker build → API smoke → Playwright E2E)
If tests pass, a “Review pending” approval button appears in GitHub Actions
Click approve to deploy to production (GHCR push → SSH deploy to Oracle VM)
Emergency deploys available via the “Deploy (Emergency)” workflow (manual trigger)