15 KiB
Phase 3 Complete: Web Platform
Overview
Phase 3 successfully implemented the Web platform for Loyal Companion, providing a private, high-intimacy chat interface accessible via browser.
What Was Accomplished
1. Complete FastAPI Backend
Created directory structure:
src/loyal_companion/web/
├── __init__.py # Module exports
├── app.py # FastAPI application factory
├── dependencies.py # Dependency injection (DB, auth, gateway)
├── middleware.py # Logging and rate limiting
├── models.py # Pydantic request/response models
├── routes/
│ ├── __init__.py
│ ├── chat.py # POST /api/chat, GET /api/health
│ ├── session.py # Session and history management
│ └── auth.py # Token generation (simple auth)
└── static/
└── index.html # Web UI
Lines of code:
app.py: 110 linesdependencies.py: 118 linesmiddleware.py: 105 linesmodels.py: 78 linesroutes/chat.py: 111 linesroutes/session.py: 189 linesroutes/auth.py: 117 linesstatic/index.html: 490 lines- Total: ~1,318 lines
2. API Endpoints
Chat Endpoint
POST /api/chat
- Accepts session_id and message
- Returns AI response with metadata (mood, relationship, facts)
- Uses Conversation Gateway with HIGH intimacy
- Enables web search
- Private context (is_public = false)
Request:
{
"session_id": "session_abc123",
"message": "I'm feeling overwhelmed today"
}
Response:
{
"response": "That sounds heavy. Want to sit with it for a bit?",
"mood": {
"label": "calm",
"valence": 0.2,
"arousal": -0.3,
"intensity": 0.4
},
"relationship": {
"level": "close_friend",
"score": 85,
"interactions_count": 42
},
"extracted_facts": ["User mentioned feeling overwhelmed"]
}
Session Management
GET /api/sessions - List all user sessions
GET /api/sessions/{session_id}/history - Get conversation history
DELETE /api/sessions/{session_id} - Delete a session
Authentication
POST /api/auth/token - Generate auth token (simple for Phase 3)
POST /api/auth/magic-link - Placeholder for future magic link auth
GET /api/auth/verify - Placeholder for token verification
Health & Info
GET /api/health - Health check
GET / - Serves web UI or API info
3. Authentication System
Phase 3 approach: Simple token-based auth for testing
Token format: web:<email>
Example: web:alice@example.com
How it works:
- User enters email in web UI
- POST to
/api/auth/tokenwith email - Server generates token:
web:{email} - Token stored in localStorage
- Included in all API calls as
Authorization: Bearer web:{email}
Future (Phase 5):
- Generate secure JWT tokens
- Magic link via email
- Token expiration
- Refresh tokens
- Redis for session storage
4. Middleware
LoggingMiddleware
- Logs all incoming requests
- Logs all responses with status code and duration
- Helps debugging and monitoring
RateLimitMiddleware
- Simple in-memory rate limiting
- Default: 60 requests per minute per IP
- Returns 429 if exceeded
- Cleans up old entries automatically
Future improvements:
- Use Redis for distributed rate limiting
- Per-user rate limits (not just IP)
- Configurable limits per endpoint
5. Web UI
Features:
- Clean, dark-themed interface
- Real-time chat
- Message history persisted
- Typing indicator
- Email-based "auth" (simple for testing)
- Session persistence via localStorage
- Responsive design
- Keyboard shortcuts (Enter to send, Shift+Enter for new line)
Technology:
- Pure HTML/CSS/JavaScript (no framework)
- Fetch API for HTTP requests
- localStorage for client-side persistence
- Minimal dependencies
UX Design Principles:
- Dark theme (low distraction)
- No engagement metrics (no "seen" indicators, no typing status from other users)
- No notifications or popups
- Intentional, quiet space
- High intimacy reflected in design
6. Configuration Updates
Added to config.py:
# Web Platform Configuration
web_enabled: bool = False # Toggle web platform
web_host: str = "127.0.0.1" # Server host
web_port: int = 8080 # Server port
web_cors_origins: list[str] = ["http://localhost:3000", "http://localhost:8080"]
web_rate_limit: int = 60 # Requests per minute per IP
# CLI Configuration (placeholder)
cli_enabled: bool = False
cli_allow_emoji: bool = False
Environment variables:
WEB_ENABLED=true
WEB_HOST=127.0.0.1
WEB_PORT=8080
WEB_CORS_ORIGINS=["http://localhost:3000"]
WEB_RATE_LIMIT=60
7. Gateway Integration
The Web platform uses the Conversation Gateway with:
- Platform:
Platform.WEB - Intimacy Level:
IntimacyLevel.HIGH - is_public:
False(always private) - requires_web_search:
True
Behavior differences vs Discord:
- Deeper reflection allowed
- Silence tolerance
- Proactive follow-ups enabled
- Fact extraction enabled
- Emotional naming encouraged
- No message length limits (handled by UI)
Safety boundaries still enforced:
- No exclusivity claims
- No dependency reinforcement
- No discouraging external connections
- Crisis deferral to professionals
Running the Web Platform
Development
# Install dependencies
pip install fastapi uvicorn
# Set environment variables
export DATABASE_URL="postgresql://..."
export WEB_ENABLED=true
# Run web server
python3 run_web.py
Server starts at: http://127.0.0.1:8080
Production
# Using uvicorn directly
uvicorn loyal_companion.web:app \
--host 0.0.0.0 \
--port 8080 \
--workers 4
# Or with gunicorn
gunicorn loyal_companion.web:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8080
Docker
# docker-compose.yml addition
web:
build: .
command: uvicorn loyal_companion.web:app --host 0.0.0.0 --port 8080
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgresql://...
- WEB_ENABLED=true
depends_on:
- db
Testing
Manual Testing Checklist
- Visit
http://localhost:8080 - Enter email and get token
- Send a message
- Receive AI response
- Check that mood/relationship metadata appears
- Send multiple messages (conversation continuity)
- Refresh page (history should load)
- Test Enter to send, Shift+Enter for new line
- Test rate limiting (send >60 requests in 1 minute)
- Test /api/health endpoint
- Test /docs (Swagger UI)
- Test CORS (from different origin)
API Testing with curl
# Get auth token
curl -X POST http://localhost:8080/api/auth/token \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com"}'
# Send chat message
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-H "Authorization: Bearer web:test@example.com" \
-d '{"session_id": "test_session", "message": "Hello!"}'
# Get session history
curl http://localhost:8080/api/sessions/test_session/history \
-H "Authorization: Bearer web:test@example.com"
# Health check
curl http://localhost:8080/api/health
Architecture
┌──────────────────────────────────────────────────────────┐
│ Browser (User) │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ FastAPI Web Application │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Middleware Layer │ │
│ │ - LoggingMiddleware │ │
│ │ - RateLimitMiddleware │ │
│ │ - CORSMiddleware │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Routes Layer │ │
│ │ - /api/chat (chat.py) │ │
│ │ - /api/sessions (session.py) │ │
│ │ - /api/auth (auth.py) │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Dependencies Layer │ │
│ │ - verify_auth_token() │ │
│ │ - get_db_session() │ │
│ │ - get_conversation_gateway() │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ ConversationGateway │
│ (Platform: WEB, Intimacy: HIGH) │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Living AI Core │
│ (Mood, Relationship, Facts, Opinions, Proactive) │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ PostgreSQL Database │
└──────────────────────────────────────────────────────────┘
Known Limitations
Current (Phase 3)
-
Simple authentication:
- No password, no encryption
- Token =
web:{email} - Anyone with email can access
- For testing only!
-
In-memory rate limiting:
- Not distributed (single server only)
- Resets on server restart
- IP-based (not user-based)
-
No real-time updates:
- No WebSocket support yet
- No push notifications
- Poll for new messages manually
-
Basic UI:
- No markdown rendering
- No image upload
- No file attachments
- No code highlighting
-
No account management:
- Can't delete account
- Can't export data
- Can't link to Discord
To Be Addressed
Phase 4 (CLI):
- Focus on CLI platform
Phase 5 (Enhancements):
- Add proper JWT authentication
- Add magic link email sending
- Add Redis for rate limiting
- Add WebSocket for real-time
- Add markdown rendering
- Add image upload
- Add account linking (Discord ↔ Web)
Security Considerations
Current Security Measures
✅ CORS configured
✅ Rate limiting (basic)
✅ Input validation (Pydantic)
✅ SQL injection prevention (SQLAlchemy ORM)
✅ XSS prevention (FastAPI auto-escapes)
Future Security Improvements
⏳ Proper JWT with expiration
⏳ HTTPS/TLS enforcement
⏳ CSRF tokens
⏳ Session expiration
⏳ Password hashing (if adding passwords)
⏳ Email verification
⏳ Rate limiting per user
⏳ IP allowlisting/blocklisting
Performance
Current Performance
- Response time: ~1-3 seconds (depends on AI provider)
- Concurrent users: Limited by single-threaded rate limiter
- Database queries: 3-5 per chat request
- Memory: ~100MB per worker process
Scalability
Horizontal scaling:
- Multiple workers: ✅ (with Redis for rate limiting)
- Load balancer: ✅ (stateless design)
- Multiple servers: ✅ (shared database)
Vertical scaling:
- More workers per server
- Larger database instance
- Redis for caching
Comparison with Discord
| Feature | Discord | Web |
|---|---|---|
| Platform | Discord app | Browser |
| Intimacy | LOW (guilds) / MEDIUM (DMs) | HIGH (always) |
| Auth | Discord OAuth | Simple token |
| UI | Discord's | Custom minimal |
| Real-time | Yes (Discord gateway) | No (polling) |
| Images | Yes | No (Phase 3) |
| Mentioned users | Yes | N/A |
| Message length | 2000 char limit | Unlimited |
| Fact extraction | No (LOW), Yes (MEDIUM) | Yes |
| Proactive events | No (LOW), Some (MEDIUM) | Yes |
| Privacy | Public guilds, private DMs | Always private |
Next Steps
Phase 4: CLI Client
- Create Typer CLI application
- HTTP client for web backend
- Local session persistence
- Terminal formatting
- Estimated: 1-2 days
Phase 5: Enhancements
- Add
PlatformIdentitymodel - Account linking UI
- Proper JWT authentication
- Magic link email
- WebSocket support
- Image upload
- Markdown rendering
- Estimated: 1 week
Conclusion
Phase 3 successfully delivered a complete Web platform:
✅ FastAPI backend with 7 endpoints
✅ Conversation Gateway integration (HIGH intimacy)
✅ Simple authentication system
✅ Session and history management
✅ Rate limiting and CORS
✅ Clean dark-themed UI
✅ 1,318 lines of new code
The Web platform is now the quiet back room—intentional, private, reflective.
Same bartender. Different stools. No one is trapped. 🍺
Completed: 2026-01-31
Status: Phase 3 Complete ✅
Next: Phase 4 - CLI Client