AI-powered mobile app for identifying and managing city pigeons using ML image recognition.
- Features
- Tech Stack
- Prerequisites
- Quick Start
- Configuration
- Development
- API Documentation
- Deployment
- Troubleshooting
- Contributing
- License
- MobileNet-V2 powered pigeon identification with 1024-dimensional embeddings
- Server-side feature extraction using TensorFlow.js
- Cosine similarity matching with configurable threshold (0.50-0.99)
- Multi-angle support through multiple image storage per pigeon
- Automatic embedding generation for new registrations
- React Native 0.76 + Expo SDK 52 for cross-platform development
- Real-time camera scanning with
expo-camera - Offline support with local state persistence (Zustand + MMKV)
- Material Design 3 UI components via React Native Paper
- Location tracking for sighting records
- OTA updates via EAS (Expo Application Services)
- API key authentication with rate limiting
- Health checks with detailed service monitoring
- CORS handling configured for mobile and web clients
- Security headers via Helmet
- Request logging with Morgan
- Comprehensive test suite (Jest + Supertest)
- PostgreSQL 15+ with pgvector extension for vector similarity search
- HNSW index for fast 1024-d embedding lookups
- Local filesystem storage for images (
/uploads) - MinIO (optional/S3-compatible, configured but not actively used)
| Technology | Version | Purpose |
|---|---|---|
| React Native | 0.76 | Native mobile UI |
| Expo SDK | 52 | Development & build platform |
| TypeScript | 5.9 | Type safety |
| React Navigation | v7 | Screen navigation |
| React Native Paper | v5 | Material Design 3 components |
| Zustand | ^5.x | Global state management |
| React Query | ^5.x | Server-state & caching |
| MMKV | ^2.x | Local persistent storage |
| Axios | ^1.x | HTTP client |
| expo-camera | ~16.0 | Camera access |
| expo-location | ~18.0 | GPS tracking |
| Technology | Purpose |
|---|---|
| Node.js + Express 5 | API server |
| TypeScript 5.9 | Type safety |
| MobileNet-V2 | AI feature extraction |
| TensorFlow.js | ML runtime |
| PostgreSQL + pgvector | Database & embeddings |
| Helmet | Security headers |
| CORS | Cross-origin handling |
| Technology | Purpose |
|---|---|
| Docker Compose | Multi-service orchestration |
| GitHub Actions | CI/CD pipelines |
| EAS Build | Mobile build service |
| Nginx Proxy Manager | Reverse proxy & SSL |
| Tool | Version | Installation |
|---|---|---|
| Docker | Latest | docker.com |
| Docker Compose | v2.x | Included with Docker Desktop |
| Node.js | 20+ LTS | nodejs.org |
| Git | 2.x | git-scm.com |
| Component | Minimum | Recommended |
|---|---|---|
| RAM | 8 GB | 16 GB |
| Storage | 10 GB free | 20 GB free |
| CPU | 4 cores | 8 cores |
- Expo Account (for EAS builds): expo.dev/signup
- Android Studio (for emulator): developer.android.com/studio
- Xcode (for iOS, macOS only): App Store
git clone https://github.com/openfugjoobot/tauben-scanner.git
cd tauben-scanner# Copy environment files
cp .env.example .env
cp backend/.env.example backend/.env
cp mobile/.env.example mobile/.env# Start all Docker services
docker-compose up -d
# Verify services are healthy
curl http://localhost:3000/health
curl -H "X-API-Key: your-key" http://localhost:3000/api/pigeonscd mobile
npm install
npx expo startThen scan the QR code with Expo Go app on your device.
Create backend/.env from backend/.env.example:
# ============================================
# Required Configuration
# ============================================
# Database Connection (REQUIRED)
DATABASE_URL=postgresql://tauben:your_password@localhost:5432/tauben_scanner
# Server
PORT=3000
NODE_ENV=production
# API Key Authentication (Generate with: npm run generate-api-key)
API_KEY=your_secure_api_key_here_min_32_characters
cd backend && npm run generate-api-key| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
β | - | PostgreSQL connection string |
PORT |
β | 3000 | API server port |
NODE_ENV |
β | production | Environment mode |
API_KEY |
- | Authentication key (recommended for prod) | |
CORS_ORIGINS |
β | - | Comma-separated allowed origins |
RATE_LIMIT_WINDOW_MS |
β | 900000 | Rate limit window (ms) |
RATE_LIMIT_MAX_REQUESTS |
β | 100 | Max requests per window |
RATE_LIMIT_UPLOAD_MAX |
β | 10 | Max upload requests per window |
JWT_SECRET |
β | - | JWT token secret |
UPLOADS_DIR |
β | /app/uploads | Image storage path |
Create mobile/.env from mobile/.env.example:
# API URL (REQUIRED)
EXPO_PUBLIC_API_URL=http://localhost:3000/api
# For LAN development:
# EXPO_PUBLIC_API_URL=http://192.168.1.100:3000/api
# For production:
# EXPO_PUBLIC_API_URL=https://api.your-domain.com/api
# Storage Encryption Key (REQUIRED - 32+ chars)
EXPO_PUBLIC_STORAGE_KEY=your-secure-storage-key-change-in-production
# Debug Mode (optional)
EXPO_PUBLIC_ENABLE_DEBUG_MODE=false
# Offline Mode (optional)
EXPO_PUBLIC_ENABLE_OFFLINE_MODE=true| Variable | Required | Description |
|---|---|---|
EXPO_PUBLIC_API_URL |
β | Backend API URL |
EXPO_PUBLIC_STORAGE_KEY |
β | Local storage encryption key |
EXPO_PUBLIC_DEFAULT_API_KEY |
β | Default API key for dev builds |
EXPO_PUBLIC_ENABLE_DEBUG_MODE |
β | Enable debug features |
EXPO_PUBLIC_ENABLE_OFFLINE_MODE |
β | Enable offline capabilities |
EXPO_PUBLIC_SENTRY_DSN |
β | Sentry error tracking |
EXPO_PUBLIC_SENTRY_ENVIRONMENT |
β | Sentry environment tag |
Generate a secure API key for production:
cd backend
npm run generate-api-key
# Output example:
# === API Key Generator ===
# Generated API Key:
# ------------------
# 3a7f9b2c8e5d1a4b6c0f3e8d5a2b7c1f4e0d8a5b2c6f3e9d1a7b4c0f2e8d5a1
# ------------------
# Add this to your .env file: API_KEY=...Copy the generated key to your backend/.env file.
cd backend
# Install dependencies
npm install
# Run development server with hot reload
npm run dev
# Build TypeScript
npm run build
# Run tests
npm test
npm run test:coverage
npm run test:watchcd mobile
# Install dependencies
npm install
# Start development server
npx expo start
# Run on Android emulator
npx expo run:android
# Run on iOS simulator (macOS only)
npx expo run:ios
# Clear cache and restart
npx expo start --clear
# Run tests
npm test
npm run test:coverage
# Lint and type check
npm run lint
npm run typecheck-
Install Expo Go on your device:
-
Start the dev server:
cd mobile && npx expo start
-
Scan the QR code with Expo Go
-
The app will hot-reload on code changes
- Development:
http://localhost:3000 - Production:
https://your-domain.com
API endpoints require authentication via one of these methods:
Header: X-API-Key
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3000/api/pigeonsHeader: Authorization Bearer
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:3000/api/pigeons| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Basic health check |
| GET | /health/detailed |
Detailed service status |
| GET | /health/live |
Liveness probe |
| GET | /health/ready |
Readiness probe |
Response Example:
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00.000Z",
"services": {
"database": "connected",
"storage": "connected"
}
}| Method | Endpoint | Description |
|---|---|---|
| POST | /api/images/match |
Match pigeon photo against database |
Request:
{
"photo": "data:image/jpeg;base64,/9j/4AAQSkZJRgAB...",
"threshold": 0.80,
"location": {
"lat": 52.52,
"lng": 13.405,
"name": "Berlin"
}
}Response (Match Found):
{
"match": true,
"pigeon": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Rudi Rothen",
"photo_url": "/uploads/rudi_2024.jpg"
},
"confidence": 0.9234
}Response (No Match):
{
"match": false,
"confidence": 0,
"suggestion": "Register as new pigeon?"
}| Method | Endpoint | Description |
|---|---|---|
| GET | /api/pigeons |
List all pigeons (paginated) |
| POST | /api/pigeons |
Register new pigeon |
| GET | /api/pigeons/:id |
Get pigeon details |
Create Pigeon Request:
{
"name": "Rudi Rothen",
"photo": "data:image/jpeg;base64,/9j/4AAQSkZJRgAB...",
"description": "Red ring on left foot",
"location": { "lat": 52.52, "lng": 13.405, "name": "Berlin" }
}| Method | Endpoint | Description |
|---|---|---|
| POST | /api/sightings |
Record a new sighting |
For complete API documentation, see docs/API.md.
# Clone repository
git clone https://github.com/openfugjoobot/tauben-scanner.git
cd tauben-scanner
# Configure environment
cp .env.example .env
nano .env # Edit your values
# Build and start services
docker-compose up -d --build
# View logs
docker-compose logs -f api- Set strong
API_KEYinbackend/.env - Configure
CORS_ORIGINSfor your domain - Set
NODE_ENV=production - Use strong
DB_PASSWORDfor PostgreSQL - Enable HTTPS with reverse proxy (Nginx/Caddy)
- Set up SSL certificates
- Configure firewall rules (only 443/3000 exposed)
- Set up database backups (see docs/BACKUP.md)
- Configure monitoring (health checks)
- Set up log rotation
- Test rate limiting
- Verify API key authentication works
cd mobile
# Login to Expo
npx expo login
# Configure EAS
npx eas build:configure
# Preview build (Android APK)
eas build --platform android --profile preview
# Production build (Android AAB)
eas build --platform android --profile production
# iOS build (requires Apple Developer account)
eas build --platform ios --profile production# Publish update to production channel
eas update --channel production --message "Bug fixes and improvements"
# Publish to preview channel
eas update --channel preview --message "New features preview"For detailed deployment instructions, see docs/DEPLOYMENT.md.
"Cannot connect to Docker daemon"
sudo usermod -aG docker $USER
newgrp docker # Or logout/login"Port 3000 already in use"
# Find and kill process
sudo lsof -i :3000
sudo kill -9 <PID>Database connection failed
# Check logs
docker-compose logs postgres
# Restart services
docker-compose down
docker-compose up -dAPI key authentication fails
# Verify API_KEY is set
printf "${API_KEY}\n" # In backend/.env
# Generate new key
cd backend && npm run generate-api-key"expo command not found"
npm install -g @expo/cli
# Or: npx expo <command>"Could not connect to development server"
# Check firewall
# Ensure device and computer are on same network
# Use tunnel mode
npx expo start --tunnelImages not loading
# Check CORS_ORIGINS includes your API URL
# Verify API base URL in mobile/.env
# Check API key is validOTA updates not working
# Verify runtimeVersion in app.json
# Check channel name matches eas update command
# App must start at least once to receive updates| Issue | Solution |
|---|---|
| 502 Bad Gateway | Check tauben-api container is running |
| CORS errors | Verify CORS_ORIGINS includes client origin |
| Images not serving | Verify uploads_data volume permissions |
| Slow matching | Check pgvector HNSW index is created |
For more troubleshooting, see docs/TROUBLESHOOTING.md.
- Fork the repository
- Create a branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'feat: Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
feat:New featuresfix:Bug fixesdocs:Documentation changesrefactor:Code refactoringtest:Adding testschore:Maintenance tasks
- TypeScript strict mode enabled
- ESLint + Prettier configured
- All tests must pass
- API changes require documentation updates
MIT License β see LICENSE file for details.
| Document | Description |
|---|---|
| docs/API.md | Complete REST API reference |
| docs/ARCHITECTURE.md | System architecture & data flow |
| docs/DATABASE.md | PostgreSQL schema & tables |
| docs/DEPLOYMENT.md | Production deployment guide |
| docs/SETUP.md | Development environment setup |
| docs/MOBILE.md | Mobile app development |
| docs/BACKUP.md | Backup strategies |
| docs/CHANGELOG.md | Change history |
Made with β€οΈ by OpenFugjooBot
For support, open an issue on GitHub or contact the maintainers.
