# Research: Anonymous Feedback Platform (Reklamator) **Branch**: `001-build-an-application` | **Date**: 2025-10-15 This document resolves all NEEDS CLARIFICATION items identified in the Technical Context section of plan.md. ## 1. AI Integration Approach ### Decision: Pluggable AI provider interface with Claude as default **Rationale**: - User input specifies "we use claude but it can be any other service as well" - Designing for extensibility aligns with good architectural practice - Enables future migration to different AI providers without major refactoring **Implementation Approach**: - Abstract base class `AIAnalyzer` defining interface: `analyze_feedback(text: str, target_lang: str) -> AnalysisResult` - Concrete implementation `ClaudeAnalyzer` using Anthropic API - Configuration-driven provider selection - API key management via environment variables **Alternatives Considered**: - **Hard-coded Claude API integration**: Simpler initially but violates user requirement for provider flexibility - **LangChain framework**: Adds significant dependency weight for simple translation/categorization task - **Multiple provider implementations from start**: Premature complexity - implement Claude first, abstract as needed **Claude API Specifics**: - Use `anthropic` Python SDK - Model: `claude-3-haiku-20240307` for cost-effective analysis (fast, sufficient for categorization/translation) - Prompt design: Single API call with structured output for category, summary, translation - Error handling: Retry logic for transient failures, graceful degradation for persistent errors **Research References**: - Anthropic API Documentation: https://docs.anthropic.com/ - Python SDK: https://github.com/anthropics/anthropic-sdk-python --- ## 2. File Upload - Malware Scanning Approach ### Decision: ClamAV integration via clamd for virus scanning **Rationale**: - FR-060 requires malware scanning before storage - ClamAV is open-source, widely used, actively maintained - `clamd` provides Python bindings for integration - Suitable for on-premise deployment matching file-based storage philosophy **Implementation Approach**: - Install ClamAV daemon (`clamd`) as system service - Use `clamd` Python library for scanning uploaded files - Scan files synchronously during upload before writing to disk - Reject files that fail virus scan with clear error message - Log scanning failures for security monitoring **Configuration**: - Maximum file size: 10MB per file (FR-006) - Allowed extensions: `.pdf`, `.docx`, `.txt`, `.jpg`, `.png`, `.gif`, `.webp` - MIME type validation in addition to extension checking - Temporary upload storage cleaned after scan (pass or fail) **Alternatives Considered**: - **Cloud-based scanning (VirusTotal API)**: Violates anonymity requirement (uploads data externally), adds latency - **No scanning**: Violates FR-060 security requirement - **Manual review**: Not scalable, delays feedback processing - **Python-based scanning (yara-python)**: More complex to configure, less comprehensive than ClamAV **Dependencies**: - `clamd` Python library - ClamAV daemon installed on server --- ## 3. File Upload - Storage Location Strategy ### Decision: Local filesystem storage in `data/products/{product-id}/feedback/{feedback-id}/attachments/` **Rationale**: - Aligns with file-based storage architecture (no database) - User input specifies "folders foreach user input" and "files in a folder" - Simple to implement, backup, and inspect - No additional service dependencies - Sufficient for MVP scale (100 products × 10k items × 3 files × 10MB = ~30TB worst case) **Directory Structure**: ``` data/ └── products/ └── {product-id}/ # e.g., "001-acme-app" ├── config.yaml # Product metadata (name, target language, owners) └── feedback/ └── {feedback-id}/ # UUID v4, e.g., "a3f2c1d5-..." ├── metadata.yaml # Feedback metadata (timestamp, status, category, etc.) ├── content.txt # Original feedback text ├── analysis.md # AI-generated analysis report └── attachments/ ├── original_filename_1.pdf ├── original_filename_2.png └── original_filename_3.jpg ``` **File Naming**: - Preserve original filenames to maintain user context - Sanitize filenames to prevent directory traversal (strip `../`, absolute paths, etc.) - Handle duplicate filenames by appending counter if needed **Alternatives Considered**: - **Cloud storage (S3/GCS)**: Adds external dependency, cost, complexity; overkill for MVP - **Flat directory per product**: Poor scalability, difficult to organize metadata - **Database with BLOB storage**: Contradicts file-based storage decision, adds DB complexity - **Content-addressed storage (hash-based filenames)**: Loses original filename context, complicates presentation **Backup Strategy** (out of scope for MVP but noted): - Simple filesystem backup via rsync/tar sufficient - Can upgrade to cloud sync if needed later --- ## 4. Authentication - Session Management Approach ### Decision: Flask-Login with server-side sessions for product owners/admins **Rationale**: - Flask-Login is standard, well-tested session management for Flask - Server-side sessions prevent token tampering - Simple username/password authentication sufficient for MVP - FR-056, FR-063 require authentication and secure password storage **Implementation Approach**: - Use `Flask-Login` extension for session management - Store user credentials in simple YAML file (products/users.yaml) for MVP consistency with file-based approach - Hash passwords with `bcrypt` (FR-063) - Session cookies: `HttpOnly`, `Secure` (HTTPS only), `SameSite=Lax` - Session timeout: 24 hours of inactivity **User Model**: ```yaml users: - id: "admin-001" email: "admin@example.com" password_hash: "$2b$12$..." role: "admin" assigned_products: [] # Empty = all products access - id: "owner-001" email: "owner@example.com" password_hash: "$2b$12$..." role: "product_owner" assigned_products: ["001-acme-app", "002-beta-service"] ``` **Access Control**: - Admins: Full access to all products, can manage products/owners - Product Owners: Read-only access to assigned products only (FR-033) - Anonymous users: Submission form access only (no authentication) **Alternatives Considered**: - **JWT tokens**: More complex, unnecessary for server-rendered HTML application - **OAuth/SAML**: Over-engineered for MVP, adds external identity provider dependency - **Database-backed sessions**: Contradicts file-based architecture - **No authentication**: Violates FR-056 requirement **Dependencies**: - `Flask-Login` extension - `bcrypt` for password hashing --- ## 5. Rate Limiting - Implementation Strategy ### Decision: Flask-Limiter with IP-based rate limiting for submission endpoint **Rationale**: - FR-061 requires rate limiting (suggested: 10 submissions/hour/IP) - Flask-Limiter is standard, well-maintained Flask extension - IP-based limiting suitable for anonymous submissions - In-memory storage sufficient for MVP (single server deployment) **Implementation Approach**: - Use `Flask-Limiter` extension - Apply rate limit decorator to submission route: `@limiter.limit("10 per hour")` - Storage backend: In-memory (default) for MVP - Return HTTP 429 Too Many Requests with clear error message - Exempt authenticated admin users from rate limits (for testing) **Rate Limit Configuration**: - Anonymous submission: 10 requests per hour per IP address - Dashboard/admin routes: No rate limiting (authenticated users only) - Rate limit headers included in response: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` **Considerations**: - IP-based limiting can be circumvented via VPN/proxy but sufficient deterrent for casual abuse - Behind proxy/load balancer: Configure Flask-Limiter to read `X-Forwarded-For` header - Note: FR-055 requires no fingerprinting for identification - rate limiting is for abuse prevention only, not user tracking **Alternatives Considered**: - **CAPTCHA (hCaptcha/reCAPTCHA)**: Adds friction to user experience, contradicts "lower barriers" goal - **Redis-backed rate limiting**: Unnecessary complexity for single-server MVP - **No rate limiting**: Violates FR-061 requirement, leaves system vulnerable to abuse - **Token bucket per session**: Requires session tracking for anonymous users, violates anonymity **Dependencies**: - `Flask-Limiter` extension --- ## 6. Flask Best Practices for Simple HTML Applications ### Decision: Server-side rendering with Jinja2 templates, no JavaScript **Rationale**: - User explicitly specifies "plain html using flask" and "does not use any css frameworks or javascript libraries" - Server-side rendering eliminates frontend build complexity - Jinja2 included with Flask, no additional dependencies - Forms use standard HTTP POST/GET, progressive enhancement approach **Template Approach**: - Minimal inline CSS for basic layout (no framework) - Semantic HTML5 for accessibility - Server-side form validation with error display - Standard browser form controls (no custom widgets) **Form Handling**: - POST requests for submissions - Server-side validation with error messages - Flash messages for user feedback - Redirect-after-POST pattern to prevent duplicate submissions **No JavaScript Requirement** (Edge Case from spec.md line 105): - "What happens when a user's browser doesn't support JavaScript" - Answer: Application works fully without JavaScript (no JS used) - File uploads work via standard HTML `` **Best Practices Applied**: - Flask app factory pattern for testability - Blueprint organization for routes - Environment-based configuration - CSRF protection via Flask-WTF (even for simple forms) **Dependencies**: - Flask (includes Jinja2) - Flask-WTF for CSRF protection --- ## 7. Python Dependency Management ### Decision: requirements.txt with pinned versions for reproducibility **Rationale**: - Simplest dependency management for Flask application - No need for Poetry/Pipenv complexity in MVP - Pin exact versions for reproducibility - Virtual environment assumed for isolation **Core Dependencies** (estimated): ``` Flask==3.0.0 Flask-Login==0.6.3 Flask-Limiter==3.5.0 Flask-WTF==1.2.1 anthropic==0.8.0 clamd==1.0.2 bcrypt==4.1.2 PyYAML==6.0.1 pytest==7.4.3 pytest-flask==1.3.0 ``` **Development Dependencies**: - pytest, pytest-flask for testing - black for code formatting - flake8 for linting --- ## Technology Stack Summary | Component | Technology | Rationale | |-----------|-----------|-----------| | **Web Framework** | Flask 3.0+ | Lightweight, simple, widely supported | | **Template Engine** | Jinja2 (built-in) | Server-side rendering, no JS needed | | **AI Provider** | Claude API (Anthropic) | User-specified, abstracted for future flexibility | | **Authentication** | Flask-Login + bcrypt | Standard session management, secure passwords | | **Rate Limiting** | Flask-Limiter | Prevent abuse, simple IP-based approach | | **Malware Scanning** | ClamAV + clamd | Open-source, reliable, on-premise | | **Storage** | Filesystem (YAML + Markdown) | Matches user requirements, simple, no DB | | **Testing** | pytest + pytest-flask | Industry standard, good Flask integration | | **Python Version** | 3.11+ | Modern, stable, good performance | | **Deployment** | Gunicorn (WSGI) | Production-ready Flask server | --- ## Non-Functional Requirements Research ### Performance Considerations **Concurrent Submissions** (SC-012): - Target: 100 concurrent submissions without errors - Flask + Gunicorn with 4-8 worker processes should handle this - File I/O is bottleneck: consider async I/O if performance issues arise - AI analysis happens asynchronously (background task) to not block submission response **Dashboard Performance** (SC-008): - Target: Load 1000 items in <3 seconds - File-based approach: Index product feedback directories, cache counts - Implement pagination (50 items per page) - Use lazy loading for file attachments (links, not embedded content) **AI Analysis Time** (SC-007): - Target: <30 seconds for 95% of submissions - Claude Haiku model typically responds in 2-5 seconds for translation/categorization - Timeout: 45 seconds before marking as failed - Queue-based processing if needed (Python `queue` module or simple file-based queue) ### Security Considerations **Anonymity Enforcement** (FR-055, SC-010): - Do NOT log IP addresses in feedback metadata - Rate limiting uses IP for abuse prevention only, not stored with feedback - No session cookies for anonymous submission - No analytics/tracking scripts **File Upload Security**: - Validate MIME types server-side (don't trust client) - ClamAV scanning before storage - Sanitize filenames to prevent directory traversal - Store outside web root, serve via Flask route with access control **HTTPS Requirement** (FR-064): - Deployment guide must specify reverse proxy (nginx) with TLS - Redirect HTTP to HTTPS - HSTS headers recommended --- ## Open Questions for Implementation Phase 1. **Asynchronous AI Analysis**: Should analysis happen synchronously (user waits) or asynchronously (background job)? - **Recommendation**: Asynchronous - return success immediately, process in background - Implement simple file-based queue or use Python `threading` for MVP 2. **Admin Bootstrap**: How is the first admin user created? - **Recommendation**: CLI command or config file initialization script 3. **Email Notifications**: Out of scope (line 255) but commonly requested - **Recommendation**: Document as future enhancement, design hooks for extensibility 4. **Logging Strategy**: Structured logs for operational monitoring? - **Recommendation**: Python `logging` module, JSON format, separate file per environment --- ## Research Validation All NEEDS CLARIFICATION items from Technical Context have been resolved: | Item | Resolution | Document Section | |------|------------|------------------| | AI Integration | Pluggable interface, Claude as default | §1 | | File Upload - Malware Scanning | ClamAV + clamd | §2 | | File Upload - Storage Location | Filesystem: `data/products/.../feedback/.../attachments/` | §3 | | Authentication | Flask-Login + server-side sessions + bcrypt | §4 | | Rate Limiting | Flask-Limiter, 10/hour/IP | §5 | **Next Phase**: Proceed to Phase 1 (data-model.md, contracts, quickstart.md)