Files
sneakyklaus/docs/decisions/0001-core-technology-stack.md
Phil Skentelbery b077112aba chore: initial project setup
Initialize Sneaky Klaus project with:
- uv package management and pyproject.toml
- Flask application structure (app.py, config.py)
- SQLAlchemy models for Admin and Exchange
- Alembic database migrations
- Pre-commit hooks configuration
- Development tooling (pytest, ruff, mypy)

Initial structure follows design documents in docs/:
- src/app.py: Application factory with Flask extensions
- src/config.py: Environment-based configuration
- src/models/: Admin and Exchange models
- migrations/: Alembic migration setup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 11:28:15 -07:00

180 lines
8.5 KiB
Markdown

# 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/