diff options
author | Calvin Morrison <calvin@pobox.com> | 2025-09-03 21:15:36 -0400 |
---|---|---|
committer | Calvin Morrison <calvin@pobox.com> | 2025-09-03 21:15:36 -0400 |
commit | 49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch) | |
tree | 61d86a7705dacc9fddccc29fa79d075d83ab8059 /ARCHITECTURE.md |
Diffstat (limited to 'ARCHITECTURE.md')
-rw-r--r-- | ARCHITECTURE.md | 693 |
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. |