init commit
This commit is contained in:
635
docs/ARCHITECTURE.md
Normal file
635
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,635 @@
|
||||
# 🏗️ API-First Zero-Trust Architecture - Complete Guide
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
1. [Architecture Overview](#architecture-overview)
|
||||
2. [Security Model](#security-model)
|
||||
3. [Authentication Flows](#authentication-flows)
|
||||
4. [RBAC & Permissions](#rbac--permissions)
|
||||
5. [API Endpoints](#api-endpoints)
|
||||
6. [Telegram Bot Integration](#telegram-bot-integration)
|
||||
7. [Testing Strategy](#testing-strategy)
|
||||
8. [Deployment](#deployment)
|
||||
9. [Production Checklist](#production-checklist)
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
### System Components
|
||||
|
||||
```
|
||||
EXTERNAL CLIENTS (Web, Mobile, Bot)
|
||||
↓
|
||||
API GATEWAY (FastAPI)
|
||||
↓
|
||||
MIDDLEWARE STACK
|
||||
├─ Security Headers
|
||||
├─ Rate Limiting
|
||||
├─ HMAC Verification
|
||||
├─ JWT Authentication
|
||||
├─ RBAC Authorization
|
||||
└─ Request Logging
|
||||
↓
|
||||
DOMAIN SERVICES
|
||||
├─ AuthService
|
||||
├─ TransactionService
|
||||
├─ WalletService
|
||||
├─ BudgetService
|
||||
└─ NotificationService
|
||||
↓
|
||||
REPOSITORY LAYER (SQLAlchemy)
|
||||
↓
|
||||
DATABASE + REDIS + MESSAGE QUEUE
|
||||
```
|
||||
|
||||
### Key Principles
|
||||
|
||||
| Principle | Implementation |
|
||||
|-----------|-----------------|
|
||||
| **Zero-Trust** | Every request requires JWT + HMAC verification |
|
||||
| **Immutability** | No direct record deletion; use reversals + audit logs |
|
||||
| **Isolation** | Family-level data isolation at service layer |
|
||||
| **Observability** | Every action logged to event_log + access_log |
|
||||
| **Stateless** | API calls don't depend on session state |
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Model
|
||||
|
||||
### Token Types
|
||||
|
||||
#### 1. **Access Token (JWT)**
|
||||
- **Purpose:** Authenticate API requests
|
||||
- **Lifetime:** 15 minutes
|
||||
- **Scope:** Contains family_ids user can access
|
||||
- **Usage:**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
#### 2. **Refresh Token**
|
||||
- **Purpose:** Issue new access tokens without re-login
|
||||
- **Lifetime:** 30 days
|
||||
- **Usage:**
|
||||
```
|
||||
POST /api/v1/auth/refresh
|
||||
{ "refresh_token": "..." }
|
||||
```
|
||||
|
||||
#### 3. **Service Token**
|
||||
- **Purpose:** Telegram bot → API communication
|
||||
- **Lifetime:** 1 year
|
||||
- **Scope:** "telegram_bot" service
|
||||
- **Note:** Different from user tokens; issued separately
|
||||
|
||||
### HMAC Signature Verification
|
||||
|
||||
**Base String Format:**
|
||||
```
|
||||
METHOD:ENDPOINT:TIMESTAMP:BODY_HASH
|
||||
POST:/api/v1/transactions:1702237800:a3f5d8c2e1b9...
|
||||
```
|
||||
|
||||
**Headers Required:**
|
||||
```
|
||||
X-Signature: HMAC_SHA256(base_string, client_secret)
|
||||
X-Timestamp: 1702237800
|
||||
X-Client-Id: telegram_bot|web_frontend|ios_app
|
||||
```
|
||||
|
||||
**Verification Steps:**
|
||||
1. Check timestamp freshness (±30 seconds)
|
||||
2. Reconstruct base_string
|
||||
3. Compute HMAC with client secret
|
||||
4. Compare with X-Signature
|
||||
5. Check signature nonce (prevent replay)
|
||||
|
||||
**MVP Default:** HMAC disabled (`require_hmac_verification=false`)
|
||||
|
||||
### Encryption Strategy
|
||||
|
||||
| Data | Encryption | Notes |
|
||||
|------|-----------|-------|
|
||||
| Password Hash | bcrypt | Never store plain passwords |
|
||||
| Phone Number | AES-256 | At rest, logged as masked |
|
||||
| Notes/Descriptions | None (MVP) | Can add AES-256 for sensitive notes |
|
||||
| Transit | HTTPS TLS 1.2+ | Enforced in production |
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Authentication Flows
|
||||
|
||||
### User Login Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 1. User submits credentials │
|
||||
│ POST /api/v1/auth/login │
|
||||
│ { "email": "user@example.com", "password": "..." } │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 2. Server verifies password hash with bcrypt │
|
||||
│ Load user → Load family_ids → Create tokens │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 3. Response │
|
||||
│ { │
|
||||
│ "access_token": "eyJhbGc...", │
|
||||
│ "refresh_token": "eyJhbGc...", │
|
||||
│ "user_id": 123, │
|
||||
│ "expires_in": 900 │
|
||||
│ } │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Telegram Binding Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 1: User sends /start │
|
||||
│ Bot chat_id: 12345 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 2: Bot generates binding code │
|
||||
│ POST /api/v1/auth/telegram/start │
|
||||
│ Response: { "code": "ABC123XYZ...", "expires_in": 600 } │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 3: Bot sends link to user │
|
||||
│ "Click: https://app.com/auth/telegram?code=ABC123&... │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 4: User clicks link │
|
||||
│ (User must be logged in or create account) │
|
||||
│ POST /api/v1/auth/telegram/confirm │
|
||||
│ { "code": "ABC123", "chat_id": 12345 } │
|
||||
│ Headers: Authorization: Bearer <user_jwt> │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 5: Server creates TelegramIdentity record │
|
||||
│ TelegramIdentity { │
|
||||
│ user_id: 123, │
|
||||
│ chat_id: 12345, │
|
||||
│ verified_at: now │
|
||||
│ } │
|
||||
│ │
|
||||
│ Generate JWT for bot usage │
|
||||
│ Response: { "jwt_token": "..." } │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 6: Bot stores JWT in Redis │
|
||||
│ Redis key: chat_id:12345:jwt │
|
||||
│ Redis key: chat_id:12345:jwt:exp │
|
||||
│ TTL: 30 days │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ STEP 7: Bot can now make API calls │
|
||||
│ POST /api/v1/transactions │
|
||||
│ Authorization: Bearer <jwt_from_redis> │
|
||||
│ X-Client-Id: telegram_bot │
|
||||
│ X-Signature: HMAC_SHA256(...) │
|
||||
│ X-Timestamp: unixtime │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👥 RBAC & Permissions
|
||||
|
||||
### Role Hierarchy
|
||||
|
||||
```
|
||||
OWNER (Full Control)
|
||||
└─ Can manage everything
|
||||
└─ Can approve/reject large transactions
|
||||
└─ Can manage members
|
||||
└─ Can delete family
|
||||
|
||||
ADULT
|
||||
└─ Can create/edit transactions
|
||||
└─ Can approve transactions (with owner)
|
||||
└─ Can create budgets/goals
|
||||
└─ Can invite members
|
||||
|
||||
MEMBER
|
||||
└─ Can create/view own transactions
|
||||
└─ Can view budgets/goals
|
||||
└─ Can view shared reports
|
||||
|
||||
CHILD
|
||||
└─ Can create/view limited transactions
|
||||
└─ Can view their allowance
|
||||
└─ Very restricted permissions
|
||||
|
||||
READ_ONLY
|
||||
└─ Can view reports only
|
||||
└─ Audit/observer role
|
||||
```
|
||||
|
||||
### Permission Examples
|
||||
|
||||
| Action | Owner | Adult | Member | Child | Read-Only |
|
||||
|--------|-------|-------|--------|-------|-----------|
|
||||
| Create Transaction | ✓ | ✓ | ✓ | ✓ (limited) | ✗ |
|
||||
| Edit Own Transaction | ✓ | ✓ | ✓ | ✗ | ✗ |
|
||||
| Edit Any Transaction | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||
| Delete Transaction | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||
| Approve Transaction | ✓ | ✓ | ✗ | ✗ | ✗ |
|
||||
| Create Budget | ✓ | ✓ | ✗ | ✗ | ✗ |
|
||||
| Manage Members | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||
| View Audit Log | ✓ | ✓ | ✓ | ✗ | ✓ |
|
||||
| Delete Family | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||
|
||||
### RBAC Implementation
|
||||
|
||||
**In Code:**
|
||||
```python
|
||||
# Check permission
|
||||
RBACEngine.check_permission(user_context, Permission.CREATE_TRANSACTION)
|
||||
|
||||
# Check family access
|
||||
RBACEngine.check_family_access(user_context, family_id=1)
|
||||
|
||||
# Check resource ownership
|
||||
RBACEngine.check_resource_ownership(user_context, owner_id=123)
|
||||
```
|
||||
|
||||
**In Endpoint:**
|
||||
```python
|
||||
@router.post("/transactions")
|
||||
async def create_transaction(
|
||||
request: TransactionCreateRequest,
|
||||
user_context: UserContext = Depends(get_user_context),
|
||||
):
|
||||
# Middleware already verified JWT
|
||||
# RBAC middleware already checked family access
|
||||
# Now just check specific permission
|
||||
|
||||
RBACEngine.check_permission(
|
||||
user_context,
|
||||
Permission.CREATE_TRANSACTION
|
||||
)
|
||||
|
||||
# Proceed with business logic
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📡 API Endpoints
|
||||
|
||||
### Authentication
|
||||
|
||||
| Method | Endpoint | Purpose |
|
||||
|--------|----------|---------|
|
||||
| POST | `/api/v1/auth/login` | User login |
|
||||
| POST | `/api/v1/auth/refresh` | Refresh access token |
|
||||
| POST | `/api/v1/auth/logout` | Revoke session |
|
||||
| POST | `/api/v1/auth/telegram/start` | Generate binding code |
|
||||
| POST | `/api/v1/auth/telegram/confirm` | Confirm Telegram binding |
|
||||
|
||||
### Transactions
|
||||
|
||||
| Method | Endpoint | Purpose | Auth |
|
||||
|--------|----------|---------|------|
|
||||
| POST | `/api/v1/transactions` | Create transaction | JWT + HMAC |
|
||||
| GET | `/api/v1/transactions` | List transactions | JWT |
|
||||
| GET | `/api/v1/transactions/{id}` | Get transaction | JWT |
|
||||
| POST | `/api/v1/transactions/{id}/confirm` | Approve pending | JWT + HMAC |
|
||||
| DELETE | `/api/v1/transactions/{id}` | Reverse transaction | JWT + HMAC |
|
||||
|
||||
### Wallets
|
||||
|
||||
| Method | Endpoint | Purpose |
|
||||
|--------|----------|---------|
|
||||
| POST | `/api/v1/wallets` | Create wallet |
|
||||
| GET | `/api/v1/wallets` | List wallets |
|
||||
| GET | `/api/v1/wallets/summary` | Balance summary |
|
||||
| PUT | `/api/v1/wallets/{id}` | Update wallet |
|
||||
|
||||
### Budgets & Goals
|
||||
|
||||
| Method | Endpoint | Purpose |
|
||||
|--------|----------|---------|
|
||||
| POST | `/api/v1/budgets` | Create budget |
|
||||
| GET | `/api/v1/budgets` | List budgets |
|
||||
| POST | `/api/v1/goals` | Create goal |
|
||||
| GET | `/api/v1/goals` | List goals |
|
||||
|
||||
### Events & Webhooks
|
||||
|
||||
| Method | Endpoint | Purpose |
|
||||
|--------|----------|---------|
|
||||
| GET | `/api/v1/events/stream/{family_id}` | WebSocket event stream |
|
||||
| POST | `/api/v1/events/webhook/telegram-notification` | Send Telegram notification |
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Telegram Bot Integration
|
||||
|
||||
### Bot Commands
|
||||
|
||||
```
|
||||
/start - Begin account binding
|
||||
/help - Show available commands
|
||||
/balance - View wallet balances
|
||||
/add - Add new transaction
|
||||
/reports - View financial reports
|
||||
```
|
||||
|
||||
### Bot API Communication Pattern
|
||||
|
||||
```python
|
||||
# Get user JWT from Redis
|
||||
jwt_token = redis.get(f"chat_id:{chat_id}:jwt")
|
||||
|
||||
# Make API request
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
"https://api.example.com/api/v1/transactions",
|
||||
headers={
|
||||
"Authorization": f"Bearer {jwt_token}",
|
||||
"X-Client-Id": "telegram_bot",
|
||||
"X-Signature": hmac_signature,
|
||||
"X-Timestamp": str(timestamp),
|
||||
},
|
||||
json=payload,
|
||||
) as response:
|
||||
result = await response.json()
|
||||
```
|
||||
|
||||
### Event Handling
|
||||
|
||||
```
|
||||
Bot listens to Redis Streams:
|
||||
- transaction.created
|
||||
- budget.alert
|
||||
- goal.completed
|
||||
- member.joined
|
||||
|
||||
Bot processes events → Sends Telegram messages
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- JWT token generation/verification
|
||||
- HMAC signature creation/verification
|
||||
- RBAC permission checks
|
||||
- Service business logic
|
||||
|
||||
### Integration Tests
|
||||
- Full request → response cycles
|
||||
- Authentication flows
|
||||
- RBAC in middleware
|
||||
- Database transactions
|
||||
|
||||
### Security Tests
|
||||
- Invalid token rejection
|
||||
- HMAC signature verification
|
||||
- Timestamp freshness
|
||||
- Signature replay prevention
|
||||
- Family isolation
|
||||
|
||||
### Load Testing Example (k6)
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
|
||||
export let options = {
|
||||
vus: 10,
|
||||
duration: '30s',
|
||||
};
|
||||
|
||||
export default function() {
|
||||
let url = 'http://localhost:8000/api/v1/wallets';
|
||||
let params = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${__ENV.JWT_TOKEN}`,
|
||||
'X-Client-Id': 'k6_test',
|
||||
},
|
||||
};
|
||||
|
||||
let res = http.get(url, params);
|
||||
check(res, {
|
||||
'status is 200': (r) => r.status === 200,
|
||||
'response time < 500ms': (r) => r.timings.duration < 500,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### Docker Compose (MVP)
|
||||
|
||||
```yaml
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
api:
|
||||
image: finance-bot:latest
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://...
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- JWT_SECRET_KEY=...
|
||||
- HMAC_SECRET_KEY=...
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
networks:
|
||||
- finance
|
||||
|
||||
bot:
|
||||
image: finance-bot-bot:latest
|
||||
environment:
|
||||
- BOT_TOKEN=...
|
||||
- API_BASE_URL=http://api:8000
|
||||
- REDIS_URL=redis://redis:6379
|
||||
depends_on:
|
||||
- api
|
||||
- redis
|
||||
networks:
|
||||
- finance
|
||||
|
||||
worker:
|
||||
image: finance-bot-worker:latest
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- DATABASE_URL=postgresql://...
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
networks:
|
||||
- finance
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: finance_db
|
||||
POSTGRES_USER: trevor
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
networks:
|
||||
- finance
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
networks:
|
||||
- finance
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
|
||||
networks:
|
||||
finance:
|
||||
```
|
||||
|
||||
### Kubernetes Deployment (Future)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: api-config
|
||||
data:
|
||||
JWT_SECRET_KEY: <secret>
|
||||
HMAC_SECRET_KEY: <secret>
|
||||
DATABASE_URL: postgresql://postgres/finance_db
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: api-server
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
image: finance-bot:latest
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: api-config
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Production Checklist
|
||||
|
||||
### Security
|
||||
- [ ] Change JWT_SECRET_KEY from default
|
||||
- [ ] Change HMAC_SECRET_KEY from default
|
||||
- [ ] Enable HTTPS/TLS (Nginx reverse proxy)
|
||||
- [ ] Enable CORS only for approved origins
|
||||
- [ ] Set require_hmac_verification=true
|
||||
- [ ] Implement password hashing (bcrypt)
|
||||
- [ ] Implement token blacklisting
|
||||
- [ ] Add rate limiting (current: 100 req/min)
|
||||
- [ ] Enable audit logging (EventLog)
|
||||
- [ ] Encrypt sensitive data at rest
|
||||
|
||||
### Deployment
|
||||
- [ ] Run migrations in production
|
||||
- [ ] Set app_env="production"
|
||||
- [ ] Disable debug mode
|
||||
- [ ] Configure proper logging
|
||||
- [ ] Set up monitoring/alerts
|
||||
- [ ] Configure backup strategy
|
||||
- [ ] Test failover procedures
|
||||
- [ ] Document runbooks
|
||||
|
||||
### Testing
|
||||
- [ ] Unit tests coverage > 80%
|
||||
- [ ] Integration tests for critical flows
|
||||
- [ ] Security testing (OWASP Top 10)
|
||||
- [ ] Load testing (identify bottlenecks)
|
||||
- [ ] Penetration testing
|
||||
- [ ] API contract testing
|
||||
|
||||
### Operations
|
||||
- [ ] Set up CI/CD pipeline
|
||||
- [ ] Configure health check endpoints
|
||||
- [ ] Set up application monitoring
|
||||
- [ ] Configure database backups
|
||||
- [ ] Document API in OpenAPI/Swagger
|
||||
- [ ] Set up error tracking (Sentry)
|
||||
- [ ] Implement graceful shutdown
|
||||
|
||||
---
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
### OpenAPI Specification
|
||||
```bash
|
||||
# Auto-generated from FastAPI
|
||||
GET /docs # Swagger UI
|
||||
GET /redoc # ReDoc documentation
|
||||
GET /openapi.json # OpenAPI spec
|
||||
```
|
||||
|
||||
### Architecture Decision Records (ADR)
|
||||
- ADR-001: JWT + HMAC for authentication (not just JWT)
|
||||
- ADR-002: Redis Streams for event bus (vs RabbitMQ)
|
||||
- ADR-003: Compensation transactions for reversals
|
||||
- ADR-004: Family-level isolation in all queries
|
||||
|
||||
### Performance Targets
|
||||
- API response time: < 200ms (p95)
|
||||
- Transaction creation: < 100ms
|
||||
- List queries: < 500ms (for 1000 items)
|
||||
- HMAC verification: < 5ms
|
||||
- JWT verification: < 2ms
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Roadmap (Post-MVP)
|
||||
|
||||
### Phase 2: Enhanced Features
|
||||
- [ ] Web Frontend (React/Vue)
|
||||
- [ ] Mobile App (React Native/Flutter)
|
||||
- [ ] Advanced reporting (PDF export, charts)
|
||||
- [ ] Recurring transactions
|
||||
- [ ] Currency conversion
|
||||
- [ ] Multi-family support
|
||||
- [ ] User notifications preferences
|
||||
|
||||
### Phase 3: Enterprise Features
|
||||
- [ ] Kubernetes deployment
|
||||
- [ ] Multi-region failover
|
||||
- [ ] Advanced RBAC (custom roles)
|
||||
- [ ] Audit webhook integrations
|
||||
- [ ] API rate limiting (per-user)
|
||||
- [ ] Data export (GDPR compliance)
|
||||
- [ ] SSO integration (OAuth2)
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2025-12-10
|
||||
**Status:** MVP Complete
|
||||
Reference in New Issue
Block a user