From 66abd500e0475b2a0c0955196164c7135f1d2513 Mon Sep 17 00:00:00 2001 From: Claude Code Bot Date: Sat, 13 Jun 2026 09:13:28 +0000 Subject: [PATCH] Update self-hosting guide to match current monorepo architecture The happy-server repo was archived and merged into the main happy monorepo (slopus/happy). This updates the self-hosting guide with: - New repo URL and Dockerfile.server build path - HANDY_MASTER_SECRET (replaces non-existent SEED env var) - Port 3005 everywhere (was incorrectly 3000 in many places) - Standalone mode with PGlite (no Postgres/Redis needed) - npm install -g happy + happy server as the easiest path - Complete environment variable reference table - S3 lifecycle and encryption configuration docs - Simplified Docker Compose (single container for standalone) - Updated Kubernetes secrets (HANDY_MASTER_SECRET, optional DB/Redis) Co-Authored-By: Claude Opus 4.6 (1M context) --- content/guides/self-hosting.mdx | 233 +++++++++++++++++++++++++------- 1 file changed, 183 insertions(+), 50 deletions(-) diff --git a/content/guides/self-hosting.mdx b/content/guides/self-hosting.mdx index e54f7be..fafe6be 100644 --- a/content/guides/self-hosting.mdx +++ b/content/guides/self-hosting.mdx @@ -12,21 +12,71 @@ When you self-host the Happy Server, you get: - **Team control** - Run one server for your whole team - **Zero dependencies** - Never worry about a service shutting down -The entire server is only 1,293 lines of typescript. You can read it yourself to -verify it just forwards encrypted messages. +The server is fully open source and part of the [Happy monorepo](https://github.com/slopus/happy). +You can read the code yourself to verify it just forwards encrypted messages. -## Quick Start +## Quick Start (Easiest) + +The simplest way to self-host is with the `happy` CLI. No Docker, no database setup needed. + + +### Install Happy + +```bash +npm install -g happy +``` + +### Start the Server + +```bash +happy server +``` + +On first start, this will: +- Create a master secret in `~/.happy/server/master-secret` +- Start an embedded database (PGlite) - no Postgres needed +- Serve on `http://127.0.0.1:3005` +- Write the server URL to your local Happy settings + +To expose the server on your local network (for mobile access): + +```bash +happy server --host 0.0.0.0 --port 3005 +``` + +### Configure Your Devices + +**On your phone:** +1. Open Happy app +2. Go to Settings +3. Set "Relay Server URL" to `http://your-server:3005` +4. Save + +**On your computer:** +```bash +export HAPPY_SERVER_URL="http://your-server:3005" +``` + +If you used `happy server`, the CLI automatically picks up the local server URL from your settings. + + + +That's it! Your Happy setup now runs through your own server. + +## Quick Start with Docker + +The standalone Docker image runs everything in a single container with no external dependencies (no Postgres, no Redis, no S3). ### Clone and Build ```bash # Get the code -git clone https://github.com/slopus/happy-server -cd happy-server +git clone https://github.com/slopus/happy +cd happy # Build with Docker -docker build -t happy-server:latest . +docker build -t happy-server -f Dockerfile.server . ``` ### Run the Server @@ -34,39 +84,61 @@ docker build -t happy-server:latest . ```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 \ + -p 3005:3005 \ + -e HANDY_MASTER_SECRET="$(openssl rand -hex 32)" \ + -v happy-data:/data \ --restart unless-stopped \ - happy-server:latest + 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) +This uses: +- **PGlite** - an embedded PostgreSQL database (data stored in `/data/pglite`) +- **Local filesystem** - for file uploads (stored in `/data/files`) +- **In-memory event bus** - no Redis needed + +Data persists in the `happy-data` Docker volume across container restarts. ### Configure Your Devices **On your phone:** 1. Open Happy app 2. Go to Settings -3. Set "Relay Server URL" to `http://your-server:3000` +3. Set "Relay Server URL" to `http://your-server:3005` 4. Save **On your computer:** ```bash -export HAPPY_SERVER_URL="http://your-server:3000" -# Configure your CLI to use your server +export HAPPY_SERVER_URL="http://your-server:3005" ``` -That's it! Your Happy setup now runs through your own server. +### Environment Variables + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `HANDY_MASTER_SECRET` | Yes | - | Master secret for auth and encryption | +| `PORT` | No | `3005` | Server port | +| `PUBLIC_URL` | No | `http://localhost:3005` | Public base URL for file URLs sent to clients | +| `DATA_DIR` | No | `/data` | Base data directory | +| `PGLITE_DIR` | No | `/data/pglite` | PGlite database directory | +| `NODE_ENV` | No | `production` | Environment mode | + +### Optional: External Services + +For larger deployments, you can use external Postgres or Redis instead of the embedded defaults: + +| Variable | Description | +|----------|-------------| +| `DATABASE_URL` | PostgreSQL connection URL (bypasses PGlite) | +| `REDIS_URL` | Redis connection URL (enables cross-process pub/sub) | +| `S3_HOST` | S3/MinIO host (bypasses local file storage) | +| `S3_PORT` | S3 port | +| `S3_ACCESS_KEY` | S3 access key | +| `S3_SECRET_KEY` | S3 secret key | +| `S3_BUCKET` | S3 bucket name | +| `S3_PUBLIC_URL` | Public URL for S3 assets | +| `S3_USE_SSL` | Whether to use SSL for S3 (`true`/`false`, default `true`) | ## Production Setup with HTTPS @@ -83,7 +155,7 @@ sudo apt install caddy # Configure reverse proxy sudo tee /etc/caddy/Caddyfile </attachments/.enc`. Two bucket-level settings should be applied once at deploy time: + +**1. Lifecycle rule for attachment TTL.** Add a lifecycle rule so objects age out automatically. Pick a TTL that matches your retention policy (30 days is a reasonable default). + +```bash +# AWS CLI +aws s3api put-bucket-lifecycle-configuration --bucket happy-blobs \ + --lifecycle-configuration '{ + "Rules": [{ + "ID": "session-attachments-ttl", + "Status": "Enabled", + "Filter": { "Prefix": "sessions/" }, + "Expiration": { "Days": 30 } + }] + }' + +# MinIO +mc ilm rule add myminio/happy-blobs \ + --expire-days 30 \ + --prefix "sessions/" +``` + +**2. Server-side encryption (defense-in-depth).** Blobs are already end-to-end encrypted by the client, but enabling AES-256 SSE on the bucket adds an extra layer of protection. + +```bash +# AWS CLI +aws s3api put-bucket-encryption --bucket happy-blobs \ + --server-side-encryption-configuration '{ + "Rules": [{ + "ApplyServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + }] + }' + +# MinIO +mc encrypt set sse-s3 myminio/happy-blobs +``` + +When using local storage mode (no `S3_HOST`), blobs are written under `/files/sessions//attachments/`. Clean up old session directories on a cron if you want a TTL. ## Security Notes @@ -291,11 +428,6 @@ spec: type: ClusterIP ``` -**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 - ## Kubernetes Secrets Create a secret file for your environment variables: @@ -308,11 +440,12 @@ 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" + HANDY_MASTER_SECRET: "replace-with-a-strong-random-secret" PORT: "3005" NODE_ENV: "production" + # Add these if using external Postgres/Redis: + # DATABASE_URL: "postgresql://postgres:postgres@postgres-service:5432/happy" + # REDIS_URL: "redis://redis-service:6379" ``` Apply the secret: @@ -321,7 +454,7 @@ kubectl apply -f happy-secrets.yaml ``` **Security Note**: Replace the values with your actual configuration. For production: -- Use a strong, random SEED value +- Use a strong, random `HANDY_MASTER_SECRET` value (e.g., `openssl rand -hex 32`) - Use proper database credentials - Consider using external secret management tools like external-secrets-operator with Vault @@ -333,4 +466,4 @@ After setting up your server: 2. [Set up monitoring](/guides/monitoring) 3. [Enable voice coding](/docs/features/voice-coding-with-claude-code) -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 +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.