Architecture Decision Records (ADRs)
Overview
This document captures the key architectural decisions made in the development of Sasha Studio (ClaudeCodeUI), including the context, decision rationale, and implications of each choice.
ADR-001: Single-Page Application with React
Status
Accepted
Context
We needed a modern, responsive web interface that could handle real-time updates, complex state management, and provide a smooth user experience for AI interactions.
Decision
We chose React as our frontend framework with Vite as the build tool.
Rationale
- React: Mature ecosystem, excellent component model, strong community support
- Vite: Fast development builds, optimized production bundles, native ES modules
- SPA Architecture: Seamless navigation, better performance, offline capabilities
Consequences
- Fast, responsive user interface
- Rich ecosystem of components and libraries
- Excellent developer experience
- Larger initial bundle size
- SEO challenges (not critical for this application)
ADR-002: WebSocket for Real-time Communication
Status
Accepted
Context
Claude AI responses stream in real-time, and we need bidirectional communication for interactive features like session management and live updates.
Decision
Implement WebSocket using the ws library for server and native WebSocket API for client.
Rationale
- Real-time streaming of AI responses
- Bidirectional communication
- Lower latency than polling
- Efficient for long-running connections
Implementation
// Server
const wss = new WebSocketServer({ server });
wss.on('connection', handleConnection);
// Client
const ws = new WebSocket(`wss://${host}/ws?token=${token}`);
Consequences
- Real-time message streaming
- Efficient long-lived connections
- Bidirectional communication
- Connection state management complexity
- Requires fallback for network issues
ADR-003: SQLite for Single-User Database
Status
Accepted
Context
As a single-user system, we don't need the complexity of a multi-user database. We need simple, reliable persistence that works across different deployment environments.
Decision
Use SQLite with better-sqlite3 driver for all data persistence.
Rationale
- Zero configuration required
- Single file database
- ACID compliant
- No network overhead
- Excellent performance for single-user
- Easy backup and migration
Schema Design
-- Simplified, denormalized for single-user
users (single row)
company_profiles (organizational context)
onboarding_documents (knowledge base)
research_documents (AI-generated content)
Consequences
- Simple deployment
- No database server required
- Excellent performance
- Easy backup (single file)
- Not suitable for multi-user
- Limited concurrent writes
ADR-004: Claude CLI Integration via Child Process
Status
Accepted
Context
We need to integrate with Anthropic's Claude AI, and the Claude CLI provides a robust, maintained interface with built-in features like tool handling and session management.
Decision
Spawn Claude CLI as a child process rather than direct API integration.
Rationale
- Maintained by Anthropic
- Built-in tool handling
- Session management
- Resume capability
- Handles streaming naturally
- Reduces implementation complexity
Implementation Pattern
const claude = spawn('claude', args, {
cwd: workingDirectory,
env: processEnv
});
claude.stdout.on('data', handleStreamedResponse);
Consequences
- Robust, maintained integration
- Full Claude feature support
- Simplified implementation
- Dependency on CLI availability
- Process management overhead
- Potential version compatibility issues
ADR-005: JWT-Based Authentication
Status
Accepted
Context
We need a stateless authentication mechanism that works well with our SPA architecture and can be easily validated across different services.
Decision
Implement JWT (JSON Web Tokens) for authentication with refresh token pattern.
Rationale
- Stateless authentication
- Works well with SPAs
- Can include user claims
- Standard implementation
- Easy to validate
Implementation
// Token generation
const token = jwt.sign(
{ userId, username },
JWT_SECRET,
{ expiresIn: '24h' }
);
// Validation middleware
jwt.verify(token, JWT_SECRET);
Consequences
- Stateless authentication
- Scalable approach
- Standard implementation
- Token size overhead
- Cannot revoke tokens (until expiry)
ADR-006: Docker Multi-Stage Build
Status
Accepted
Context
We need consistent deployment across different environments while minimizing image size and maximizing security.
Decision
Use multi-stage Docker builds with Alpine Linux base images.
Rationale
- Smaller final images
- Better security (minimal attack surface)
- Build-time optimization
- Consistent environments
- Clear separation of concerns
Build Stages
FROM node:20-alpine AS deps # Dependencies
FROM node:20-alpine AS builder # Build assets
FROM node:20-alpine AS runner # Production
Consequences
- Smaller images (~200MB vs ~1GB)
- Better security
- Faster deployments
- Clear build process
- More complex Dockerfile
- Longer initial builds
ADR-007: Component-Based UI Architecture
Status
Accepted
Context
We need a maintainable, scalable frontend architecture that supports code reuse and clear separation of concerns.
Decision
Implement a component-based architecture with clear hierarchy and composition patterns.
Rationale
- Reusable components
- Clear data flow
- Testable units
- Maintainable code
- Team scalability
Component Hierarchy
App
βββ Router
βββ AuthContext
βββ ThemeContext
βββ Routes
βββ MainContent
β βββ ChatInterface
β βββ FileTree
β βββ Settings
βββ Sidebar
βββ ProjectList
βββ Navigation
Consequences
- Highly maintainable
- Reusable components
- Clear data flow
- Easy to test
- Prop drilling challenges
- Component composition complexity
ADR-008: Session Protection System
Status
Accepted
Context
Automatic project updates from file system watchers were interrupting active chat sessions, causing poor user experience.
Decision
Implement a session protection system that tracks active conversations and pauses non-critical updates.
Rationale
- Prevents UI disruption
- Maintains conversation context
- Better user experience
- Selective update control
Implementation
// Track active sessions
const activeSessions = new Set();
// Pause updates during active session
if (!activeSessions.has(sessionId)) {
updateProjects();
}
Consequences
- Uninterrupted conversations
- Better user experience
- Maintained context
- Potential stale data
- Complex state management
ADR-009: File-Based Configuration
Status
Accepted
Context
We need flexible configuration that works across different deployment environments and allows runtime changes without rebuilding.
Decision
Use environment variables with .env files for configuration, with fallback chains for different environments.
Rationale
- Standard approach
- Works with Docker
- Easy to manage
- Secure (not in code)
- Runtime configurable
Configuration Hierarchy
1. Environment variables
2. .env file in config directory
3. .env file in root
4. Default values in code
Consequences
- Flexible configuration
- Environment-specific settings
- Secure credential management
- File permission concerns
- Configuration drift risk
ADR-010: Markdown as Universal Format
Status
Accepted
Context
We need a common format for storing and processing documents that is human-readable, AI-friendly, and preserves formatting.
Decision
Convert all documents to Markdown for storage and processing.
Rationale
- Human-readable
- AI-friendly
- Preserves formatting
- Version control friendly
- Universal support
- Lightweight
Conversion Pipeline
Upload β Validate β Convert to MD β Store β Index
Consequences
- Consistent format
- Easy processing
- Version control friendly
- Human-readable
- Complex document fidelity loss
- Conversion overhead
ADR-011: Monolithic Deployment
Status
Accepted
Context
As a single-user system with moderate complexity, we need to balance operational simplicity with functionality.
Decision
Deploy as a monolithic application with frontend and backend in a single container.
Rationale
- Operational simplicity
- Single deployment unit
- No service coordination
- Easier debugging
- Lower latency
- Reduced infrastructure cost
Deployment Structure
Container
βββ Node.js Server (Express)
βββ Static Assets (React build)
βββ Claude CLI
βββ SQLite Database
Consequences
- Simple deployment
- Easy to manage
- Lower costs
- Better performance
- Cannot scale independently
- Single point of failure
ADR-012: Browser LocalStorage for Caching
Status
Accepted
Context
We need client-side caching for better performance and offline capability without adding complexity.
Decision
Use browser LocalStorage for caching messages and user preferences.
Rationale
- Simple API
- Synchronous access
- Persistent storage
- No additional dependencies
- 5-10MB capacity sufficient
Implementation
// Safe storage with quota handling
safeLocalStorage.setItem(key, value);
// Automatic compression for large data
// Automatic cleanup on quota exceeded
Consequences
- Simple implementation
- Good performance
- Offline capability
- Storage quota limits
- No cross-device sync
- Manual cleanup required
Future Architectural Considerations
Potential Changes
- Multi-user Support: PostgreSQL + Redis
- Microservices: Separate API, AI, and file services
- Cloud-Native: Kubernetes deployment
- Real-time Collaboration: Operational Transformation/CRDTs
- Plugin Architecture: Dynamic tool loading
- Multi-model AI: Support for different AI providers
Migration Paths
Each current decision has been made with future migration in mind:
- SQLite β PostgreSQL (standard SQL)
- Monolith β Microservices (clear boundaries)
- LocalStorage β IndexedDB/Cloud Sync
- Claude CLI β Direct API/Multiple Providers