diff --git a/CLAUDE.md b/CLAUDE.md
index 9e20d49..04f4eaa 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,111 +1,173 @@
-# Writing Guidelines for Content Contributors
+# CLAUDE.md
-## Reading Level Requirements
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-All `.mdx` files in the ./content/ directory should target a **10th grade reading level**. This means using simple, clear language that's accessible to most readers while maintaining the depth and accuracy of information.
+## Project Overview
-## Core Principles
+This is the documentation website for **Happy** - an open-source mobile client for Claude Code. Built with Next.js 15 + Nextra, deployed to GitHub Pages via static export.
-### 1. Simplicity Without Sacrificing Depth
+## Development Commands
-We don't want to "dumb down" content or skip over important nuance. Instead, we want to include ALL the information that someone reading at a post-graduate level would expect to find, but present it using:
+```bash
+npm run dev # Development server with Turbopack
+npm run build # Production build (outputs to .next)
+npm run export # Full static export with search index & sitemap
+npm run preview # Build and serve on port 3001 for testing
+npm run start # Start production server
+npm run lint # Run ESLint
+```
-- Simple vocabulary (avoid rare or overly technical words when common alternatives exist)
-- Clear sentence structures (shorter sentences, active voice when possible)
-- Plain English explanations of complex concepts
-- Step-by-step breakdowns of complicated processes
+**Note**: `npm run export` is the canonical build command for production. It runs `next build`, generates the Pagefind search index, and creates the sitemap.
-### 2. Complete Information Coverage
+## Architecture
-Every reader should be able to find the detailed information and answers they're looking for, regardless of their background knowledge. This means:
+### Tech Stack
+- **Next.js 15.4.5** with App Router
+- **Nextra 4.6** - Documentation framework with blog support
+- **Tailwind CSS v4** - Styling
+- **TypeScript** - Strict mode enabled
+- **Turbopack** - Development server
+- **Pagefind** - Static site search
+- **GitHub Pages** - Deployment target
-- Including comprehensive explanations
-- Providing context for technical concepts
-- Adding examples and analogies when helpful
-- Covering edge cases and important details
+### Key Directories
+
+| Directory | Purpose |
+|-----------|---------|
+| `/app` | Next.js App Router pages |
+| `/content` | MDX documentation files (processed by Nextra) |
+| `/components` | React components (marketing, UI, terminal demos) |
+| `/public` | Static assets (images, videos, icons) |
+| `/types` | TypeScript type definitions |
+
+### Routing Structure
+
+```
+/ # Landing page (app/page.tsx)
+/docs/[[...mdxPath]]/ # Documentation routes from /content
+/blog/ # Blog index
+/blog/[slug]/ # Blog posts from /app/blog/*/page.mdx
+/tools/[[...kind]]/ # Tools pages
+/privacy, /terms # Legal pages
+```
-## Writing Process and File Organization
+**Important**: Documentation content in `/content` maps to `/docs/*` URLs (configured via `contentDirBasePath: '/docs'` in next.config.mjs).
-### Draft Files: `*.notes.txt`
+### Content Workflow
-When preparing content for an `.mdx` file, start by creating a corresponding `*.notes.txt` file (e.g., `how-it-works.notes.txt`). This file should contain:
+The project uses a three-file system for content creation:
-- **All detailed information** you want to include
-- Technical explanations in their natural form
-- Complex concepts before simplification
-- Research notes and references
-- **No reading level restrictions** - write as technically as needed
+1. **`*.notes.txt`** - Draft content with no reading level restrictions
+ - Write detailed technical explanations
+ - Brainstorm and organize ideas
+ - No simplification required
-### Communication Files: `*.talk.txt`
+2. **`*.talk.txt`** - Collaboration context
+ - Goals, audience, editorial feedback
+ - Metacommentary about the writing process
-Create `*.talk.txt` files to communicate with other contributors about:
+3. **`*.mdx`** - Final published content
+ - Target: **10th grade reading level**
+ - Simplified but complete information
+ - Processed by Nextra and displayed on the website
-- The goals and purpose of each page
-- Target audience considerations
-- Key concepts that must be covered
-- Specific communication objectives
-- **No reading level restrictions** - use whatever language is clearest for collaboration
+**Build Configuration**: Both `*.notes.txt` and `*.talk.txt` files are excluded from the build via webpack IgnorePlugin and Turbopack rules in next.config.mjs.
-### Final Content: `*.mdx`
+### Nextra Configuration
-The final `.mdx` files should be the 10th-grade reading level versions that transform your detailed notes into accessible content.
+- **Themes**: `nextra-theme-docs` for docs, `nextra-theme-blog` for blog
+- **Search**: Pagefind integration (codeblocks indexed)
+- **Copy Code**: Enabled by default
+- **Static Images**: Optimized automatically
+- **Sidebar**: Default menu collapse level = 1
-## File Extensions and Nextra
+### TypeScript Path Aliases
-- `.mdx` files → Processed by Nextra and displayed on the website
-- `.notes.txt` files → Ignored by Nextra, treated as Markdown in VS Code
-- `.talk.txt` files → Ignored by Nextra, treated as Markdown in VS Code
+```json
+{
+ "@/*": ["./*"]
+}
+```
-This setup lets you keep all your detailed work alongside the final content without cluttering the published documentation.
+Use `@/components/...` to import from project root.
+
+## Writing Guidelines for Content Contributors
+
+### Reading Level Requirements
+
+All `.mdx` files in the ./content/ directory should target a **10th grade reading level**. This means using simple, clear language that's accessible to most readers while maintaining the depth and accuracy of information.
+
+### Core Principles
+
+#### 1. Simplicity Without Sacrificing Depth
+
+We don't want to "dumb down" content or skip over important nuance. Instead, we want to include ALL the information that someone reading at a post-graduate level would expect to find, but present it using:
+
+- Simple vocabulary (avoid rare or overly technical words when common alternatives exist)
+- Clear sentence structures (shorter sentences, active voice when possible)
+- Plain English explanations of complex concepts
+- Step-by-step breakdowns of complicated processes
+
+#### 2. Complete Information Coverage
+
+Every reader should be able to find the detailed information and answers they're looking for, regardless of their background knowledge. This means:
+
+- Including comprehensive explanations
+- Providing context for technical concepts
+- Adding examples and analogies when helpful
+- Covering edge cases and important details
-## Why Simple Language for Technical Audiences?
+### Claude's Writing Workflow
-You might be thinking: *"This is a highly technical product only meant for software engineers. Why can't I expect them to be fully versed in all the jargon and terminology I know? Why can't we raise the bar a little?"*
+When users ask you to write a new article or edit an existing article in the content/ directory, follow this specific process:
-Here's why simple language is better, even for technical audiences:
+#### Step 1: Start with Notes, Not Final Content
-**Just because you CAN use complicated language doesn't mean you SHOULD.**
+**Always begin by creating a `*.notes.txt` file first.** Do not jump straight to writing the final `.mdx` content. This notes file is your workspace for:
+- Brainstorming and organizing ideas
+- Writing detailed technical explanations
+- Iterating on structure and content
+- **No reading level restrictions** - write naturally and technically
-Richard Feynman, Nobel Prize-winning physicist, put it perfectly:
+#### Step 2: Capture User Goals and Feedback
-> "If you can't explain it simply, you don't understand it well enough."
+If users provide goals, target audience specifications, editorial feedback, or metacommentary about the writing process, **put all of this in a corresponding `*.talk.txt` file.** This keeps collaboration context separate from content development.
-## The Real Benefits of Simple Language
+#### Step 3: Iterate in the Notes File
-When you truly understand something, you should be able to:
+Work with the user to refine the content in the `*.notes.txt` file until they're satisfied with:
+- The completeness of information
+- The logical flow and structure
+- The coverage of important topics
+- The technical accuracy
-- **Be direct** - Get to the point without unnecessary complexity
-- **Paint a clear picture** - Help readers visualize concepts immediately
-- **Eliminate confusion** - Prevent readers from having to reread sections
-- **Reduce cognitive load** - Let readers focus on the concepts, not decoding your language
+#### Step 4: Translate to Final Format
-Even brilliant developers appreciate clear explanations. Complex jargon often
-just gets in the way of communication.
+**Only after the user is happy with the notes version** should you create the final `.mdx` file by translating the content to 10th grade reading level following the guidelines below.
-## Practical Tips
+### Practical Writing Tips
-### Instead of rare words, use common ones:
+**Instead of rare words, use common ones:**
- "utilize" → "use"
- "implement" → "set up" or "put in place"
- "subsequently" → "then" or "after that"
- "facilitate" → "help" or "make easier"
-### Break up complex sentences:
+**Break up complex sentences:**
- **Complex**: "The implementation of this feature, which requires careful consideration of multiple interdependent factors, can significantly enhance user experience."
- **Simple**: "This feature can greatly improve user experience. Setting it up requires thinking about several connected factors."
-### Use active voice when possible:
+**Use active voice when possible:**
- **Passive**: "The configuration should be modified by the administrator."
- **Active**: "The administrator should modify the configuration."
-### Add context for technical terms:
+**Add context for technical terms:**
- **Technical**: "Configure the API endpoint."
- **With context**: "Configure the API endpoint (the web address where your app sends requests)."
-## Quality Check
+### Quality Checklist
Before finalizing any `.mdx` content, ask yourself:
-
1. Would a high school sophomore understand this explanation?
2. Have I included all the important details an expert would need?
3. Are there any unnecessarily complex words I could replace?
@@ -114,41 +176,6 @@ Before finalizing any `.mdx` content, ask yourself:
Remember: Our goal is to make complex information accessible to everyone, not to make it simplistic.
-------------------------------------
-
-## Claude's Writing Workflow
-
-When users ask you to write a new article or edit an exiting article in the content/ directory, follow this specific process:
-
-### Step 1: Start with Notes, Not Final Content
-
-**Always begin by creating a `*.notes.txt` file first.** Do not jump straight to writing the final `.mdx` content. This notes file is your workspace for:
-
-- Brainstorming and organizing ideas
-- Writing detailed technical explanations
-- Iterating on structure and content
-- **No reading level restrictions** - write naturally and technically
-
-### Step 2: Capture User Goals and Feedback
-
-If users provide:
-- Goals for the article
-- Target audience specifications
-- Editorial feedback or suggestions
-- Metacommentary about the writing process
-
-**Put all of this in a corresponding `*.talk.txt` file.** This keeps the collaboration context separate from the content development.
-
-### Step 3: Iterate in the Notes File
-
-Work with the user to refine the content in the `*.notes.txt` file until they're satisfied with:
-- The completeness of information
-- The logical flow and structure
-- The coverage of important topics
-- The technical accuracy
-
-### Step 4: Translate to Final Format
-
-**Only after the user is happy with the notes version** should you create the final `.mdx` file by translating the content to 10th grade reading level following the guidelines below.
+---
-This process ensures we capture all the important information first, then make it accessible without losing crucial details.
\ No newline at end of file
+> "If you can't explain it simply, you don't understand it well enough." — Richard Feynman
diff --git a/content/guides/self-hosting.mdx b/content/guides/self-hosting.mdx
index e54f7be..f665763 100644
--- a/content/guides/self-hosting.mdx
+++ b/content/guides/self-hosting.mdx
@@ -2,241 +2,232 @@ import { Steps } from 'nextra/components'
# Self-Host Your Own Happy Server
-Run your own relay server in minutes. Keep complete control over your Claude Code mobile setup.
+Run your own relay server in about 3 minutes. Keep complete control over your Claude Code mobile setup.
-## Why Run Your Own Server?
+## Why Self-Host?
-When you self-host the Happy Server, you get:
-- **Total privacy** - Your encrypted data stays on your hardware
-- **No limits** - Set your own rate limits and storage
-- **Team control** - Run one server for your whole team
-- **Zero dependencies** - Never worry about a service shutting down
+### Complete Control
-The entire server is only 1,293 lines of typescript. You can read it yourself to
-verify it just forwards encrypted messages.
+- Your server, your rules
+- No dependency on other companies' infrastructure
+- Customize rate limits and storage as needed
-## Quick Start
+### Better Privacy
-
-### Clone and Build
+- Keep all encrypted data on your own hardware
+- Full record of all connections
+- No third-party company has any record of your usage
-```bash
-# Get the code
-git clone https://github.com/slopus/happy-server
-cd happy-server
+### Team Deployments
-# Build with Docker
-docker build -t happy-server:latest .
-```
+- Run one server for your entire development team
+- Keep all team communications within your network
+- Connect with your existing infrastructure
+
+## What You Need
-### Run the Server
+**Before you start, make sure you have:**
+
+- Docker installed on your server
+- A server with a public IP address (or accessible within your network)
+- About 1GB of disk space for the server and logs
+- Network access from both your computer and mobile device
+- **A publicly accessible domain name (strongly recommended)** - This is critical for mobile app WebSocket connections to work properly
+
+## Step 1: Clone the Repository
```bash
-docker run -d \
- --name happy-server \
- -p 3000:3000 \
- -e NODE_ENV=production \
- -e DATABASE_URL="postgresql://postgres:postgres@localhost:5432/happy-server" \
- -e REDIS_URL="redis://localhost:6379" \
- -e SEED="your-seed-for-token-generation" \
- -e PORT=3005 \
- --restart unless-stopped \
- happy-server:latest
+git clone https://github.com/slopus/happy-server
+cd happy-server
```
-**Important**: Replace the environment variable values with your actual configuration:
-- `DATABASE_URL`: Your PostgreSQL connection string
-- `REDIS_URL`: Your Redis connection string
-- `SEED`: A secure random seed for token generation
-- `PORT`: The port the application listens on (3005)
+The entire server is only about 900 lines of code. You can read through it in a few minutes to verify it just forwards encrypted messages.
-### Configure Your Devices
+## Step 2: Build the Docker Image
-**On your phone:**
-1. Open Happy app
-2. Go to Settings
-3. Set "Relay Server URL" to `http://your-server:3000`
-4. Save
-
-**On your computer:**
```bash
-export HAPPY_SERVER_URL="http://your-server:3000"
-# Configure your CLI to use your server
+docker build -t happy-server:latest .
```
-
+This creates a Docker image with the Happy Server. The build takes about 30 seconds.
-That's it! Your Happy setup now runs through your own server.
+## Step 3: Push to Your Container Registry (Optional)
-## Production Setup with HTTPS
-
-For real use, you want HTTPS. Here's the easiest way with Caddy:
+If you're deploying to a cloud provider or using Kubernetes:
```bash
-# Install Caddy (auto-manages SSL certificates)
-sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
-curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
-curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
-sudo apt update
-sudo apt install caddy
-
-# Configure reverse proxy
-sudo tee /etc/caddy/Caddyfile <
+ /bin/sh -c "
+ sleep 5;
+ /usr/bin/mc alias set local http://happy-minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
+ /usr/bin/mc mb local/${S3_BUCKET} --ignore-existing;
+ /usr/bin/mc anonymous set download local/${S3_BUCKET};
+ exit 0;
+ "
+ restart: "no"
+
volumes:
postgres_data:
redis_data:
+ minio_data:
```
-Run with: `docker-compose up -d`
-
-This setup includes PostgreSQL and Redis containers that the Happy Server depends on.
-
-## Where to Host
-
-### Option 1: Home Server (Free)
-Run on any computer at home:
-- Raspberry Pi ($35 one-time)
-- Old laptop
-- Mac Mini
-- Desktop that's always on
-
-### Option 2: Cloud VPS ($5-10/month)
-- **DigitalOcean**: $6/month droplet works great
-- **Linode**: $5/month shared CPU
-- **Vultr**: $6/month regular performance
-- **Hetzner**: €4/month (best value in Europe)
-
-### Option 3: Corporate Network
-Run inside your company network for team use. The server works behind firewalls and proxies.
-
-## System Requirements
+Create `.env` file with your configuration:
-Happy Server is lightweight:
-
-**For 1-10 developers:**
-- 512MB RAM
-- 1 CPU core
-- 10GB storage
-- 100Mbps network
-
-**For 10-100 developers:**
-- 2GB RAM
-- 2 CPU cores
-- 100GB storage
-- 1Gbps network
-
-## Monitor Your Server
-
-Check server health:
```bash
-# View logs
-docker logs -f happy-server
-
-# Check health endpoint
-curl http://your-server:3000/health
-
-# See connection count
-curl http://your-server:3000/stats
+# Database connection
+DATABASE_URL=postgresql://postgres:yourpassword@postgres:5432/happy
+POSTGRES_DB=happy
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=yourpassword
+
+# Server authentication
+HANDY_MASTER_SECRET=your-secret-here
+
+# IMPORTANT: This MUST be a publicly accessible URL
+# Use your domain name with HTTPS, or your public IP with port
+HAPPY_SERVER_URL=https://your-domain.com
+# For local testing without SSL:
+# HAPPY_SERVER_URL=http://your-public-ip:3005
+
+# S3/MinIO storage
+S3_BUCKET=happy
+S3_PUBLIC_URL=https://your-domain.com
+
+# MinIO credentials
+# SECURITY: Generate strong random credentials, don't use "minioadmin"
+MINIO_ROOT_USER=your-minio-user
+MINIO_ROOT_PASSWORD=your-minio-password
```
-## Backup Your Data
-
-The server stores encrypted blobs in `/data`. To backup:
+**Generate secure credentials:**
```bash
-# Simple backup
-tar -czf backup-$(date +%Y%m%d).tar.gz ./data
-
-# Or sync to another location
-rsync -av ./data/ backup-location/
+# Generate secure random credentials
+openssl rand -base64 16 # for MINIO_ROOT_USER
+openssl rand -base64 32 # for MINIO_ROOT_PASSWORD
+openssl rand -base64 32 # for HANDY_MASTER_SECRET
```
-Remember: These backups are encrypted. No one can read them without your device keys.
-
-
-## Security Notes
-
-The Happy Server design keeps your data safe:
+**Deploy the services:**
-1. **Server can't read your data** - Everything is encrypted before sending
-2. **No account needed** - Authentication uses cryptographic proofs
-3. **No logs of your code** - Server only logs connection metadata
-4. **Open source** - Audit the code yourself
-
-For extra security:
-- Use HTTPS in production
-- Run behind a VPN for team deployments
-- Set up fail2ban for brute force protection
-
-## Cost Comparison
-
-**Self-hosted Happy Server:**
-- Home: Free (use existing hardware)
-- VPS: $5-10/month for unlimited use
+```bash
+# Start all services (database, redis, minio, and happy-server)
+docker-compose up -d
-**Commercial alternatives:**
-- Omnara: $9/month per user
-- Cursor Mobile: Runs on their VMs
-- Terragon/Siteboon: Usage-based pricing
-- Conductor: $29/month after free tier
+# Check all services are running
+docker-compose ps
-Self-hosting saves money and gives you control.
+# View logs
+docker-compose logs -f happy-server
+```
-## Advanced: Kubernetes Deployment
+The services start in this order:
+1. PostgreSQL, Redis, and MinIO (data storage)
+2. migrate (sets up database schema)
+3. minio-setup (creates S3 bucket)
+4. happy-server (starts after all dependencies ready)
-For teams using Kubernetes, here's the production configuration:
+### Kubernetes Deployment
```yaml
apiVersion: apps/v1
@@ -244,11 +235,6 @@ kind: Deployment
metadata:
name: happy-server
spec:
- strategy:
- type: RollingUpdate
- rollingUpdate:
- maxUnavailable: 0
- maxSurge: 1
replicas: 1
selector:
matchLabels:
@@ -260,23 +246,16 @@ spec:
spec:
containers:
- name: happy-server
- image: happy-server:latest # Use your actual image registry
+ image: your-registry.com/happy-server:latest
ports:
- - containerPort: 3005
- envFrom:
- - secretRef:
- name: happy-secrets
----
-apiVersion: policy/v1
-kind: PodDisruptionBudget
-metadata:
- name: happy-server-pdb
-spec:
- minAvailable: 1
- selector:
- matchLabels:
- app: happy-server
-
+ - containerPort: 8080
+ volumeMounts:
+ - name: data
+ mountPath: /data
+ volumes:
+ - name: data
+ persistentVolumeClaim:
+ claimName: happy-server-pvc
---
apiVersion: v1
kind: Service
@@ -286,51 +265,295 @@ spec:
selector:
app: happy-server
ports:
- - port: 3005
- targetPort: 3005
- type: ClusterIP
+ - port: 80
+ targetPort: 8080
+ type: LoadBalancer
```
-**Configuration Notes**:
-- All ports now consistently use 3005 (the application's actual listening port)
-- Uses the `happy-secrets` secret created in the previous section
-- Update the image name to match your actual container registry
+## Step 5: Set Up SSL/TLS (Recommended)
-## Kubernetes Secrets
+For production use, you should use HTTPS with Caddy as a reverse proxy. Caddy automatically handles SSL certificates from Let's Encrypt.
-Create a secret file for your environment variables:
+```bash
+# Install Caddy
+curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/caddy-stable.asc
+curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
+sudo apt update
+sudo apt install caddy
-```yaml
-# happy-secrets.yaml
-apiVersion: v1
-kind: Secret
-metadata:
- name: happy-secrets
-type: Opaque
-stringData:
- DATABASE_URL: "postgresql://postgres:postgres@postgres-service:5432/happy-server"
- REDIS_URL: "redis://redis-service:6379"
- SEED: "your-secure-seed-for-token-generation"
- PORT: "3005"
- NODE_ENV: "production"
+# Configure Caddy to proxy to Happy Server
+sudo tee /etc/caddy/Caddyfile <
+
+### Open Settings
+
+1. Open the Happy app on your phone
+2. Go to Settings
+3. Find "Relay Server URL"
+
+### Enter Your Server URL
+
+Enter your server URL:
+- With SSL: `https://your-domain.com`
+- Without SSL: `http://your-server-ip:3005`
+
+### Save Settings
+
+Tap Save to apply the settings.
+
+
+
+**Critical**: The URL you enter here must exactly match `HAPPY_SERVER_URL` in your server's environment. This is essential for WebSocket connections to work properly.
+
+## Step 7: Configure Happy CLI
+
+On your computer, configure the CLI to use your server:
+
+```bash
+# Set the server URL in your environment
+export HAPPY_SERVER_URL="https://your-domain.com"
+
+# Or add to your shell profile (.bashrc, .zshrc, etc.)
+echo 'export HAPPY_SERVER_URL="https://your-domain.com"' >> ~/.bashrc
+
+# Alternatively, use a config file
+happy config set server-url https://your-domain.com
+```
+
+## Step 8: Test the Connection
+
+1. Run `happy` on your computer
+2. Scan the QR code with your mobile app
+3. Verify the connection works
+
+You should see "Connected to relay server" in both the CLI and mobile app.
+
+## Monitor Your Server
+
+### View Server Logs
+
+```bash
+docker logs -f happy-server
+```
+
+### Check Server Health
+
+The server has a health endpoint:
+
+```bash
+curl http://your-server:3005/health
+```
+
+### Backup Encrypted Data
+
+The server stores encrypted data in the `/data` directory. To backup:
+
+```bash
+# Create backup
+tar -czf happy-backup-$(date +%Y%m%d).tar.gz ./data
+
+# Or use rsync for incremental backups
+rsync -av ./data/ backup-server:/backups/happy-server/
+```
+
+Remember: These backups are encrypted. Even if someone gets your backup files, they can't read your code without the encryption keys from your devices.
+
+## Performance Tuning
+
+### For Small Teams (1-10 developers)
+
+The default configuration works fine:
+- 1 CPU core
+- 512MB RAM
+- 10GB disk space
+
+### For Larger Teams (10-100 developers)
+
+Increase resources:
+- 2-4 CPU cores
+- 2GB RAM
+- 100GB disk space
+- Consider running multiple instances behind a load balancer
+
+### Rate Limiting
+
+The default rate limit is 1,000 messages per second, which is more than enough for most teams. To adjust:
+
+```bash
+docker run -d \
+ -e RATE_LIMIT=5000 \
+ # ... other options
+ happy-server:latest
+```
+
+## Troubleshooting
+
+### Connection Refused
+
+Check that:
+1. The server is running: `docker ps`
+2. The port is open: `netstat -tlnp | grep 3005`
+3. Firewall allows connections: `sudo ufw allow 3005`
+
+### Mobile Shows "Connected" but CLI Waits Forever
+
+This is a common issue. The problem is almost always related to WebSocket and real-time communication.
+
+**Solutions:**
+
+1. **Check `HAPPY_SERVER_URL`** - This MUST be a publicly accessible URL that both devices can reach:
+ ```bash
+ # In your .env file or docker-compose.yml
+ HAPPY_SERVER_URL=https://your-public-domain.com
+ # NOT: http://localhost:3005
+ ```
+
+2. **Verify SSL/TLS setup** - Mobile devices often reject self-signed certificates. Use Caddy or Let's Encrypt for proper SSL.
+
+3. **Check firewall rules** - Ensure both port 3005 and WebSocket connections are allowed:
+ ```bash
+ sudo ufw allow 3005
+ sudo ufw allow 3005/tcp
+ ```
+
+4. **Test WebSocket connection** - Use a WebSocket testing tool or browser console:
+ ```javascript
+ const ws = new WebSocket('wss://your-domain.com');
+ ws.onopen = () => console.log('WebSocket connected');
+ ws.onerror = (error) => console.log('WebSocket error:', error);
+ ```
+
+5. **Check server logs** - Look for WebSocket-related errors:
+ ```bash
+ docker-compose logs -f happy-server
+ ```
+
+### Certificate Issues
+
+If using self-signed certificates:
+1. Export your certificate
+2. Install it on your mobile device
+3. Trust the certificate in your device settings
+
+### High Memory Usage
+
+Encrypted data is stored indefinitely by default. To clean old data:
+
+```bash
+# Remove data older than 30 days
+find ./data -type f -mtime +30 -delete
```
-Apply the secret:
+Or configure automatic cleanup in the server environment:
+
```bash
-kubectl apply -f happy-secrets.yaml
+docker run -d \
+ -e BLOB_RETENTION_DAYS=30 \
+ # ... other options
+ happy-server:latest
```
-**Security Note**: Replace the values with your actual configuration. For production:
-- Use a strong, random SEED value
-- Use proper database credentials
-- Consider using external secret management tools like external-secrets-operator with Vault
+## Security Considerations
+
+### Network Security
+
+1. **Use HTTPS in production** - Prevents man-in-the-middle attacks
+2. **Firewall rules** - Only allow connections from your network if possible
+3. **VPN access** - Consider requiring VPN for team deployments
+
+### Server Security
+
+1. **Regular updates** - Keep Docker and your OS updated
+2. **Limited access** - Don't run the server as root
+3. **Monitoring** - Set up alerts for unusual activity
+
+### Data Security
+
+Remember that the server never sees unencrypted data:
+- All data is encrypted on your devices before sending
+- The server only stores and forwards encrypted data
+- Only devices with the shared secret can decrypt messages
+
+## Advanced Configurations
+
+### Running Behind Corporate Proxy
+
+If your company uses a proxy:
+
+```bash
+docker run -d \
+ -e HTTP_PROXY=http://proxy.company.com:8080 \
+ -e HTTPS_PROXY=http://proxy.company.com:8080 \
+ # ... other options
+ happy-server:latest
+```
+
+### High Availability Setup
+
+For mission-critical deployments, run multiple servers:
+
+1. Deploy 3+ instances
+2. Use a load balancer (HAProxy, nginx, or cloud LB)
+3. Share storage between instances (NFS, S3, etc.)
+4. Configure health checks
+
+### Integration with Existing Infrastructure
+
+The Happy Server can integrate with:
+- **Prometheus** - Metrics endpoint at `/metrics`
+- **Grafana** - Pre-built dashboards available
+- **ELK Stack** - JSON structured logs
+- **Kubernetes** - Helm chart available
+
+## Cost Comparison
+
+### Self-Hosted
+- **Small VPS**: $5-10/month (DigitalOcean, Linode)
+- **Home server**: Free (Raspberry Pi, old laptop)
+- **Cloud (AWS/GCP)**: $20-50/month depending on usage
+
+### Managed Alternatives
+- **Omnara**: $9/month per user + Claude Code costs
+- **Terragon**: Free tier limited, then usage-based
+- **Siteboon**: Usage-based pricing
+- **Conductor**: Free tier, then $29/month
+
+Self-hosting Happy Server gives you unlimited usage for just the cost of a small server.
+
+## Summary
+
+Setting up your own Happy Server takes about 3 minutes and gives you:
+- Complete control over your infrastructure
+- Enhanced privacy with no third-party involvement
+- Unlimited usage for your entire team
+- No vendor lock-in or surprise pricing changes
-## Next Steps
+The server is simple, auditable, and designed to do one thing well: relay encrypted messages between your devices.
-After setting up your server:
+## Credits
-1. [Configure team access](/guides/team-setup)
-2. [Set up monitoring](/guides/monitoring)
-3. [Enable voice coding](/docs/features/voice-coding-with-claude-code)
+This guide incorporates improvements from the community, especially:
+- **@geekdada** - Provided the complete Docker Compose configuration with automated database migrations and MinIO setup
-Running your own Happy Server takes 3 minutes and gives you permanent control over your Claude Code mobile access. No subscriptions, no lock-in, no surprises.
\ No newline at end of file
+The Docker Compose setup in this guide directly addresses the WebSocket connection issues by ensuring all services are properly configured and the `HAPPY_SERVER_URL` is set to a publicly accessible URL.
diff --git a/content/guides/self-hosting.notes.txt b/content/guides/self-hosting.notes.txt
index c712252..61a2a31 100644
--- a/content/guides/self-hosting.notes.txt
+++ b/content/guides/self-hosting.notes.txt
@@ -26,6 +26,7 @@ Many developers want to run their own relay server for complete control over the
- A server with a public IP address (or accessible within your network)
- About 1GB of disk space for the server and logs
- Network access from both your computer and mobile device
+- **A publicly accessible domain name (recommended)** - Critical for mobile CLI WebSocket connections
## Step 1: Clone the Repository
@@ -58,41 +59,168 @@ docker push your-registry.com/happy-server:latest
## Step 4: Run the Server
-### Basic Docker Run
+### Docker Compose
-```bash
-docker run -d \
- --name happy-server \
- -p 8080:8080 \
- -v $(pwd)/data:/data \
- --restart unless-stopped \
- happy-server:latest
-```
+Happy Server requires multiple services to run properly. Here's a complete setup with all required components:
-### Docker Compose
+**Important**: The `HAPPY_SERVER_URL` environment variable MUST be a publicly accessible URL that both your CLI and mobile client can reach. This is critical for WebSocket/real-time communication to work properly.
-Create a `docker-compose.yml`:
+Create `docker-compose.yml`:
```yaml
version: '3.8'
+
services:
happy-server:
image: happy-server:latest
+ build:
+ context: ./happy-server
+ dockerfile: Dockerfile
+ restart: unless-stopped
ports:
- - "8080:8080"
+ - "3005:3005"
+ environment:
+ - NODE_ENV=production
+ - PORT=3005
+ - DATABASE_URL=${DATABASE_URL}
+ - REDIS_URL=redis://redis:6379
+ - HANDY_MASTER_SECRET=${HANDY_MASTER_SECRET}
+ - HAPPY_SERVER_URL=${HAPPY_SERVER_URL}
+ - S3_HOST=happy-minio
+ - S3_PORT=9000
+ - S3_USE_SSL=false
+ - S3_ACCESS_KEY=${MINIO_ROOT_USER}
+ - S3_SECRET_KEY=${MINIO_ROOT_PASSWORD}
+ - S3_BUCKET=${S3_BUCKET}
+ - S3_PUBLIC_URL=${S3_PUBLIC_URL}
+ depends_on:
+ - postgres
+ - redis
+ - happy-minio
+ - migrate
+ - minio-setup
+
+ # Database migration service - runs once on startup
+ migrate:
+ image: happy-server:latest
+ command: npx prisma db push --schema=/app/node_modules/.prisma/client/schema.prisma
+ environment:
+ - DATABASE_URL=${DATABASE_URL}
+ depends_on:
+ - postgres
+ restart: "no"
+
+ postgres:
+ image: postgres:15.4
+ environment:
+ - POSTGRES_DB=${POSTGRES_DB}
+ - POSTGRES_USER=${POSTGRES_USER}
+ - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- - ./data:/data
+ - postgres_data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
+
+ redis:
+ image: redis:7-alpine
restart: unless-stopped
+ ports:
+ - "6379:6379"
+ volumes:
+ - redis_data:/data
+
+ happy-minio:
+ image: minio/minio:latest
+ ports:
+ - "9000:9000"
+ - "9001:9001"
environment:
- - MAX_BLOB_SIZE=100MB # Optional: customize limits
- - RATE_LIMIT=2000 # Optional: messages per second
+ MINIO_ROOT_USER: ${MINIO_ROOT_USER}
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
+ volumes:
+ - minio_data:/data
+ command: server /data --console-address :9001
+
+ # Initialize MinIO bucket - runs once on startup
+ minio-setup:
+ image: minio/mc:latest
+ depends_on:
+ - happy-minio
+ entrypoint: >
+ /bin/sh -c "
+ sleep 5;
+ /usr/bin/mc alias set local http://happy-minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
+ /usr/bin/mc mb local/${S3_BUCKET} --ignore-existing;
+ /usr/bin/mc anonymous set download local/${S3_BUCKET};
+ exit 0;
+ "
+ restart: "no"
+
+volumes:
+ postgres_data:
+ redis_data:
+ minio_data:
+```
+
+Create `.env` file with your configuration:
+
+```bash
+# Database connection
+DATABASE_URL=postgresql://postgres:yourpassword@postgres:5432/happy
+POSTGRES_DB=happy
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=yourpassword
+
+# Server authentication
+HANDY_MASTER_SECRET=your-secret-here
+
+# IMPORTANT: This MUST be a publicly accessible URL
+# Use your domain name with HTTPS, or your public IP with port
+HAPPY_SERVER_URL=https://your-domain.com
+# For local testing without SSL:
+# HAPPY_SERVER_URL=http://your-public-ip:3005
+
+# S3/MinIO storage
+S3_BUCKET=happy
+S3_PUBLIC_URL=https://your-domain.com
+
+# MinIO credentials
+# SECURITY: Generate strong random credentials, don't use "minioadmin"
+MINIO_ROOT_USER=your-minio-user
+MINIO_ROOT_PASSWORD=your-minio-password
+```
+
+**Generate secure credentials:**
+
+```bash
+# Generate secure random credentials
+openssl rand -base64 16 # for MINIO_ROOT_USER
+openssl rand -base64 32 # for MINIO_ROOT_PASSWORD
+openssl rand -base64 32 # for HANDY_MASTER_SECRET
```
-Then run:
+**Deploy the services:**
+
```bash
+# Start all services (database, redis, minio, and happy-server)
docker-compose up -d
+
+# Check all services are running
+docker-compose ps
+
+# View logs
+docker-compose logs -f happy-server
```
+The services start in this order:
+1. PostgreSQL, Redis, and MinIO (data stores)
+2. migrate (sets up database schema)
+3. minio-setup (creates S3 bucket)
+4. happy-server (starts after all dependencies ready)
+
### Kubernetes Deployment
```yaml
@@ -138,7 +266,7 @@ spec:
## Step 5: Configure SSL/TLS (Recommended)
-For production use, you should use HTTPS. Here's a simple setup with Caddy as a reverse proxy:
+For production use, you should use HTTPS with Caddy as a reverse proxy. Caddy automatically handles SSL certificates from Let's Encrypt.
```bash
# Install Caddy
@@ -147,10 +275,15 @@ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo
sudo apt update
sudo apt install caddy
-# Configure Caddy
+# Configure Caddy to proxy to Happy Server
sudo tee /etc/caddy/Caddyfile < console.log('WebSocket connected');
+ ws.onerror = (error) => console.log('WebSocket error:', error);
+ ```
+
+5. **Check server logs** - Look for WebSocket-related errors:
+ ```bash
+ docker-compose logs -f happy-server
+ ```
### Certificate Issues
@@ -356,8 +527,15 @@ Self-hosting Happy Server gives you unlimited usage for just the cost of a small
Setting up your own Happy Server takes about 3 minutes and gives you:
- Complete control over your infrastructure
-- Enhanced privacy with no third-party involvement
+- Enhanced privacy with no third-party involvement
- Unlimited usage for your entire team
- No vendor lock-in or surprise pricing changes
-The server is simple, auditable, and designed to do one thing well: relay encrypted messages between your devices. It's the Unix philosophy applied to mobile Claude Code access.
\ No newline at end of file
+The server is simple, auditable, and designed to do one thing well: relay encrypted messages between your devices. It's the Unix philosophy applied to mobile Claude Code access.
+
+## Credits & References
+
+This guide incorporates improvements from the community, especially:
+- **@geekdada** - Provided the complete Docker Compose configuration with automated database migrations and MinIO setup (GitHub issue #12)
+
+The Docker Compose setup in this guide directly addresses the WebSocket connection issues reported in issue #12 by ensuring all services are properly configured and the `HAPPY_SERVER_URL` is set to a publicly accessible URL.
\ No newline at end of file