# Requirements ## Functional Requirements ### 1. Storage Management - 2GB default quota per user (soft limits at 80%, cumulative tracking) - Hard limit enforcement when quota exceeded - Cumulative tracking (never resets) - Blob count limits (default: 1000) ### 2. GDPR Compliance - Automatic EU country detection via GeoLite2 database - EU users must store data in EU data centers - Non-EU users can use default backend - Country detection failure must crash application - GeoLite2 database path configurable via `GEOIP_DATABASE_PATH` environment variable - Block uploads until country is set in profile - Provide country consent flow with GDPR alerts ### 3. Multi-Backend Storage - Bunny Storage support (default backend) - DigitalOcean Spaces support (alternative backend) - Real-time cost calculation from backend APIs - 1-hour cost cache with automatic background refresh - Backend selection based on user location (GDPR-compliant) - Automatic storage backups (Bunny built-in: 7-day retention) ### 4. Authentication - JWT for XRPC endpoints (24-hour expiration) - Passkey for admin CLI (terminal-only, no web UI) - Terminal registration via single interactive command with QR code - HTTP endpoint for credential response (from phone) - Age encryption for passkeys database (synced to Bunny Storage) - Session cookies for web UI (7-day expiration) - Session cookies created after country consent - HTTP-only, Secure, SameSite=Lax settings for cookies ### 5. Passkey Management - Terminal-only registration (no web UI) - Single interactive command: `scrtchbk-ctl passkey-register ` - Display QR code in terminal (ASCII art) - Wait for HTTP POST `/auth/passkey/response` from phone - Store credentials in passkeys database - Encrypt passkeys database with age - Sync to Bunny Storage bucket after every registration - Encryption key loaded from `.env.age` environment variable - 5-minute timeout for credential response ### 6. Cost Management - Real-time storage cost per GB (Bunny: $0.01, DO: $0.005) - Real-time bandwidth cost per GB (Bunny: $0.005, DO: $0.008) - Combined total cost calculation - 1-hour cache duration - Automatic background refresh task (tokio) - Cached using `cached` proc macro for memoization - Cache cleared every hour to force recalculation - Individual backend costs cached separately - Combined total cost cached separately ### 7. Admin Controls - CLI tool named `scrtchbk-ctl` - Direct database access (no authentication required) - System statistics command - User management commands (list, view, set quota) - Blob management commands (list, soft delete) - Cleanup command for expired soft-deleted blobs (14-day retention) - Passkey commands (register, sync) - Database query commands - Static binary (separate build.sr.ht) ### 8. Web UI - Homepage with deployment information and real-time cost estimates - Protected self-view page (session cookie auth required) - Country consent flow page - HTMX for dynamic interactions - minijinja templates with Object contexts - 7-day session cookie expiration - HTTP-only, Secure, SameSite=Lax cookie settings ### 9. Configuration - Priority: Environment variables > config.toml > defaults - Age decryption on startup (`.env.age`) - GeoLite2 database path via `GEOIP_DATABASE_PATH` environment variable - Session cookie configuration (7-day expiration) - Separate build.sr.ht for admin CLI ### 10. Deployment - Deployment metadata generated fresh at every startup - Static binary builds via build.sr.ht - No CI/CD (manual deployment to Hetzner VPS) - Deployment info stored in `deployment-info.json` ## Non-Functional Requirements ### 1. Technology Stack - Rust 2021 edition - Axum 0.7 (web framework) - Diesel 2.1 (ORM) - SQLite (database via rsky) - minijinja 2.0 (templates) - age 0.11 (encryption) - cached 0.1 (memoization) - fs-err-tokio 0.2 (error handling) - webauthn-rs 0.5 (passkey) - aws-sdk-s3 1.20 (Bunny/DigitalOcean) - maxminddb 0.24 (GeoIP) - qrcode 0.14 (QR codes) - cookie 0.18 (session cookies) ### 2. Performance - Cost calculations cached for 1 hour - Background refresh task for cache - Async/await throughout - Connection pooling for databases - Real-time API calls to storage backends ### 3. Security - Zero-knowledge (server cannot decrypt user data) - Age encryption at rest (passkeys in Bunny Storage) - HTTPS-only for production - Secure cookies (HTTP-only, Secure flag, SameSite=Lax) - JWT tokens (24-hour expiration) - Session tokens (7-day expiration) - Separate passkeys database for admin credentials ### 4. Reliability - Bunny Storage 99.95% uptime SLA - DigitalOcean Spaces 99.9% uptime SLA - Soft deletion with 14-day retention (grace period for recovery) - Automatic storage backups (Bunny built-in, 7-day retention) - GeoIP detection with crash on failure (admin must configure) ### 5. Usability - Terminal-only passkey registration with QR codes - Clear error messages for configuration failures - GDPR consent flow with storage restrictions explained - Mobile-responsive HTMX interface - Real-time cost estimates on homepage ### 6. Maintainability - Separate admin CLI from web server - Comprehensive documentation - Diesel migrations for schema changes - Build.sr.ht for reproducible deployments - Error handling with miette for detailed diagnostics ### 7. Scalability - Dual storage backends (Bunny + DigitalOcean) - GDPR-compliant region routing - Cost cache reduces API calls - Connection pooling for databases - Background tasks for non-blocking operations ## Constraints - Budget: $80/month maximum - Hetzner EU VPS: $5.50 - DigitalOcean US VPS: $12 (upgraded) - Bunny Storage: $11 total (separate EU/US buckets) - Sentry: $15 (team plan) - uptime.io: $7 - Elastic Email: $5 - Turso: $10 (for metadata only) - Storage backends must be S3-compatible - Passkey registration must be terminal-only - GeoIP database must be configured before starting - Application must crash if GeoIP detection fails - Session cookies must expire after 7 days - Soft-deleted blobs must be retained for 14 days - Cost cache must refresh every hour - Passkeys must sync to Bunny Storage after registration - Age encryption key must be in `.env.age`