# 0001. Core Technology Stack Date: 2025-12-22 ## Status Accepted (Updated 2025-12-22) ## Context Sneaky Klaus is a self-hosted Secret Santa organization application designed for individuals, families, and small organizations who want full control over their data. The application must be: - Easy to self-host via containerization - Simple to deploy and maintain - Minimal in external dependencies - Suitable for small-scale usage (dozens of participants, not thousands) - Functional without complex infrastructure Key requirements that inform technology choices: 1. **Self-hosting first**: Users should be able to deploy with a single container 2. **Simplicity**: The tech stack should be straightforward and well-documented 3. **No participant accounts**: Authentication must support passwordless magic links 4. **Email delivery**: Must send transactional emails reliably 5. **Background jobs**: Must handle scheduled tasks (reminders, data purging) 6. **Data persistence**: Must store user data reliably but doesn't need high-scale database features ## Decision We will use the following core technology stack: | Component | Technology | Version Constraint | |-----------|------------|-------------------| | **Backend Framework** | Flask | ^3.0 | | **Language** | Python | ^3.11 | | **Database** | SQLite | ^3.40 (via Python stdlib) | | **Email Service** | Resend | Latest SDK | | **Deployment** | Docker | Latest | | **Package Manager** | uv | Latest | | **Background Jobs** | APScheduler | ^3.10 | | **Template Engine** | Jinja2 | ^3.1 (Flask default) | | **WSGI Server** | Gunicorn | ^21.0 (production) | | **Form Handling** | Flask-WTF (includes WTForms) | ^1.2 | | **Session Management** | Flask-Session | ^0.8 | | **Timezone Validation** | pytz | Latest | | **CSS Framework** | Pico CSS | Latest (via CDN) | ### Key Technology Rationale **Flask**: Lightweight, well-documented, excellent for small-to-medium applications. Large ecosystem, straightforward patterns, and no unnecessary complexity. **Python 3.11+**: Modern Python with performance improvements, excellent type hinting support, and active security support. **SQLite**: Perfect for self-hosted applications. Zero-configuration, single-file database, excellent for read-heavy workloads with occasional writes. Eliminates need for separate database server. **Resend**: Modern transactional email API with excellent deliverability, simple API, and reasonable pricing for small-scale usage. **Docker**: Industry-standard containerization. Single container deployment simplifies self-hosting significantly. **uv**: Fast, modern Python package manager and project manager. Significantly faster than pip, with better dependency resolution and lockfile support. **APScheduler**: In-process job scheduling. Eliminates need for separate job queue infrastructure (Redis, Celery) while still supporting background tasks like reminder emails and data purging. **Jinja2**: Flask's default templating engine. Server-side rendering eliminates need for frontend JavaScript framework, simplifying deployment and maintenance. **Gunicorn**: Production-ready WSGI server for Flask applications. Well-tested, stable, and appropriate for the scale of this application. **Flask-WTF**: Integrates WTForms with Flask, providing form validation, CSRF protection, and secure form handling. Industry-standard for Flask applications. **Flask-Session**: Server-side session management for Flask. Stores session data in SQLite, providing secure session handling without client-side storage concerns. **pytz**: Standard Python library for timezone validation and handling. Required for validating IANA timezone names in exchange configurations. **Pico CSS**: Minimal, classless CSS framework delivered via CDN. Provides clean, semantic styling without requiring a build step or complex class names. Fully responsive and accessible out of the box. ### Frontend Approach **Pure server-side rendering** with Jinja2 templates. No JavaScript framework (React, Vue, etc.). This decision: - Eliminates build tooling complexity - Reduces deployment artifacts (no separate frontend bundle) - Simplifies security (no client-side state management) - Ensures full functionality without JavaScript enabled - Maintains mobile-friendliness through responsive CSS Progressive enhancement with minimal JavaScript for interactivity (copy-to-clipboard, form validation) is acceptable but not required for core functionality. ## Consequences ### Positive - **Simple deployment**: Single container with no external service dependencies (except Resend for email) - **Low resource requirements**: SQLite and in-process job scheduling minimize memory and CPU usage - **Fast development**: Flask's simplicity and Jinja2's straightforward templating accelerate development - **Easy debugging**: All code runs in single process, simplifying troubleshooting - **Predictable performance**: Server-side rendering is fast and consistent - **No build step**: Templates render directly; no frontend compilation required - **Security by default**: Server-side rendering reduces attack surface compared to client-side SPAs - **Excellent for scale target**: Perfect for dozens to hundreds of participants per deployment ### Negative - **SQLite limitations**: Not suitable if application needs to scale to thousands of concurrent users (not a concern for target use case) - **No horizontal scaling**: Single SQLite file prevents multi-instance deployment (acceptable trade-off for simplicity) - **Email vendor lock-in**: Resend is the only supported email provider (could be abstracted later if needed) - **APScheduler constraints**: Job scheduling tied to application process lifetime; jobs don't survive application restarts (acceptable for reminder scheduling) - **Less interactive UI**: Server-side rendering means no SPA-style instant interactivity (acceptable trade-off for simplicity) ### Neutral - **Python expertise required**: Development requires Python knowledge (expected for Flask application) - **Database portability**: SQLite schema could be migrated to PostgreSQL if scaling needs change, but would require development effort - **Email testing**: Requires Resend account for development (free tier available) or mocking in tests ## Implementation Notes ### Database Considerations SQLite will be configured with: - WAL (Write-Ahead Logging) mode for better concurrency - Foreign keys enabled - Appropriate timeout for locked database scenarios - Regular backups recommended via volume mounts ### Job Scheduling Considerations APScheduler will run in-process with: - JobStore backed by SQLite for job persistence across restarts (for scheduled jobs) - Executor using thread pool for background tasks - Misfire grace time configured appropriately for reminders ### Email Configuration Resend integration will: - Store API key in environment variable (not in code) - Support template-based emails - Handle failures gracefully with logging - Rate limit appropriately ### Development vs Production - **Development**: Flask development server, SQLite in local file - **Production**: Gunicorn with multiple workers, SQLite in mounted volume, proper logging ## Alternatives Considered ### Database Alternatives **PostgreSQL**: More scalable but requires separate database container/service, significantly complicating self-hosting. Overkill for target scale. **MySQL/MariaDB**: Same drawbacks as PostgreSQL for this use case. ### Job Queue Alternatives **Celery + Redis**: More robust job processing but requires Redis container, significantly complicating deployment. Overkill for reminder emails and daily data purging tasks. **Cron + separate script**: Could work but fragments application logic and complicates deployment. ### Email Service Alternatives **SendGrid**: Viable alternative but more complex API and pricing structure. **Amazon SES**: Requires AWS account and more complex setup. Higher barrier for self-hosters. **SMTP**: Requires users to configure their own SMTP server, significantly increasing setup complexity and deliverability issues. ### Frontend Alternatives **React/Vue SPA**: Considered but rejected. Would require build tooling, increase deployment complexity, and provide minimal benefit for the application's relatively simple UI needs. **HTMX**: Considered for progressive enhancement. May be added later but not required for MVP. ## References - Flask documentation: https://flask.palletsprojects.com/ - SQLite documentation: https://www.sqlite.org/docs.html - Resend documentation: https://resend.com/docs - APScheduler documentation: https://apscheduler.readthedocs.io/ - uv documentation: https://docs.astral.sh/uv/