Files
giglez/DEPLOYMENT_CONSIDERATIONS.md
T

802 lines
22 KiB
Markdown

# GigLez Deployment Considerations: Development vs Production
**Created**: 2026-01-12
**Purpose**: Identify critical differences between local development and production deployment
---
## Executive Summary
GigLez needs to support two distinct deployment scenarios:
1. **Local Development** (Termux on Android)
- Resource-constrained (mobile device)
- Single-user access
- Direct USB serial communication
- Local file storage
- Development/testing workflow
2. **Production Server** (Cloud/VPS)
- Multi-user platform
- Public API access
- No hardware connection
- Object storage for files
- High availability requirements
**Key Insight**: We need a **modular architecture** that supports both scenarios without code duplication.
---
## Architecture Strategy
### Hybrid Architecture: "Capture" vs "Platform" Mode
```
┌─────────────────────────────────────────────────────────┐
│ GigLez Application │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ Capture Mode │ │ Platform Mode │ │
│ │ (Termux) │ │ (Server) │ │
│ ├────────────────┤ ├────────────────┤ │
│ │ - T-Embed USB │ │ - No hardware │ │
│ │ - Local GPS │ │ - File uploads │ │
│ │ - Auto-capture │ │ - Multi-user │ │
│ │ - Local API │ │ - Public API │ │
│ └────────────────┘ └────────────────┘ │
│ │ │ │
│ └──────────┬───────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Shared Core │ │
│ ├────────────────┤ │
│ │ - Database │ │
│ │ - Parser │ │
│ │ - Matcher │ │
│ │ - GPS Validator│ │
│ │ - API Endpoints│ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
---
## Detailed Comparison
### 1. Hardware Access
#### Development (Termux)
- **T-Embed Connection**: USB serial communication (pyserial)
- **GPS Source**: Android native GPS via SL4A or Termux:API
- **Real-time Capture**: Direct signal scanning and capture
- **Storage**: Local filesystem on Android device
**Implications**:
- Need hardware abstraction layer
- Support offline operation
- Handle USB disconnections
- Batch upload when network available
#### Production (Server)
- **No Hardware**: Accept pre-captured .sub files only
- **GPS Source**: Embedded in upload manifest
- **No Real-time**: Historical data only
- **Storage**: Object storage (S3, MinIO, or filesystem)
**Implications**:
- No serial communication code needed
- Network-dependent operation
- Focus on file processing pipeline
- Horizontal scaling possible
**Solution**:
```python
# Abstract hardware interface
class CaptureSource(ABC):
@abstractmethod
def scan_signals(self): pass
@abstractmethod
def get_gps(self): pass
class TEmbedSource(CaptureSource):
"""Development: Direct hardware"""
pass
class UploadSource(CaptureSource):
"""Production: File uploads"""
pass
```
---
### 2. Database Configuration
#### Development (Termux)
- **PostgreSQL**: Local installation via `pkg install postgresql`
- **Host**: localhost or 127.0.0.1
- **Port**: 5432
- **Connections**: Small pool (5 + 5 overflow)
- **Storage**: Device internal storage or SD card
**Considerations**:
- Limited RAM on mobile devices
- Smaller connection pool
- May need to tune PostgreSQL for mobile
- Backup to SD card or cloud
#### Production (Server)
- **PostgreSQL**: Dedicated server or managed database (RDS, Cloud SQL)
- **Host**: Remote database server
- **Port**: 5432 (or custom)
- **Connections**: Larger pool (20 + 40 overflow)
- **Storage**: SSD/NVMe for performance
**Considerations**:
- Connection pooling via PgBouncer
- Read replicas for scaling
- Automated backups
- Point-in-time recovery
**Solution**: Environment-based configuration
```bash
# Development (.env)
GIGLEZ_DB_HOST=localhost
GIGLEZ_DB_POOL_SIZE=5
GIGLEZ_DB_MAX_OVERFLOW=5
# Production (.env.production)
GIGLEZ_DB_HOST=db.giglez.com
GIGLEZ_DB_POOL_SIZE=20
GIGLEZ_DB_MAX_OVERFLOW=40
```
---
### 3. File Storage
#### Development (Termux)
- **Location**: `/data/data/com.termux/files/home/giglez/storage/`
- **Structure**: Organized by session/date
- **Size**: Limited by device storage (typically < 64GB)
- **Access**: Direct filesystem access
**Structure**:
```
storage/
├── captures/
│ ├── 2026-01-12/
│ │ ├── session_abc123/
│ │ │ ├── capture_001.sub
│ │ │ ├── capture_002.sub
│ │ │ └── manifest.json
```
#### Production (Server)
- **Location**: Object storage (S3, MinIO, GCS) or networked filesystem
- **Structure**: Content-addressed by file hash
- **Size**: Unlimited (cloud storage)
- **Access**: Via storage API or CDN
**Structure**:
```
s3://giglez-captures/
├── ab/
│ ├── c1/
│ │ └── abc123...def456.sub # First 2+2 chars of hash for sharding
```
**Solution**: Storage abstraction
```python
class StorageBackend(ABC):
@abstractmethod
def save_file(self, file_hash: str, content: bytes): pass
@abstractmethod
def get_file(self, file_hash: str) -> bytes: pass
class LocalStorage(StorageBackend):
"""Development: Filesystem"""
pass
class S3Storage(StorageBackend):
"""Production: S3-compatible storage"""
pass
```
---
### 4. API Access & Security
#### Development (Termux)
- **Access**: Local only (127.0.0.1) or local network
- **Port**: 8000 (or any available)
- **HTTPS**: Not required (localhost)
- **Authentication**: Optional (single user)
- **CORS**: Permissive (development)
- **Rate Limiting**: None needed
**Use Cases**:
- Testing API endpoints
- Web UI development
- Local data management
- Debugging
#### Production (Server)
- **Access**: Public internet
- **Port**: 443 (HTTPS) behind reverse proxy
- **HTTPS**: Required (Let's Encrypt)
- **Authentication**: JWT + API keys mandatory
- **CORS**: Strict origin whitelisting
- **Rate Limiting**: Aggressive (per-user, per-IP)
**Security Requirements**:
- Nginx/Caddy reverse proxy
- SSL/TLS certificates
- API key rotation
- Request validation
- SQL injection prevention
- File upload limits
- DDoS protection
**Solution**: Environment-based security
```python
# config/security.py
if os.getenv("GIGLEZ_MODE") == "production":
REQUIRE_AUTH = True
RATE_LIMIT = "10/minute"
ALLOWED_ORIGINS = ["https://giglez.com"]
else:
REQUIRE_AUTH = False
RATE_LIMIT = None
ALLOWED_ORIGINS = ["*"]
```
---
### 5. Performance & Scaling
#### Development (Termux)
- **Workers**: 1 Uvicorn worker
- **Concurrency**: Minimal (single user)
- **Caching**: In-memory only (LRU)
- **Background Tasks**: Simple queue or synchronous
- **Resource Limits**: RAM < 2GB, CPU 2-4 cores
**Optimization Focus**:
- Memory efficiency
- Minimal background processes
- Battery life considerations
- Offline operation
#### Production (Server)
- **Workers**: Multiple Uvicorn workers (4-8)
- **Concurrency**: High (100+ concurrent users)
- **Caching**: Redis + LRU
- **Background Tasks**: Celery + RabbitMQ/Redis
- **Resource Limits**: RAM 8-32GB, CPU 4-16 cores
**Optimization Focus**:
- Query performance
- Connection pooling
- Horizontal scaling
- CDN for static assets
- Load balancing
**Solution**: Conditional imports
```python
# src/api/main.py
if PRODUCTION_MODE:
from celery import Celery
celery_app = Celery('giglez', broker='redis://localhost')
else:
# Use FastAPI BackgroundTasks for development
celery_app = None
```
---
### 6. Logging & Monitoring
#### Development (Termux)
- **Logging**: Console output (stdout)
- **Level**: DEBUG
- **Format**: Colorized, verbose
- **Storage**: Optional file logging
- **Monitoring**: Manual observation
**Example**:
```python
logger.add(sys.stdout, level="DEBUG", colorize=True)
```
#### Production (Server)
- **Logging**: Structured JSON to file + syslog
- **Level**: INFO/WARNING
- **Format**: JSON for parsing
- **Storage**: Log rotation + cloud storage
- **Monitoring**: Prometheus + Grafana, error tracking (Sentry)
**Example**:
```python
logger.add(
"/var/log/giglez/app.log",
level="INFO",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
rotation="100 MB",
compression="gz"
)
```
**Solution**: Environment-based logging
```python
# config/logging.py
if PRODUCTION_MODE:
setup_production_logging()
else:
setup_development_logging()
```
---
### 7. Backup & Disaster Recovery
#### Development (Termux)
- **Database**: Manual pg_dump to SD card
- **Files**: Synced to cloud storage (Google Drive, Dropbox)
- **Frequency**: On-demand
- **Recovery**: Restore from local backups
#### Production (Server)
- **Database**: Automated daily backups with PITR
- **Files**: Object storage replication (S3 versioning)
- **Frequency**: Hourly incremental, daily full
- **Recovery**: Automated restore procedures
**Backup Strategy**:
```bash
# Development
pg_dump giglez > /sdcard/giglez_backup_$(date +%Y%m%d).sql
# Production
pg_dump giglez | gzip | aws s3 cp - s3://giglez-backups/$(date +%Y%m%d).sql.gz
```
---
### 8. Network Considerations
#### Development (Termux)
- **Connectivity**: WiFi or cellular (intermittent)
- **Bandwidth**: Potentially limited (mobile data)
- **Latency**: Variable (mobile networks)
- **Offline Support**: Critical (wardriving in field)
**Implications**:
- Queue uploads for when network available
- Support offline capture and analysis
- Minimize API calls
- Compress uploads
#### Production (Server)
- **Connectivity**: Always online (datacenter)
- **Bandwidth**: High (1Gbps+)
- **Latency**: Low (< 50ms typical)
- **Offline Support**: Not applicable
**Implications**:
- Assume network availability
- No offline mode needed
- Can make external API calls freely
- Stream large responses
**Solution**: Offline queue
```python
# src/capture/uploader.py
class UploadQueue:
def queue_capture(self, capture_data):
if network_available():
upload_immediately(capture_data)
else:
save_to_local_queue(capture_data)
def process_queue(self):
"""Called when network becomes available"""
for item in get_queued_items():
try:
upload_immediately(item)
mark_as_uploaded(item)
except NetworkError:
break # Stop and try again later
```
---
## Configuration Strategy
### Environment Variables Approach
Use different `.env` files for each environment:
```bash
# .env.development (Termux)
GIGLEZ_MODE=development
GIGLEZ_DB_HOST=localhost
GIGLEZ_DB_POOL_SIZE=5
GIGLEZ_API_HOST=127.0.0.1
GIGLEZ_API_PORT=8000
GIGLEZ_STORAGE_TYPE=filesystem
GIGLEZ_STORAGE_PATH=/data/data/com.termux/files/home/giglez/storage
GIGLEZ_REQUIRE_AUTH=false
GIGLEZ_LOG_LEVEL=DEBUG
GIGLEZ_ENABLE_HARDWARE=true
GIGLEZ_TEMBED_PORT=/dev/ttyUSB0
# .env.production (Server)
GIGLEZ_MODE=production
GIGLEZ_DB_HOST=db.giglez.com
GIGLEZ_DB_POOL_SIZE=20
GIGLEZ_API_HOST=0.0.0.0
GIGLEZ_API_PORT=8000
GIGLEZ_STORAGE_TYPE=s3
GIGLEZ_STORAGE_BUCKET=giglez-captures
GIGLEZ_REQUIRE_AUTH=true
GIGLEZ_LOG_LEVEL=INFO
GIGLEZ_ENABLE_HARDWARE=false
```
### Configuration Loading
```python
# config/settings.py
import os
from enum import Enum
class DeploymentMode(Enum):
DEVELOPMENT = "development"
PRODUCTION = "production"
class Settings:
def __init__(self):
self.mode = DeploymentMode(os.getenv("GIGLEZ_MODE", "development"))
@property
def is_production(self) -> bool:
return self.mode == DeploymentMode.PRODUCTION
@property
def is_development(self) -> bool:
return self.mode == DeploymentMode.DEVELOPMENT
@property
def enable_hardware(self) -> bool:
"""Only enable hardware access in development"""
return os.getenv("GIGLEZ_ENABLE_HARDWARE", "false").lower() == "true"
@property
def storage_backend(self) -> str:
return os.getenv("GIGLEZ_STORAGE_TYPE", "filesystem")
settings = Settings()
```
---
## Recommended Architecture
### Modular Component Design
```
giglez/
├── src/
│ ├── core/ # Shared core (both modes)
│ │ ├── database/ # Database models & queries
│ │ ├── parser/ # .sub file parser
│ │ ├── matcher/ # Signature matching
│ │ ├── gps/ # GPS validation
│ │ └── storage/ # Storage abstraction
│ │
│ ├── capture/ # Capture mode only (Termux)
│ │ ├── tembed.py # T-Embed communication
│ │ ├── scanner.py # Signal scanning
│ │ ├── gps_manager.py # Android GPS integration
│ │ └── uploader.py # Offline queue & upload
│ │
│ ├── api/ # API endpoints (both modes)
│ │ ├── main.py # FastAPI app
│ │ ├── routes/
│ │ │ ├── captures.py # Upload endpoints
│ │ │ ├── devices.py # Device catalog
│ │ │ ├── query.py # Search endpoints
│ │ │ └── stats.py # Statistics
│ │ ├── auth.py # Authentication
│ │ ├── middleware.py # Rate limiting, CORS
│ │ └── dependencies.py # Dependency injection
│ │
│ └── web/ # Web interface (both modes)
│ ├── static/ # CSS, JS, images
│ └── templates/ # HTML templates
└── config/
├── settings.py # Environment-based config
├── database.py # Database connection
├── storage.py # Storage backend selection
└── logging.py # Logging configuration
```
---
## Implementation Recommendations
### 1. Use Feature Flags
```python
# src/api/main.py
from config.settings import settings
app = FastAPI()
# Conditional hardware endpoints
if settings.enable_hardware:
@app.post("/api/capture/start")
async def start_capture():
# Only available in Termux mode
pass
# Always available endpoints
@app.post("/api/captures/upload")
async def upload_captures():
# Available in both modes
pass
```
### 2. Storage Abstraction
```python
# config/storage.py
from src.core.storage import LocalStorage, S3Storage
def get_storage_backend():
if settings.storage_backend == "s3":
return S3Storage(
bucket=os.getenv("GIGLEZ_STORAGE_BUCKET"),
access_key=os.getenv("AWS_ACCESS_KEY_ID"),
secret_key=os.getenv("AWS_SECRET_ACCESS_KEY")
)
else:
return LocalStorage(
base_path=os.getenv("GIGLEZ_STORAGE_PATH")
)
storage = get_storage_backend()
```
### 3. Optional Dependencies
```python
# requirements.txt (core)
fastapi==0.109.0
sqlalchemy==2.0.25
psycopg2-binary==2.9.9
...
# requirements-dev.txt (Termux additions)
-r requirements.txt
pyserial==3.5
aioserial==1.3.2
# requirements-prod.txt (Server additions)
-r requirements.txt
boto3==1.34.0 # S3 storage
redis==5.0.1 # Caching
celery==5.3.4 # Background tasks
gunicorn==21.2.0 # WSGI server
```
### 4. Graceful Degradation
```python
# src/api/routes/captures.py
@app.post("/api/captures/upload")
async def upload_captures(files: List[UploadFile]):
# Core functionality works in both modes
captures = []
for file in files:
# Parse .sub file (works everywhere)
metadata = parse_sub_file(file)
# Store file (abstracted)
storage.save_file(metadata.file_hash, file.content)
# Save to database (works everywhere)
capture = Capture(**metadata)
db.add(capture)
captures.append(capture)
db.commit()
# Background matching (production) or synchronous (development)
if settings.is_production:
celery_app.send_task('match_signatures', args=[capture.file_hash])
else:
match_signatures_sync(capture.file_hash)
return {"uploaded": len(captures)}
```
---
## Testing Strategy
### Development Testing (Termux)
- Direct hardware testing
- USB serial communication
- GPS acquisition
- Offline queue processing
- Local file storage
### Production Testing (Server)
- API endpoint testing
- File upload validation
- Multi-user concurrency
- Rate limiting
- Authentication
- S3 storage integration
### Shared Testing
- Database operations
- .sub file parsing
- Signature matching
- GPS validation
- Query performance
**Test Structure**:
```
tests/
├── unit/ # Unit tests (both modes)
│ ├── test_parser.py
│ ├── test_matcher.py
│ └── test_gps.py
├── integration/ # Integration tests
│ ├── test_api.py # Both modes
│ ├── test_hardware.py # Development only
│ └── test_storage.py # Both modes
└── e2e/ # End-to-end tests
├── test_capture.py # Development only
└── test_upload.py # Both modes
```
---
## Deployment Checklist
### Development (Termux) Setup
- [ ] Install Termux from F-Droid
- [ ] Install PostgreSQL: `pkg install postgresql`
- [ ] Install Python: `pkg install python`
- [ ] Install dependencies: `pip install -r requirements-dev.txt`
- [ ] Configure USB permissions for T-Embed
- [ ] Set up Termux:API for GPS
- [ ] Create `.env.development`
- [ ] Run database setup: `./scripts/setup_database.sh`
- [ ] Test hardware connection
- [ ] Start API: `python src/api/main.py`
### Production (Server) Setup
- [ ] Provision VPS (2GB+ RAM, 2+ cores)
- [ ] Install PostgreSQL 16 + PostGIS
- [ ] Install Python 3.10+
- [ ] Install Redis (for caching)
- [ ] Install Nginx (reverse proxy)
- [ ] Configure SSL with Let's Encrypt
- [ ] Set up S3/MinIO for file storage
- [ ] Configure firewall (allow 80, 443)
- [ ] Create `.env.production`
- [ ] Run database setup
- [ ] Set up systemd service
- [ ] Configure automated backups
- [ ] Set up monitoring (optional)
---
## Performance Considerations
### Development (Termux) Optimizations
- Smaller connection pool (5 + 5)
- In-memory caching only
- Synchronous processing (no Celery)
- Minimal logging
- Local file storage
### Production (Server) Optimizations
- Larger connection pool (20 + 40)
- Redis caching
- Async background tasks (Celery)
- Structured logging with rotation
- CDN for file delivery
- Read replicas for scaling
- Materialized view refresh (cron)
---
## Cost Considerations
### Development (Termux)
- **Hardware**: LilyGo T-Embed (~$30-50)
- **Android Device**: Existing phone/tablet
- **Infrastructure**: $0 (local)
- **Storage**: Device storage only
- **Total**: ~$30-50 one-time
### Production (Server)
- **VPS**: $10-50/month (2-8GB RAM)
- **Database**: Included or $10-30/month (managed)
- **Storage**: $0.02/GB/month (S3) or included (filesystem)
- **Bandwidth**: Typically included (1-5TB)
- **Domain**: $10-15/year
- **SSL**: $0 (Let's Encrypt)
- **Total**: $10-80/month
---
## Recommended Phase 2 Approach
### Build for Both from the Start
**Strategy**: Implement the API with abstraction layers that work in both environments.
**Implementation Order**:
1. ✅ Core API structure (FastAPI app)
2. ✅ Storage abstraction (filesystem + S3)
3. ✅ Upload endpoint (works in both modes)
4. ✅ Authentication (optional in dev, required in prod)
5. ✅ Background tasks (sync in dev, Celery in prod)
6. ✅ Environment-based configuration
**Key Principle**:
> Write code that works in both environments by default, with conditional imports/features for mode-specific functionality.
---
## Summary: Key Differences
| Aspect | Development (Termux) | Production (Server) |
|--------|---------------------|---------------------|
| **Hardware** | T-Embed USB + GPS | None (file uploads) |
| **Users** | Single user | Multi-user |
| **Database** | Local PostgreSQL (small pool) | Remote PostgreSQL (large pool) |
| **Storage** | Local filesystem | S3/object storage |
| **API Access** | localhost only | Public internet |
| **Security** | Optional auth | Required auth + HTTPS |
| **Background Tasks** | Synchronous | Celery async |
| **Caching** | In-memory LRU | Redis + LRU |
| **Logging** | Console (DEBUG) | File + JSON (INFO) |
| **Monitoring** | Manual | Automated (Prometheus) |
| **Scaling** | N/A | Horizontal |
| **Cost** | $0/month | $10-80/month |
---
## Next Steps
Before implementing Phase 2, we need to:
1.**Create environment configuration** (`.env.development`, `.env.production`)
2.**Implement storage abstraction** (`src/core/storage/`)
3.**Create settings module** (`config/settings.py`)
4.**Document deployment procedures** (development guide, production guide)
5.**Begin FastAPI implementation** with environment awareness
**Recommendation**: Start with a **shared API implementation** that works in both modes, then add mode-specific features as needed.
---
**Document Version**: 1.0
**Status**: Ready for Phase 2 Implementation
**Next Action**: Create environment-aware FastAPI application structure