# Phase 1 Implementation Clarifications Date: 2024-11-20 This document provides specific answers to Developer's clarification questions for Phase 1 implementation. ## 1. Configuration Management - Environment Variables **Decision**: YES - Use the `GONDULF_` prefix for all environment variables. **Complete environment variable specification**: ```bash # Required - no defaults GONDULF_SECRET_KEY= # Database GONDULF_DATABASE_URL=sqlite:///./data/gondulf.db # SMTP Configuration GONDULF_SMTP_HOST=localhost GONDULF_SMTP_PORT=587 GONDULF_SMTP_USERNAME= GONDULF_SMTP_PASSWORD= GONDULF_SMTP_FROM=noreply@example.com GONDULF_SMTP_USE_TLS=true # Token and Code Expiry (seconds) GONDULF_TOKEN_EXPIRY=3600 GONDULF_CODE_EXPIRY=600 # Logging GONDULF_LOG_LEVEL=INFO GONDULF_DEBUG=false ``` **Implementation Requirements**: - Create `.env.example` with all variables documented - Use `python-dotenv` for loading (already in requirements.txt) - Validate `GONDULF_SECRET_KEY` exists on startup (fail fast if missing) - All other variables should have sensible defaults as shown above **See Also**: ADR 0004 - Configuration Management Strategy --- ## 2. Database Schema - Tables for Phase 1 **Decision**: Create exactly THREE tables in Phase 1. ### Table 1: `authorization_codes` ```sql CREATE TABLE authorization_codes ( code TEXT PRIMARY KEY, client_id TEXT NOT NULL, redirect_uri TEXT NOT NULL, state TEXT, code_challenge TEXT, code_challenge_method TEXT, scope TEXT, me TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` ### Table 2: `domains` ```sql CREATE TABLE domains ( domain TEXT PRIMARY KEY, email TEXT NOT NULL, verification_code TEXT NOT NULL, verified BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, verified_at TIMESTAMP ); ``` ### Table 3: `migrations` ```sql CREATE TABLE migrations ( version INTEGER PRIMARY KEY, description TEXT NOT NULL, applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` **Do NOT create**: - Audit tables (use logging instead) - Token tables (Phase 2) - Client tables (Phase 3) **Implementation Requirements**: - Create `src/gondulf/database/migrations/` directory - Create `001_initial_schema.sql` with above schema - Migration runner should track applied migrations in `migrations` table - Use simple sequential versioning: 001, 002, 003, etc. **See Also**: ADR 0005 - Phase 1 Database Schema --- ## 3. In-Memory Storage - Implementation Details **Decision**: Option B - Standard dict with manual expiration check on access. **Rationale**: - Simplest implementation - No background threads or complexity - Codes are short-lived (10 minutes), so memory cleanup isn't critical - Lazy deletion on access is sufficient **Implementation Specification**: ```python class CodeStore: """In-memory storage for domain verification codes with TTL.""" def __init__(self, ttl_seconds: int = 600): self._store: dict[str, tuple[str, float]] = {} self._ttl = ttl_seconds def store(self, email: str, code: str) -> None: """Store verification code with expiry timestamp.""" expiry = time.time() + self._ttl self._store[email] = (code, expiry) def verify(self, email: str, code: str) -> bool: """Verify code and remove from store.""" if email not in self._store: return False stored_code, expiry = self._store[email] # Check expiration if time.time() > expiry: del self._store[email] return False # Check code match if code != stored_code: return False # Valid - remove from store del self._store[email] return True ``` **Expiration cleanup**: On read only. No background cleanup needed. **Configuration**: Use `GONDULF_CODE_EXPIRY=600` (10 minutes default) --- ## 4. Email Service - SMTP TLS/STARTTLS **Decision**: Support both via port-based configuration (Option B variant). **Configuration**: ```bash GONDULF_SMTP_HOST=smtp.gmail.com GONDULF_SMTP_PORT=587 # or 465 for implicit TLS GONDULF_SMTP_USERNAME=user@gmail.com GONDULF_SMTP_PASSWORD=app-password GONDULF_SMTP_FROM=noreply@example.com GONDULF_SMTP_USE_TLS=true ``` **Implementation Logic**: ```python if smtp_port == 465: # Implicit TLS server = smtplib.SMTP_SSL(smtp_host, smtp_port) elif smtp_port == 587 and smtp_use_tls: # STARTTLS server = smtplib.SMTP(smtp_host, smtp_port) server.starttls() else: # Unencrypted (testing only) server = smtplib.SMTP(smtp_host, smtp_port) if smtp_username and smtp_password: server.login(smtp_username, smtp_password) ``` **Defaults**: Port 587 with STARTTLS (most common) **See Also**: ADR 0006 - Email SMTP Configuration --- ## 5. DNS Service - Resolver Configuration **Decision**: Option C - Use system DNS with fallback to public DNS. **Rationale**: - Respects system configuration (good citizenship) - Fallback to reliable public DNS if system fails - No configuration needed for most users - Works in containerized environments **Implementation Specification**: ```python import dns.resolver def create_resolver() -> dns.resolver.Resolver: """Create DNS resolver with system DNS and public fallbacks.""" resolver = dns.resolver.Resolver() # Try system DNS first (resolver.nameservers is already populated) # If you need to explicitly set fallbacks: if not resolver.nameservers: # Fallback to public DNS if system DNS not available resolver.nameservers = ['8.8.8.8', '1.1.1.1'] return resolver ``` **No environment variable needed** - keep it simple and use system defaults. **Timeout configuration**: Use dnspython defaults (2 seconds per nameserver) --- ## 6. Logging Configuration - Log Levels and Format **Decision**: Option B - Standard Python logging with structured fields. **Format**: ``` %(asctime)s [%(levelname)s] %(name)s: %(message)s ``` **Example output**: ``` 2024-11-20 10:30:45,123 [INFO] gondulf.domain: Domain verification requested domain=example.com email=user@example.com 2024-11-20 10:30:46,456 [INFO] gondulf.auth: Authorization code generated client_id=https://app.example.com me=https://example.com ``` **Log Levels**: - **Development** (`GONDULF_DEBUG=true`): `DEBUG` - **Production** (`GONDULF_DEBUG=false`): `INFO` - Configurable via `GONDULF_LOG_LEVEL=INFO|DEBUG|WARNING|ERROR` **Implementation**: ```python import logging # Configure root logger log_level = os.getenv('GONDULF_LOG_LEVEL', 'DEBUG' if debug else 'INFO') logging.basicConfig( level=log_level, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # Get logger for module logger = logging.getLogger('gondulf.domain') # Log with structured information logger.info(f"Domain verification requested domain={domain} email={email}") ``` **Output**: stdout/stderr (let deployment environment handle log files) **See Also**: ADR 0007 - Logging Strategy for v1.0.0 --- ## 7. Health Check Endpoint **Decision**: Option B - Check database connectivity. **Rationale**: - Must verify database is accessible (critical dependency) - Email and DNS are used on-demand, not required for health - Keep it simple - one critical check - Fast response time **Endpoint Specification**: ``` GET /health ``` **Response - Healthy**: ```json { "status": "healthy", "database": "connected" } ``` Status Code: 200 **Response - Unhealthy**: ```json { "status": "unhealthy", "database": "error", "error": "unable to connect to database" } ``` Status Code: 503 **Implementation**: - Execute simple query: `SELECT 1` against database - Timeout: 5 seconds - No authentication required for health check - Log failures at WARNING level --- ## 8. Database File Location **Decision**: Option C - Configurable via `GONDULF_DATABASE_URL` with smart defaults. **Configuration**: ```bash GONDULF_DATABASE_URL=sqlite:///./data/gondulf.db ``` **Path Resolution**: - Relative paths resolved from current working directory - Absolute paths used as-is - Default: `./data/gondulf.db` (relative to cwd) **Data Directory Creation**: ```python from pathlib import Path from urllib.parse import urlparse def ensure_database_directory(database_url: str) -> None: """Create database directory if it doesn't exist.""" if database_url.startswith('sqlite:///'): # Parse path from URL db_path = database_url.replace('sqlite:///', '', 1) db_file = Path(db_path) # Create parent directory if needed db_file.parent.mkdir(parents=True, exist_ok=True) ``` **Call this on application startup** before any database operations. **Deployment Examples**: Development: ```bash GONDULF_DATABASE_URL=sqlite:///./data/gondulf.db ``` Production (Docker): ```bash GONDULF_DATABASE_URL=sqlite:////data/gondulf.db ``` Production (systemd): ```bash GONDULF_DATABASE_URL=sqlite:////var/lib/gondulf/gondulf.db ``` --- ## Summary All 8 questions have been answered with specific implementation details. Key ADRs created: - ADR 0004: Configuration Management - ADR 0005: Phase 1 Database Schema - ADR 0006: Email SMTP Configuration - ADR 0007: Logging Strategy The Developer now has complete, unambiguous specifications to proceed with Phase 1 implementation.