aboutsummaryrefslogtreecommitdiff
path: root/ARCHITECTURE.md
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
committerCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
commit49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch)
tree61d86a7705dacc9fddccc29fa79d075d83ab8059 /ARCHITECTURE.md
i vibe coded itHEADmaster
Diffstat (limited to 'ARCHITECTURE.md')
-rw-r--r--ARCHITECTURE.md693
1 files changed, 693 insertions, 0 deletions
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
new file mode 100644
index 0000000..b681fa4
--- /dev/null
+++ b/ARCHITECTURE.md
@@ -0,0 +1,693 @@
+# JCHAT Architecture Document
+
+**Version:** 1.0
+**Date:** August 15, 2025
+**Status:** Living Document
+
+## Executive Summary
+
+JCHAT is a real-time chat system built on the JMAP (JSON Meta Application Protocol) standard. It provides a scalable, extensible foundation for messaging with a clean separation between protocol, server implementation, and client interfaces.
+
+## Current Architecture Overview
+
+### System Components
+
+```
+┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
+│ Web Client │────│ JMAP Server │────│ Database │
+│ (HTML/JS/CSS) │ │ (Erlang/OTP) │ │ (Mnesia) │
+└─────────────────┘ └──────────────────┘ └─────────────────┘
+ │ │ │
+ │ ┌────────────────┐ │
+ └──────────────│ HTTP/REST │───────────────┘
+ │ JSON/JMAP │
+ └────────────────┘
+```
+
+### Protocol Layer: JMAP Extension
+
+**Base Protocol:** RFC 8620 - JSON Meta Application Protocol
+**Extension:** JCHAT Capability (`urn:ietf:params:jmap:chat`)
+
+**Core Objects:**
+- `Conversation`: Chat rooms/threads with metadata and participants
+- `Message`: Individual messages with content, reactions, and delivery status
+- `Participant`: User membership and roles within conversations
+- `Presence`: User availability and status information
+
+**Methods Implemented:**
+- `Conversation/get`, `Conversation/set`, `Conversation/query`, `Conversation/changes`
+- `Message/get`, `Message/set`, `Message/query`, `Message/changes`
+- `Participant/get`, `Participant/set`, `Participant/changes`
+- `Presence/get`, `Presence/set`
+
+### Server Architecture (Erlang/OTP)
+
+```
+Application Layer (jchat_app)
+ │
+Supervisor Layer (jchat_sup)
+ │
+┌─────────────────────────────────────────────────────────────┐
+│ Worker Processes │
+├─────────────────┬─────────────────┬─────────────────────────┤
+│ HTTP Server │ Push Manager │ Presence Manager │
+│ (jchat_http) │ (jchat_push) │ (jchat_presence) │
+└─────────────────┴─────────────────┴─────────────────────────┘
+ │ │ │
+┌─────────────────────────────────────────────────────────────┐
+│ Business Logic Layer │
+├─────────────────┬─────────────────┬─────────────────────────┤
+│ JMAP Methods │ Utilities │ Database Layer │
+│(jchat_methods) │(jchat_utils) │ (jchat_db) │
+└─────────────────┴─────────────────┴─────────────────────────┘
+```
+
+**Key Modules:**
+- `jchat_http`: Cowboy-based HTTP server, CORS, request routing
+- `jchat_methods`: JMAP method implementations, business logic
+- `jchat_db`: Mnesia database operations, CRUD, queries
+- `jchat_utils`: Shared utilities (UUID generation, timestamps, formatting)
+- `jchat_push`: Server-sent events for real-time updates
+- `jchat_presence`: User status and availability management
+
+### Client Architecture
+
+**Technology Stack:** Vanilla HTML5, CSS3, JavaScript (ES6+)
+**Deployment:** Static files served by lightweight HTTP server (shttpd, Python, nginx)
+
+```
+┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
+│ UI Layer │────│ Application │────│ JMAP Client │
+│ (index.html) │ │ Logic │ │ Library │
+│ │ │ (app.js) │ │ (jmap-client.js)│
+└─────────────────┘ └──────────────────┘ └─────────────────┘
+ │ │
+ │ ┌────────────────┐
+ └──────────────│ Server API │
+ │ (HTTP/JSON) │
+ └────────────────┘
+```
+
+**Features:**
+- Real-time message synchronization (5-second polling)
+- Responsive design for mobile/desktop
+- Local state management and caching
+- User settings persistence (localStorage)
+- Modal-based UI for settings and new conversations
+
+### Database Schema (Mnesia)
+
+**Tables:**
+- `user`: User accounts and profile information
+- `conversation`: Chat rooms with metadata, participants, settings
+- `message`: Individual messages with content, reactions, delivery status
+- `participant`: User membership and roles in conversations
+- `presence`: User availability and status
+- `state_counter`: JMAP state tracking for real-time synchronization
+
+**Storage:** In-memory with optional disk persistence (ram_copies)
+**Consistency:** ACID transactions, distributed capabilities
+
+## Current Implementation Status
+
+### ✅ Completed Features
+- [x] JMAP protocol foundation and session management
+- [x] Basic conversation management (create, list, select)
+- [x] Message sending and real-time synchronization
+- [x] User display name settings with persistence
+- [x] Cross-origin resource sharing (CORS) support
+- [x] Responsive web UI with modern design
+- [x] Static file deployment (no build process)
+
+### 🔄 Partially Implemented
+- [ ] Message history loading (implemented but needs optimization)
+- [ ] Real-time updates (polling works, changes detection needs improvement)
+- [ ] Error handling and user feedback (basic implementation)
+
+### ❌ Not Yet Implemented
+- [ ] User authentication and sessions
+- [ ] Role-based access control (RBAC)
+- [ ] Private vs public conversations
+- [ ] Message attachments and file uploads
+- [ ] Message reactions and threading
+- [ ] Push notifications
+- [ ] Persistent database storage
+
+---
+
+## Future Architecture Considerations
+
+### 1. User Authentication & Authorization
+
+#### Current State
+- **Authentication:** None - users set display names locally
+- **Session Management:** No server-side sessions
+- **User Identity:** Display names stored in client localStorage
+
+#### Proposed Architecture
+
+```
+┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
+│ Client Auth │────│ Auth Service │────│ User Database │
+│ (Login Form) │ │ (OAuth2/JWT/etc) │ │ (Accounts) │
+└─────────────────┘ └──────────────────┘ └─────────────────┘
+ │ │ │
+ │ ┌────────────────┐ │
+ └──────────────│ Session Mgmt │───────────────┘
+ │ (Redis/Mnesia) │
+ └────────────────┘
+```
+
+**Options to Consider:**
+1. **JWT-based Authentication**
+ - Stateless tokens
+ - Self-contained user info
+ - Easy to scale horizontally
+
+2. **OAuth2/OpenID Connect**
+ - External identity providers (Google, GitHub, etc.)
+ - Standardized protocols
+ - Reduced authentication complexity
+
+3. **Traditional Session-Based**
+ - Server-side session storage
+ - Cookie-based authentication
+ - More control over session lifecycle
+
+**Recommended Approach:**
+```erlang
+%% User record with authentication
+-record(user, {
+ id, % binary() - Unique user ID
+ username, % binary() - Unique username
+ email, % binary() - Email address
+ password_hash, % binary() - Hashed password (if local auth)
+ display_name, % binary() - Display name
+ avatar_url, % binary() | null - Avatar image URL
+ created_at, % binary() - ISO8601 timestamp
+ last_login_at, % binary() | null - Last login time
+ is_active, % boolean() - Account status
+ auth_provider, % binary() - 'local' | 'google' | 'github' etc
+ auth_provider_id % binary() | null - External provider user ID
+}).
+
+%% Session record
+-record(session, {
+ id, % binary() - Session token/ID
+ user_id, % binary() - User ID
+ created_at, % binary() - ISO8601 timestamp
+ expires_at, % binary() - ISO8601 timestamp
+ ip_address, % binary() - Client IP
+ user_agent % binary() - Client user agent
+}).
+```
+
+### 2. Role-Based Access Control (RBAC)
+
+#### Proposed Permission Model
+
+```
+System Level Permissions:
+├── admin.system (Full system administration)
+├── admin.users (User management)
+├── admin.conversations (Global conversation management)
+├── user.create (Create new conversations)
+└── user.invite (Invite others to conversations)
+
+Conversation Level Permissions:
+├── conversation.admin (Full conversation control)
+├── conversation.moderate (Moderation capabilities)
+├── conversation.write (Send messages)
+├── conversation.read (View messages)
+└── conversation.invite (Invite new participants)
+
+Message Level Permissions:
+├── message.delete.own (Delete own messages)
+├── message.delete.any (Delete any message - moderators)
+├── message.edit.own (Edit own messages)
+└── message.react (Add reactions to messages)
+```
+
+#### Database Schema for RBAC
+
+```erlang
+%% Role definition
+-record(role, {
+ id, % binary() - Role ID
+ name, % binary() - Human readable name
+ description, % binary() - Role description
+ permissions, % [binary()] - List of permission strings
+ is_system, % boolean() - System role vs user-defined
+ created_at % binary() - ISO8601 timestamp
+}).
+
+%% User role assignments (system-wide)
+-record(user_role, {
+ user_id, % binary() - User ID
+ role_id, % binary() - Role ID
+ granted_by, % binary() - User ID who granted the role
+ granted_at % binary() - ISO8601 timestamp
+}).
+
+%% Conversation-specific role assignments
+-record(conversation_role, {
+ user_id, % binary() - User ID
+ conversation_id, % binary() - Conversation ID
+ role_id, % binary() - Role ID
+ granted_by, % binary() - User ID who granted the role
+ granted_at % binary() - ISO8601 timestamp
+}).
+```
+
+#### Permission Checking Architecture
+
+```erlang
+%% Permission checking API
+jchat_auth:check_permission(UserId, Permission) -> boolean().
+jchat_auth:check_permission(UserId, Permission, ConversationId) -> boolean().
+
+%% Example usage in JMAP methods
+handle_message_set(Args, AccountId) ->
+ ConversationId = get_conversation_from_args(Args),
+ case jchat_auth:check_permission(AccountId, <<"message.write">>, ConversationId) of
+ true ->
+ %% Proceed with message creation
+ proceed_with_message_creation(Args, AccountId);
+ false ->
+ {error, #{type => <<"forbidden">>, description => <<"Insufficient permissions">>}}
+ end.
+```
+
+### 3. Conversation Types & Privacy
+
+#### Current State
+- All conversations are essentially "public" (no access control)
+- No distinction between different conversation types
+
+#### Proposed Conversation Types
+
+```erlang
+%% Enhanced conversation record
+-record(conversation, {
+ id, % binary() - Conversation ID
+ title, % binary() | null - Conversation title
+ description, % binary() | null - Description
+ conversation_type, % binary() - Type of conversation
+ privacy_level, % binary() - Privacy setting
+ created_at, % binary() - ISO8601 timestamp
+ updated_at, % binary() - ISO8601 timestamp
+ created_by, % binary() - Creator user ID
+ is_archived, % boolean() - Archived status
+ is_muted, % boolean() - Muted status
+ participant_ids, % [binary()] - Participant IDs
+ last_message_id, % binary() | null - Last message ID
+ last_message_at, % binary() | null - Last message timestamp
+ unread_count, % integer() - Unread message count
+ message_count, % integer() - Total message count
+ settings, % map() - Conversation settings
+ metadata % map() | null - Additional metadata
+}).
+```
+
+**Conversation Types:**
+- `<<"direct">>` - Direct message between 2 users
+- `<<"group">>` - Private group chat (invite-only)
+- `<<"channel">>` - Public channel (discoverable, open join)
+- `<<"announcement">>` - Broadcast channel (read-only for most users)
+
+**Privacy Levels:**
+- `<<"private">>` - Invite-only, not discoverable
+- `<<"public">>` - Anyone can join, publicly discoverable
+- `<<"restricted">>` - Discoverable but requires approval to join
+
+#### Channel/Room Discovery
+
+```erlang
+%% Public channel discovery
+-record(channel_directory, {
+ conversation_id, % binary() - Conversation ID
+ title, % binary() - Channel title
+ description, % binary() - Channel description
+ category, % binary() - Category/topic
+ member_count, % integer() - Number of members
+ activity_score, % float() - Activity ranking
+ is_featured, % boolean() - Featured channel
+ created_at % binary() - ISO8601 timestamp
+}).
+```
+
+### 4. Database Architecture Evolution
+
+#### Current State: Mnesia
+**Pros:**
+- Built into Erlang/OTP
+- ACID transactions
+- Distributed capabilities
+- No external dependencies
+- RAM-based performance
+
+**Cons:**
+- Memory limitations for large datasets
+- Limited query capabilities
+- No full-text search
+- Backup/restore complexity
+
+#### Future Database Considerations
+
+##### Option 1: Enhanced Mnesia Setup
+```erlang
+%% Hybrid storage approach
+Tables = [
+ {user, disc_copies}, % Users - persistent
+ {session, ram_copies}, % Sessions - temporary
+ {conversation, disc_copies}, % Conversations - persistent
+ {message, disc_only_copies}, % Messages - disk-based for large volume
+ {presence, ram_copies}, % Presence - temporary
+ {state_counter, disc_copies} % State - persistent
+].
+```
+
+##### Option 2: PostgreSQL Backend
+```
+┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
+│ Erlang App │────│ Database Pool │────│ PostgreSQL │
+│ │ │ (Poolboy) │ │ │
+└─────────────────┘ └──────────────────┘ └─────────────────┘
+ │
+ ┌────────────────┐
+ │ SQL Queries │
+ │ (epgsql/pgapp)│
+ └────────────────┘
+```
+
+**Benefits:**
+- Mature, battle-tested
+- Full-text search capabilities
+- JSON support for flexible schema
+- Excellent tooling and monitoring
+- Horizontal scaling options (read replicas)
+
+**Schema Example:**
+```sql
+-- Users table
+CREATE TABLE users (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ username VARCHAR(50) UNIQUE NOT NULL,
+ email VARCHAR(255) UNIQUE NOT NULL,
+ password_hash VARCHAR(255),
+ display_name VARCHAR(100),
+ avatar_url TEXT,
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+ last_login_at TIMESTAMP WITH TIME ZONE,
+ is_active BOOLEAN DEFAULT true,
+ auth_provider VARCHAR(50) DEFAULT 'local',
+ auth_provider_id VARCHAR(255),
+ metadata JSONB DEFAULT '{}'
+);
+
+-- Conversations table
+CREATE TABLE conversations (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ title VARCHAR(255),
+ description TEXT,
+ conversation_type VARCHAR(20) NOT NULL DEFAULT 'group',
+ privacy_level VARCHAR(20) NOT NULL DEFAULT 'private',
+ created_by UUID REFERENCES users(id),
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+ is_archived BOOLEAN DEFAULT false,
+ settings JSONB DEFAULT '{}',
+ metadata JSONB DEFAULT '{}'
+);
+
+-- Messages table with partitioning for scale
+CREATE TABLE messages (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ conversation_id UUID REFERENCES conversations(id),
+ sender_id UUID REFERENCES users(id),
+ body TEXT NOT NULL,
+ body_type VARCHAR(50) DEFAULT 'text/plain',
+ sent_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+ edited_at TIMESTAMP WITH TIME ZONE,
+ is_deleted BOOLEAN DEFAULT false,
+ reply_to_message_id UUID REFERENCES messages(id),
+ attachments JSONB DEFAULT '[]',
+ reactions JSONB DEFAULT '[]',
+ metadata JSONB DEFAULT '{}'
+) PARTITION BY RANGE (sent_at);
+
+-- Full-text search
+CREATE INDEX messages_body_fts ON messages USING GIN(to_tsvector('english', body));
+```
+
+##### Option 3: Hybrid Approach
+- **Mnesia**: Real-time data (sessions, presence, caching)
+- **PostgreSQL**: Persistent data (users, messages, conversations)
+- **Redis**: Caching and pub/sub for real-time features
+
+#### Message Storage Scaling Strategy
+
+For high-volume messaging, consider:
+
+1. **Message Archival**
+ ```erlang
+ %% Archive old messages to separate storage
+ -record(message_archive, {
+ id,
+ original_message,
+ archived_at,
+ archive_reason
+ }).
+ ```
+
+2. **Message Partitioning**
+ - Partition by time (monthly tables)
+ - Partition by conversation ID
+ - Hot/warm/cold data tiers
+
+3. **Search Integration**
+ - Elasticsearch for full-text search
+ - Index message content asynchronously
+ - Search API separate from chat API
+
+---
+
+## Implementation Roadmap
+
+### Phase 1: Authentication & Authorization (Weeks 1-2)
+- [ ] Implement JWT-based authentication
+- [ ] Add user registration/login endpoints
+- [ ] Basic role system (admin, user, moderator)
+- [ ] Secure existing JMAP endpoints
+
+### Phase 2: Enhanced Conversations (Weeks 3-4)
+- [ ] Conversation types (direct, group, channel)
+- [ ] Privacy levels and access control
+- [ ] Channel discovery and joining
+- [ ] Participant management UI
+
+### Phase 3: Database Evolution (Weeks 5-6)
+- [ ] Evaluate PostgreSQL migration
+- [ ] Implement database abstraction layer
+- [ ] Message archival and pagination
+- [ ] Full-text search capabilities
+
+### Phase 4: Advanced Features (Weeks 7-8)
+- [ ] File attachments and media
+- [ ] Message reactions and threading
+- [ ] Push notifications
+- [ ] Presence and typing indicators
+
+### Phase 5: Scaling & Production (Weeks 9-10)
+- [ ] Horizontal scaling architecture
+- [ ] Monitoring and observability
+- [ ] Performance optimization
+- [ ] Security hardening and audit
+
+---
+
+## Security Considerations
+
+### Current Security Status
+- **Authentication:** ❌ None implemented
+- **Authorization:** ❌ No access control
+- **Input Validation:** ⚠️ Basic validation
+- **CORS:** ✅ Properly configured
+- **HTTPS:** ❌ Not enforced (development)
+
+### Security Roadmap
+
+#### Authentication Security
+- [ ] Secure password hashing (bcrypt/scrypt)
+- [ ] JWT token security (short expiry, refresh tokens)
+- [ ] Rate limiting on authentication endpoints
+- [ ] Account lockout on failed attempts
+- [ ] Two-factor authentication (TOTP)
+
+#### API Security
+- [ ] Input validation and sanitization
+- [ ] SQL injection prevention (parameterized queries)
+- [ ] XSS prevention (content escaping)
+- [ ] CSRF protection for state-changing operations
+- [ ] Rate limiting per user/IP
+
+#### Transport Security
+- [ ] HTTPS enforcement (TLS 1.3)
+- [ ] HTTP Strict Transport Security (HSTS)
+- [ ] Certificate pinning for mobile apps
+- [ ] Secure cookie settings
+
+#### Data Protection
+- [ ] Encryption at rest (database encryption)
+- [ ] PII data handling compliance
+- [ ] Message retention policies
+- [ ] User data export/deletion (GDPR)
+
+---
+
+## Monitoring & Observability
+
+### Current Monitoring
+- **Logging:** ✅ Basic Erlang logging
+- **Metrics:** ❌ None implemented
+- **Health Checks:** ❌ None implemented
+- **Error Tracking:** ⚠️ Console errors only
+
+### Proposed Monitoring Stack
+
+```
+┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
+│ Application │────│ Telemetry │────│ Prometheus │
+│ │ │ (Metrics) │ │ │
+└─────────────────┘ └──────────────────┘ └─────────────────┘
+ │ │ │
+ │ ┌────────────────┐ │
+ └──────────────│ Logging │───────────────┘
+ │ (ELK Stack) │
+ └────────────────┘
+```
+
+#### Key Metrics to Track
+- **Performance**: Response times, throughput, error rates
+- **Business**: Active users, messages sent, conversations created
+- **Infrastructure**: CPU, memory, disk usage, connection pools
+- **Security**: Failed login attempts, rate limit hits, suspicious activity
+
+#### Alerting Strategy
+- **Critical**: System down, database connection lost
+- **Warning**: High error rate, memory usage above threshold
+- **Info**: New user registrations, system updates
+
+---
+
+## Deployment & DevOps
+
+### Current Deployment
+- **Development**: Local rebar3 shell, static file server
+- **Production**: ❌ Not implemented
+
+### Proposed Deployment Architecture
+
+#### Containerization
+```dockerfile
+# Erlang application container
+FROM erlang:26-alpine
+COPY _build/prod/rel/jchat /opt/jchat
+EXPOSE 8080
+CMD ["/opt/jchat/bin/jchat", "foreground"]
+```
+
+#### Container Orchestration
+```yaml
+# docker-compose.yml for local development
+version: '3.8'
+services:
+ jchat-server:
+ build: ./server
+ ports:
+ - "8080:8080"
+ environment:
+ - DATABASE_URL=postgresql://user:pass@db:5432/jchat
+ depends_on:
+ - db
+ - redis
+
+ jchat-client:
+ image: nginx:alpine
+ volumes:
+ - ./client:/usr/share/nginx/html
+ ports:
+ - "3000:80"
+
+ db:
+ image: postgres:15
+ environment:
+ POSTGRES_DB: jchat
+ POSTGRES_USER: jchat
+ POSTGRES_PASSWORD: password
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+
+ redis:
+ image: redis:7-alpine
+ ports:
+ - "6379:6379"
+
+volumes:
+ postgres_data:
+```
+
+#### Production Considerations
+- **Load Balancing**: HAProxy/nginx for horizontal scaling
+- **Database**: Managed PostgreSQL (AWS RDS, Google Cloud SQL)
+- **Caching**: Redis cluster for session management
+- **CDN**: CloudFlare/AWS CloudFront for static assets
+- **Monitoring**: Datadog/New Relic for APM
+
+---
+
+## Testing Strategy
+
+### Current Testing
+- **Unit Tests**: ⚠️ Basic test suite structure
+- **Integration Tests**: ❌ Not implemented
+- **Performance Tests**: ❌ Not implemented
+- **Security Tests**: ❌ Not implemented
+
+### Comprehensive Testing Plan
+
+#### Unit Testing (Erlang)
+```erlang
+%% Example test structure
+-module(jchat_methods_tests).
+-include_lib("eunit/include/eunit.hrl").
+
+conversation_create_test() ->
+ %% Test conversation creation logic
+ Args = #{<<"title">> => <<"Test Conv">>, <<"description">> => <<"Test">>},
+ {ok, Result} = jchat_methods:handle_conversation_set(Args, <<"user1">>),
+ ?assertMatch(#{<<"created">> := CreatedMap} when map_size(CreatedMap) > 0, Result).
+```
+
+#### Integration Testing
+- JMAP protocol compliance tests
+- Database operations end-to-end
+- HTTP API endpoint testing
+- WebSocket/SSE real-time features
+
+#### Performance Testing
+- Message throughput benchmarks
+- Concurrent user simulation
+- Database query optimization
+- Memory usage profiling
+
+#### Security Testing
+- Authentication bypass attempts
+- Input validation testing
+- Rate limiting verification
+- Session management security
+
+---
+
+This architecture document provides a comprehensive roadmap for evolving JCHAT from a proof-of-concept to a production-ready system. Each section can be expanded based on specific implementation decisions and requirements.