Files
Reklamator/specs/001-build-an-application/research.md
T

367 lines
14 KiB
Markdown
Raw Normal View History

# 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 `<input type="file" multiple>`
**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)