Mobile-first microβbounty marketplace. Create β Match β Chat β Complete β Settle. Fast, transparent, escrowβbacked.
BOUNTYExpo makes it fast and safe to post small jobs ("bounties"), get matched with a hunter, coordinate in-app, and settle payment via an escrow flow. Designed for trust, speed, and clarity.
Early development / scaffolding. Core navigation + initial domain modeling in progress. Short-term focus: posting flow polish, chat stability, wallet (mock) interactions.
- Create Bounty: Poster enters title, description, amount (or marks as honor), optional location β bounty appears in Postings feed.
- Accept & Coordinate: Hunter opens a Posting β applies/accepts β Conversation auto-initiated.
- Complete & Settle: Escrow funded on accept β completion triggers release β both parties see history.
- Schedule (Lightweight): Optional due date shows in a read-only Calendar summary.
| Term | Meaning |
|---|---|
| Bounty | A task with title, description, amount or isForHonor flag, optional location. |
| Posting | A bounty in the public feed (status = open). |
| Request | A proposal/acceptance record on a bounty (future extension). |
| Conversation | 1:1 or group chat, optionally tied to a bounty. |
| Wallet | Escrow + transaction records (mock for now). |
| Calendar | Date summarization layer (read-only initially). |
// lib/types.ts
export type Money = number; // USD for now
export interface Bounty {
id: string;
user_id: string;
title: string;
description: string;
amount?: Money;
isForHonor?: boolean;
location?: string;
createdAt?: string;
status?: "open" | "in_progress" | "completed" | "archived";
}
export interface Conversation {
id: string;
bountyId?: string;
isGroup: boolean;
name: string;
avatar?: string;
lastMessage?: string;
updatedAt?: string;
}
export interface WalletTransaction {
id: string;
type: "escrow" | "release" | "refund";
amount: Money;
bountyId?: string;
createdAt: string;
}(Do not redefine these elsewhereβimport from lib/types.)
The app uses Expo Router with file-based routing. A single root shell component (e.g. BountyApp) renders the BottomNav once. Screens must NOT duplicate navigation state.
Bottom navigation mapping:
- create β Messenger (entry point to conversations / future create funnel enhancements)
- wallet β WalletScreen
- bounty β Dashboard / Home summary view
- postings β PostingsScreen (public feed)
- calendar β Calendar summary
Layout rules:
- Root container:
position: relative; flex: 1;pluspaddingBottomto clear nav height. - BottomNav:
position: absolute; left:0; right:0; bottom:0; zIndexhigh. - If nav height changes, increase root
paddingBottomaccordingly.
- Mobile-first, emerald palette (emerald-600/700/800) for primary actions.
- Clear primary CTA: central bounty action in nav.
- Favor helpful empty states over spinners (action-oriented copy + 1 primary button).
- Respect safe areas; no content hidden behind nav.
- Lift navigation state to root only. Pass via props:
<BottomNav activeScreen={activeScreen} onNavigate={setActiveScreen} />. - Avoid shadow local
activeScreenstates. - Async data: custom hooks in
hooks/; remote/services inlib/services/. - Memoize heavy list items / chat nodes with
React.memo,useCallback,useMemo.
- Non-blocking: On fetch failure, render fallback UI + Retry instead of blank screen.
- Inline error banners with dismiss
β. - Cache-first (future enhancement) to allow degraded offline view.
- Node.js 18+ - Download from nodejs.org
- Docker & Docker Compose - For running PostgreSQL and Stripe Mock locally
- pnpm - Fast package manager (auto-installed if missing)
- Expo CLI - For mobile development (installed on-demand)
# Clone the repository
git clone https://github.com/kodaksax/bountyexpo.git
cd bountyexpo
# Run the automated setup script
./scripts/setup.sh
# Start all services with one command
pnpm dev
# In another terminal, start the Expo development server
pnpm start- Clone and Install Dependencies
git clone https://github.com/kodaksax/bountyexpo.git
cd bountyexpo
pnpm install- Configure Environment
# Copy environment template
cp .env.example .env
# Edit .env with your configuration (see Environment Variables section below)
# For local development, the defaults work out of the box!- Start Development Stack
# Start infrastructure services (PostgreSQL + Redis + Stripe Mock)
pnpm dev
# In a new terminal, start the API server
pnpm dev:api
# This will:
# β
Start PostgreSQL database on port 5432
# β
Start Redis cache on port 6379
# β
Start Stripe Mock server on port 12111
# β
Start BountyExpo API server on port 3001
# β
Automatically run database migrations- Start Mobile App
# In a new terminal window
pnpm start
# Follow Expo CLI instructions to:
# - Scan QR code with Expo Go app (iOS/Android)
# - Press 'i' for iOS Simulator
# - Press 'a' for Android Emulator
# - Press 'w' for web browserYour .env file should contain these essential variables:
# Database (automatically configured for local Docker setup)
DATABASE_URL="postgresql://bountyexpo:bountyexpo123@localhost:5432/bountyexpo"
# Stripe (use test keys for development)
STRIPE_SECRET_KEY="sk_test_your_key_here"
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_your_key_here"
# Supabase (for authentication - REQUIRED)
# Get these from your Supabase project settings:
# 1. Go to https://supabase.com/dashboard
# 2. Select your project > Settings > API
SUPABASE_URL="https://your-project.supabase.co"
EXPO_PUBLIC_SUPABASE_URL="https://your-project.supabase.co"
SUPABASE_ANON_KEY="your-anon-key"
EXPO_PUBLIC_SUPABASE_ANON_KEY="your-anon-key"
SUPABASE_SERVICE_ROLE_KEY="your-service-role-key" # Keep secret, server-side only!
SUPABASE_JWT_SECRET="your-jwt-secret"
# API Configuration
API_BASE_URL="http://localhost:3001"
EXPO_PUBLIC_API_URL="http://localhost:3001"After running pnpm dev, these services will be available:
| Service | URL | Description |
|---|---|---|
| API Server | http://localhost:3001 | Main BountyExpo API |
| PostgreSQL | localhost:5432 | Database server |
| Redis | localhost:6379 | Cache server |
| Stripe Mock | http://localhost:12111 | Mock payment processing |
| API Health | http://localhost:3001/health | Health check endpoint |
A minimal Express server (server/) handles Stripe payment operations:
Features:
- Payment Intent creation for wallet deposits
- Webhook handling with signature verification
- Stripe Connect scaffolds for withdrawals
- Local transaction logging (demo mode)
Quick Start:
# Navigate to server directory
cd server
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with your Stripe keys
# Start the server
npm startEndpoints:
GET /health- Health checkPOST /payments/create-payment-intent- Create payment intentPOST /webhooks/stripe- Handle Stripe webhooksPOST /connect/create-account-link- Stripe Connect onboardingPOST /connect/transfer- Initiate bank transfers
Configuration:
Set these in server/.env:
STRIPE_SECRET_KEY=sk_test_your_key_here
STRIPE_WEBHOOK_SECRET=whsec_your_secret_here
PORT=3001π Full documentation: See STRIPE_INTEGRATION_BACKEND.md
When testing payments from a phone using Expo Go, the client must call your development machine's LAN IP (not localhost). Follow these steps:
- Start the payments server (in project root):
# start payments server
npm run payments:server- Set the client to use your machine LAN IP. In the project root
.envset:
EXPO_PUBLIC_API_BASE_URL=http://<YOUR_LAN_IP>:3001
# e.g. EXPO_PUBLIC_API_BASE_URL=http://192.168.0.59:3001- Restart Expo (clear cache) so the client picks up the env:
npx expo start --clear- Open the app on your phone with Expo Go and trigger the Add Money flow. You can verify reachability in a browser on your phone:
http://<YOUR_LAN_IP>:3001/health
Alternatives:
- Use
expo start --tunnelto avoid firewall/LAN issues. - Or run
ngrok http 3001and setEXPO_PUBLIC_API_BASE_URLto the ngrok https URL.
# Development
pnpm dev # Start infrastructure services (PostgreSQL + Stripe Mock)
pnpm dev:api # Start API server (run in separate terminal)
pnpm start # Start Expo development server
pnpm dev:stop # Stop all services
pnpm dev:logs # View service logs
# Testing
pnpm test:auth # Test authentication endpoints (sign-up/sign-in)
pnpm test:api # Test API endpoints
# Database
pnpm db:init # Initialize database (if needed)
pnpm --filter @bountyexpo/api db:migrate # Run migrations
pnpm --filter @bountyexpo/api db:seed # Seed with sample data
# Code Quality
pnpm type-check # TypeScript checks across all packages
pnpm lint # Lint codeIf you're updating from an older version:
- Update Dependencies
pnpm install- Update Environment File
# Compare your .env with the new .env.example
# Add any missing variables- Database Migrations
# Stop existing services
pnpm dev:stop
# Start fresh with latest schema
pnpm dev
# The database will auto-migrate on startup- Clear Metro Cache (if experiencing issues)
pnpm start --clearBountyExpo uses Supabase for authentication. To set it up:
-
Create a Supabase Project
- Go to supabase.com
- Create a new project
- Wait for database to be provisioned
-
Get API Credentials
- Navigate to Settings > API in your Supabase dashboard
- Copy the Project URL and API keys
- Update your
.envfile with these values
-
Test Authentication
# Make sure API server is running pnpm dev:api # Run auth tests pnpm test:auth
You should see:
- β Health check passed
- β Supabase configuration valid
- β Sign-up successful
- β Sign-in successful
-
Troubleshooting Auth Issues
- Check
.envhas all required Supabase variables - Verify
SUPABASE_SERVICE_ROLE_KEY(not ANON_KEY) is set for server - Ensure both server and client keys are configured
- Check Supabase dashboard for user creation logs
- Check
-
API Client Package
The @bountyexpo/api-client package provides typed API wrappers and React hooks:
# Build all packages
npm run build
# Type check everything
npm run type-check- Access the Application
- Frontend: Expo Dev Tools will open in your browser
- Backend API: Available at
http://localhost:3001 - Database: Migrations run automatically on API startup
- Database Connection Issues: Ensure PostgreSQL is running and DATABASE_URL is correct
- Build Errors: Run
npm run type-checkto identify TypeScript issues - Metro Bundler Issues: Clear cache with
npx expo start --clear
BountyExpo uses a modern, scalable monorepo architecture:
- Frontend: React Native + Expo app (main directory)
- Backend API: Fastify + Drizzle ORM + PostgreSQL (
services/api/) - Shared Types: Cross-platform type definitions (
packages/domain-types/) - Docker Environment: Containerized PostgreSQL + Stripe Mock for consistent development
- Mobile: React Native 0.81+ with Expo 54+
- Backend: Node.js + Fastify + TypeScript
- Database: PostgreSQL 15 with Drizzle ORM
- Payments: Stripe integration with local mock server
- Authentication: Supabase JWT with automatic user provisioning
- Package Management: pnpm with workspace configuration
# Start everything (recommended)
pnpm dev # Infrastructure: PostgreSQL + Stripe Mock
pnpm dev:api # API server (separate terminal)
pnpm start # Expo development server (separate terminal)
# Individual services
pnpm dev:api # API server only
pnpm dev:stop # Stop all Docker services
pnpm dev:logs # View service logs
# Code quality
pnpm type-check # TypeScript validation across all packages
pnpm lint # ESLint checks- Start Services:
pnpm dev(runs PostgreSQL, API, Stripe Mock) - Start Mobile App:
pnpm start(in separate terminal) - Make Changes: Edit code with hot reloading
- Check Types:
pnpm type-checkbefore committing - View Logs:
pnpm dev:logsfor debugging
- Auto-Migration: Database schema updates automatically
- JWT Authentication: Secure endpoints with Supabase integration
- Type Safety: End-to-end TypeScript with Drizzle ORM
- Real-time Events: WebSocket support for live updates
- Stripe Integration: Payment processing with local mock server
# Direct database access (when services are running)
psql postgresql://bountyexpo:bountyexpo123@localhost:5432/bountyexpo
# API-specific database commands
pnpm --filter @bountyexpo/api db:migrate # Run migrations
pnpm --filter @bountyexpo/api db:seed # Add sample data
pnpm --filter @bountyexpo/api db:studio # Drizzle Studio UISee services/api/README.md for detailed API documentation.
npx tsc --noEmitnpm run reset-project(Not typically needed now that base scaffolding is customized.)
app/
(routes...)
components/
BottomNav.tsx
hooks/
useBounties.ts
lib/
types.ts
services/
bountyService.ts
(Actual structure may evolve; keep types centralized.)
Initial phase: mock transactions for UI. Future integration targets: Stripe Connect / Replit Deploy / TBD custody service. Design assumptions:
- Escrow created at acceptance.
- Release only by Poster confirmation or dual-sign event.
- Refund path for timeouts / disputes (manual early phase).
The app uses Metro bundler caching for faster rebuilds. If you encounter layout issues or stale code:
# Clear Metro cache and restart
npx expo start --clear
# Alternative: Clear all caches
rm -rf node_modules/.cache
rm -rf .expo
npx expo start --clearThe API uses Redis for caching frequently accessed data to improve response times and reduce database load.
Cached Resources:
- Profiles: Cached for 5 minutes (300s)
- Bounties: Cached for 3 minutes (180s)
- Bounty Lists: Cached for 1 minute (60s)
Cache Invalidation:
- Profile updates automatically invalidate the profile cache
- Bounty create/update/delete operations invalidate bounty caches
- Status changes (accept/complete/archive) invalidate related caches
- List caches are cleared when bounties are modified
Configuration: Set these environment variables to customize caching behavior:
# Redis Configuration
REDIS_HOST=localhost # Redis server host
REDIS_PORT=6379 # Redis server port
REDIS_ENABLED=true # Enable/disable caching
REDIS_TTL_PROFILE=300 # Profile cache TTL (seconds)
REDIS_TTL_BOUNTY=180 # Bounty cache TTL (seconds)
REDIS_TTL_BOUNTY_LIST=60 # Bounty list cache TTL (seconds)Testing Redis:
# Test Redis connection and caching operations
cd services/api
npx tsx src/test-redis-simple.ts- Auto-caching: The app uses
expo-imageinstead of React Native'sImagefor automatic caching - Lazy loading: Images in lists are loaded on-demand with thumbnail optimization
- CDN-aware: Supports Cloudinary and Imgix transformations for optimal sizing
All FlatLists are optimized with:
windowSize={5}- Renders 5 screens worth of itemsmaxToRenderPerBatch={5}- Batches rendering for smoother scrollingremoveClippedSubviews={true}- Removes off-screen views from memoryinitialNumToRender={5}- Initial render size for faster startup
To analyze bundle size and identify large dependencies:
# Install the analyzer
npm install --save-dev react-native-bundle-visualizer
# Analyze the bundle
npx react-native-bundle-visualizerPerformance is tracked using:
- React Profiler for identifying slow renders
- Expo performance monitoring for screen load times
- Custom metrics for user interactions
Target Metrics:
- Images load instantly from cache
- Lists scroll at 60fps
- Bundle size < 10MB
- App launch < 2 seconds
# Run all tests
npm test
# Run unit tests only
npm run test:unit
# Run integration tests
npm run test:integration
# Run end-to-end tests
npm run test:e2e
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode during development
npm run test:watch
# Run with verbose output for debugging
npm run test:verboseCurrent Coverage (~20% overall):
- β 593 tests passing across unit, integration, and E2E suites
- β
Strong coverage in critical areas:
- Phone & Email Verification (100%)
- Password Validation (94%)
- Sanitization Utilities (93%)
- Date Utilities (100%)
- Bounty Validation (100%)
Test Organization:
__tests__/unit/- Unit tests for services, utilities, and components__tests__/integration/- API integration tests__tests__/e2e/- End-to-end user flow tests
We're actively working to increase test coverage across the codebase. See COVERAGE_IMPROVEMENT_PLAN.md for:
- Current coverage metrics and goals
- Prioritized areas for new tests
- Testing best practices and patterns
- How to contribute tests
Note: Coverage thresholds are currently disabled in CI to allow incremental improvement without blocking development. Coverage reports are still generated and uploaded to Codecov for visibility.
Short Term:
- Postings creation polish
- Conversation stability & message persistence layer
- Mock wallet flows (escrow β release)
- Calendar summary pass
The API supports realtime bounty status updates via WebSocket or Supabase Realtime.
WebSocket Connection:
const ws = new WebSocket('ws://localhost:3001/events/subscribe');
ws.on('message', (data) => {
const event = JSON.parse(data);
console.log('Realtime event:', event);
});Event Payload Format:
{
"type": "bounty.status",
"id": "bounty-id-123",
"status": "in_progress",
"timestamp": "2024-01-01T00:00:00.000Z"
}Status Values:
open- Bounty is available for acceptancein_progress- Bounty has been accepted and is being worked oncompleted- Bounty work has been completedarchived- Bounty has been archived/cancelled
Endpoint Documentation:
- GET
/events/subscribe-info- WebSocket connection instructions - GET
/events/stats- Current connection statistics - WebSocket
/events/subscribe- Realtime event stream
Realtime events are published when:
- A bounty is accepted (
openβin_progress) - A bounty is completed (
in_progressβcompleted)
Mid Term:
- Real escrow provider integration
- Request lifecycle (apply / accept handshake)
- Push notifications for chat + status changes
- Offline optimistic message queue
Long Term:
- Reputation / profile proofs
- Dispute mediation tooling
- Multi-currency support
"Docker not found" or service won't start
# Ensure Docker is running
docker --version
docker-compose --version
# Check if ports are available
lsof -i :5432 # PostgreSQL
lsof -i :3001 # API server"Database connection failed"
# Check if PostgreSQL container is running
docker ps
# Restart database service
pnpm dev:stop
pnpm dev
# Check database logs
docker logs bountyexpo-postgres"Expo build fails" or Metro bundler issues
# Clear Metro cache
pnpm start --clear
# Clean node_modules and reinstall
rm -rf node_modules
pnpm install"TypeScript errors in IDE"
# Run type checking manually
pnpm type-check
# Restart TypeScript language server in your IDE
# VS Code: Cmd/Ctrl + Shift + P β "TypeScript: Restart TS Server""API endpoints return 401/403 errors"
- Check your
.envfile has correct Supabase credentials - Verify JWT tokens are being sent from the mobile app
- Check API server logs:
pnpm dev:logs
"Stripe payments not working"
- Ensure you're using test keys (start with
sk_test_andpk_test_) - Verify Stripe Mock server is running on port 12111
- Check the console for payment-related errors
"Redis connection issues"
# Check if Redis container is running
docker ps | grep redis
# Restart Redis service
docker compose restart redis
# Check Redis logs
docker logs bountyexpo-redis
# Test Redis connection
cd services/api
npx tsx src/test-redis-simple.ts"Cache issues or stale data"
# Flush Redis cache (use with caution)
docker exec bountyexpo-redis redis-cli FLUSHDB
# Or disable caching temporarily
# Set REDIS_ENABLED=false in .env- Check the logs:
pnpm dev:logsshows all service logs - Verify services: All services should show as "UP" in
docker ps - Health check: Visit http://localhost:3001/health
- Reset everything:
pnpm dev:stopthenpnpm dev - Open an issue: If problems persist, create a GitHub issue with:
- Your OS and Node.js version
- Complete error messages
- Steps to reproduce
- Fork & Clone: Fork the repo and create a feature branch
git checkout -b feat/your-feature-name- Set up Development Environment:
./scripts/setup.sh # Automated setup
# OR follow the "First Run" section above-
Make Your Changes: Edit code with hot reloading enabled
-
Test Your Changes:
pnpm type-check # TypeScript validation
pnpm lint # Code style checks
pnpm dev:logs # Check for errors in services- Commit & Push:
git add .
git commit -m "feat: add your feature description"
git push origin feat/your-feature-name- Open Pull Request: Include:
- Problem Summary: What issue does this solve?
- Screenshots: For UI changes
- Testing Notes: How to verify the changes work
- Breaking Changes: Any API or schema changes
- Commit Style: Use conventional commits (
feat:,fix:,docs:, etc.) - TypeScript: All code must pass
pnpm type-check - Code Style: Follow existing patterns, use provided linting
- Testing: Test your changes locally with
pnpm dev+pnpm start - Documentation: Update README.md for significant changes
When using AI assistance:
- Provide patch-style suggestions with clear filepath headers.
- Do NOT add duplicate navigation components.
- Always import
StyleSheetas a value fromreact-native(never as a type-only import). - Ensure all JSX tags are properly closed.
- Prefer async updates in PR descriptions vs. large speculative refactors.
- Document new domain fields directly in
lib/types.tsfirst.
(Choose a licenseβcurrently unspecified. Consider MIT for openness.)
Built with Expo + React Native. Inspired by lightweight, trust-centered gig flows.
Questions / ideas? Open a discussion or start a PR.