Complete Phase 0 (Research) and Phase 1 (Design) of implementation planning workflow: - plan.md: Technical context, constitution check, and project structure - research.md: Technology decisions for AI integration, file storage, authentication, security - data-model.md: Domain entities with validation rules and state transitions - contracts/: API specifications for submission, dashboard, and admin endpoints - quickstart.md: Developer setup guide with test-first workflow - CLAUDE.md: Updated agent context with tech stack All constitutional requirements validated. Ready for task generation phase. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
367 lines
14 KiB
Markdown
367 lines
14 KiB
Markdown
# 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)
|