diff --git a/.env.example b/.env.example index d54b873..031bde3 100644 --- a/.env.example +++ b/.env.example @@ -1,34 +1,33 @@ # =========================================== -# Synapsis Docker Environment Configuration +# Synapsis Environment Configuration # =========================================== # Copy this file to .env and configure your values # =========================================== -# Required Settings +# REQUIRED: Core Settings # =========================================== -# Domain Configuration -# Replace with your actual domain +# Your domain name (e.g., synapsis.example.com) DOMAIN=your-domain.com -# Database Credentials -# Change these to secure values! +# Admin email addresses (comma-separated for multiple) +ADMIN_EMAILS=admin@your-domain.com + +# Authentication secret - generate with: openssl rand -hex 32 +AUTH_SECRET=your-secret-key-here-minimum-32-characters + +# =========================================== +# REQUIRED: Database +# =========================================== + DB_USER=synapsis DB_PASSWORD=your-secure-password-here DB_NAME=synapsis -# Authentication Secret -# Generate with: openssl rand -hex 32 -AUTH_SECRET=your-secret-key-here-minimum-32-characters - -# Admin Users -# Comma-separated list of email addresses with admin access -ADMIN_EMAILS=admin@your-domain.com - # =========================================== -# S3-Compatible Storage (Required for uploads) +# REQUIRED: S3-Compatible Storage # =========================================== -# You can use AWS S3, MinIO, Wasabi, Backblaze B2, etc. +# Supports AWS S3, MinIO, Wasabi, Backblaze B2, Cloudflare R2, etc. STORAGE_ENDPOINT=https://s3.your-provider.com STORAGE_REGION=us-east-1 @@ -38,17 +37,22 @@ STORAGE_SECRET_KEY=your-secret-key STORAGE_PUBLIC_BASE_URL=https://cdn.your-domain.com # =========================================== -# Optional Settings +# OPTIONAL: General Settings # =========================================== -# Maximum bots per user (default: 5) +# Application port - 'auto' scans 3000-3020 for first available (recommended) +# Or set a specific port: PORT=3000 +PORT=auto + +# Maximum AI bots per user (default: 5) # BOT_MAX_PER_USER=5 -# Redis configuration (if using external Redis) +# =========================================== +# OPTIONAL: Advanced +# =========================================== + +# External Redis URL (if not using the built-in Redis) # REDIS_URL=redis://redis:6379 -# =========================================== -# Docker Compose Settings -# =========================================== - +# Docker Compose project name # COMPOSE_PROJECT_NAME=synapsis diff --git a/Dockerfile b/Dockerfile index ded0ad8..2983bfc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,9 @@ WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 +# Install tools needed for port detection (netcat) +RUN apk add --no-cache netcat-openbsd + # Create non-root user RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs @@ -39,19 +42,26 @@ COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +# Copy and set up entrypoint script +COPY --chown=nextjs:nodejs scripts/docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +# Create shared directory for port file (must be writable by nextjs) +RUN mkdir -p /var/run/synapsis && chown nextjs:nodejs /var/run/synapsis + # Switch to non-root user USER nextjs -# Expose port -EXPOSE 3000 +# Expose port range for auto-detection +EXPOSE 3000-3020 # Set environment variables (can be overridden at runtime) ENV PORT=3000 ENV HOSTNAME="0.0.0.0" -# Health check +# Health check (will use the actual PORT at runtime) HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD curl -f http://localhost:3000/api/node || exit 1 + CMD wget -q --spider http://localhost:${PORT:-3000}/api/health || exit 1 -# Start the application -CMD ["node", "server.js"] +# Use entrypoint script for port detection +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/README.md b/README.md index 00cf6d4..756c4f1 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,64 @@ # Synapsis -Synapsis is an open-source, federated social network built to serve as global communication infrastructure. It is designed to be lightweight, easy to deploy, and built on the Synapsis Swarm network. +**Your node. Your network. Your identity.** -## Features - -- **Swarm Network**: Native peer-to-peer network for Synapsis nodes with gossip protocol. -- **Swarm Chat**: End-to-end encrypted chat system built for the swarm. -- **Decentralized Identity (DIDs)**: Portable identity system that you truly own. -- **AI Bots**: Create and manage AI-powered bot accounts with custom personalities. -- **Modern UI**: Clean, responsive interface inspired by Vercel's design system. -- **Rich Media**: Support for image uploads and media galleries. -- **Moderation**: Built-in admin dashboard for user management and content moderation. -- **Setup Wizard**: User-friendly `/install` flow to get your node running in minutes. -- **Curated Feeds**: Smart feed algorithms to highlight engaging content across the swarm. +Synapsis is an open-source, federated social network built for the decentralized web. Run your own node, own your identity with DIDs, and communicate across the Swarm network with end-to-end encryption. No corporations. No lock-in. Just you and the network. --- -## 📖 User Guide +## 🚀 Deploy Your Own Node (5 Minutes) -New to Synapsis? Visit the **[/guide](/guide)** page in the app for a comprehensive walkthrough on: +Run your personal Synapsis node with a single command: -- How the Swarm network works -- How Synapsis differs from traditional social networks -- How to follow users on other nodes -- How others can follow you -- Understanding Decentralized Identifiers (DIDs) and portable identity +```bash +# 1. Download and start +mkdir -p /opt/synapsis && cd /opt/synapsis +curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker-compose.yml +curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker/Caddyfile +curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker/.env.example +cp .env.example .env + +# 2. Edit .env with your domain and secrets (takes 2 minutes) +nano .env + +# 3. Start your node +docker compose up -d +``` + +Done! Your node is live at `https://your-domain.com` with automatic SSL. No build step. No dependencies. No fuss. + +**Updating:** +```bash +docker compose pull && docker compose up -d +``` + +For detailed Docker setup, see [docker/README.md](docker/README.md). --- -## Architecture & Concepts +## ✨ Features + +- **🌐 Swarm Network** — Native peer-to-peer network with automatic node discovery and gossip protocol +- **💬 Swarm Chat** — End-to-end encrypted messaging across the entire network +- **🔐 Decentralized Identity (DIDs)** — Cryptographic identity you truly own, portable between nodes +- **🤖 AI Bots** — Create AI-powered bot accounts with custom personalities +- **🎨 Modern UI** — Clean, responsive interface inspired by Vercel's design system +- **🖼️ Rich Media** — Image uploads, media galleries, and S3-compatible storage +- **🛡️ Built-in Moderation** — Admin dashboard for user management and content moderation +- **📱 Auto Port Detection** — Runs on the first available port (3000-3020) automatically +- **📊 Curated Feeds** — Smart algorithms highlighting engaging content across the swarm + +--- + +## 📖 Documentation + +- **[Docker Deployment Guide](docker/README.md)** — Complete production deployment instructions +- **[User Guide](/guide)** — Learn how Synapsis works (visit after installing) +- **[API Documentation](https://docs.synapsis.social)** — Developer reference + +--- + +## 🏗️ Architecture Synapsis differs from traditional social networks by prioritizing **sovereign identity** and **native peer-to-peer communication**. @@ -43,33 +73,26 @@ Unlike centralized platforms where your identity is a row in a database owned by | **Key Pair** | Every account has a public/private key pair. Your private key proves you are you; your public key lets others verify your identity. | **Why this matters:** -- **Ownership**: Your identity is cryptographically yours, not controlled by a company. -- **Authenticity**: Every post is signed with your private key, proving it came from you. -- **True Portability**: Move your account between nodes without losing followers. +- **Ownership** — Your identity is cryptographically yours, not controlled by a company +- **Authenticity** — Every post is signed with your private key, proving it came from you +- **True Portability** — Move your account between nodes without losing followers ### 🌐 The Swarm Network Synapsis operates on the **Swarm** — a native peer-to-peer network designed specifically for Synapsis nodes: -- **Gossip Protocol**: Nodes discover each other automatically and exchange information. -- **Swarm Timeline**: Aggregated feed of posts from across all Synapsis nodes. -- **Swarm Chat**: End-to-end encrypted direct messaging between users on any Synapsis node. -- **Handle Registry**: Distributed directory of user handles across the swarm. -- **Instant Interactions**: Likes, reposts, follows, and mentions delivered in real-time. - -**Swarm Features:** -- Real-time post delivery across the network -- Encrypted chat with read receipts -- Automatic node discovery and health monitoring -- Distributed user directory -- Cross-node interactions (likes, reposts, follows) +- **Gossip Protocol** — Nodes discover each other automatically and exchange information +- **Swarm Timeline** — Aggregated feed of posts from across all Synapsis nodes +- **Swarm Chat** — End-to-end encrypted direct messaging between users on any Synapsis node +- **Handle Registry** — Distributed directory of user handles across the swarm +- **Instant Interactions** — Likes, reposts, follows, and mentions delivered in real-time ### 🆚 Synapsis vs. Traditional Federation | Feature | Traditional Federation | Synapsis | |---------|------------------------|----------| | **Identity** | Server-bound (`@user@server`) | DID-based (cryptographic, portable) | -| **Account Migration** | Limited (followers don't auto-migrate) | **Full**: DID-based migration with auto-follow | +| **Account Migration** | Limited (followers don't auto-migrate) | **Full** — DID-based migration with auto-follow | | **Cryptographic Signing** | HTTP Signatures only | Full post signing with user keys | | **Direct Messages** | Posts with limited visibility | True E2E encrypted chat | | **Network Discovery** | Manual server discovery | Automatic gossip protocol | @@ -78,75 +101,54 @@ Synapsis operates on the **Swarm** — a native peer-to-peer network designed sp --- -## Tech Stack +## 🛠️ Development -- **Framework**: [Next.js 15+](https://nextjs.org/) (App Router) -- **Database**: PostgreSQL (via [Neon](https://neon.tech) / Drizzle ORM) -- **Styling**: Tailwind CSS v4 & custom Vercel-like design system -- **Authentication**: Auth.js (NextAuth) -- **Type Safety**: TypeScript +Want to hack on Synapsis? Here's how to run it locally: ---- +### Prerequisites +- Node.js 20+ +- PostgreSQL 15+ +- S3-compatible storage (or MinIO for local dev) -## Recent Updates - -### Swarm Chat (Latest) -- End-to-end encrypted messaging between Synapsis users -- Real-time delivery across nodes -- Read receipts and delivery status -- No legacy protocol limitations - built for the swarm -- See [SWARM_CHAT.md](SWARM_CHAT.md) for details - -### Bug Fixes -- Fixed remote users appearing in local user lists -- Fixed duplicate posts in swarm feeds -- Improved swarm timeline filtering to only show local posts - -### Swarm Network Improvements -- Enhanced gossip protocol for node discovery -- Improved handle registry synchronization -- Better error handling for cross-node communication - ---- - -## 🚀 Run Your Own Node - -### Quick Start (Docker - Recommended) - -Deploy your own Synapsis node in minutes using Docker: +### Local Setup ```bash -# 1. Create directory and download files -mkdir -p /opt/synapsis && cd /opt/synapsis -curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker-compose.yml -curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker/Caddyfile -curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/.env.example +# 1. Clone the repository +git clone https://github.com/cyph3rasi/synapsis.git +cd synapsis -# 2. Configure environment +# 2. Install dependencies +npm install + +# 3. Configure environment cp .env.example .env -# Edit .env with your domain, database password, auth secret, etc. +# Edit .env with your local database and storage settings -# 3. Start your node -docker compose up -d +# 4. Set up the database +npx drizzle-kit push + +# 5. Run the development server +npm run dev ``` -Your node will be available at `https://your-domain.com` with automatic SSL. +Visit `http://localhost:3000` — the app will redirect you to `/install` for initial setup. -**Updating:** -```bash -docker compose pull && docker compose up -d -``` +### Tech Stack -For detailed instructions, see [docker/README.md](docker/README.md). - -### Documentation - -For complete setup instructions, visit: - -**📚 [docs.synapsis.social/run-your-own-node](https://docs.synapsis.social/run-your-own-node)** +- **Framework** — [Next.js 15+](https://nextjs.org/) (App Router) +- **Database** — PostgreSQL with [Drizzle ORM](https://orm.drizzle.team/) +- **Styling** — Tailwind CSS v4 & custom Vercel-like design system +- **Authentication** — Auth.js (NextAuth) +- **Type Safety** — TypeScript --- -## License +## 📜 License Licensed under the **Apache 2.0 License**. See [LICENSE](LICENSE) for details. + +--- + +
+ Run your node. Join the swarm. Own your social. +
diff --git a/docker-compose.yml b/docker-compose.yml index 17f99b2..8b6dae9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,16 +52,16 @@ services: environment: # Database connection DATABASE_URL: postgresql://${DB_USER:-synapsis}:${DB_PASSWORD:-changeme}@postgres:5432/${DB_NAME:-synapsis} - + # Authentication AUTH_SECRET: ${AUTH_SECRET} - + # Domain configuration NEXT_PUBLIC_NODE_DOMAIN: ${DOMAIN:-localhost} - + # Admin emails ADMIN_EMAILS: ${ADMIN_EMAILS} - + # S3 Storage configuration STORAGE_ENDPOINT: ${STORAGE_ENDPOINT} STORAGE_REGION: ${STORAGE_REGION:-us-east-1} @@ -69,15 +69,20 @@ services: STORAGE_ACCESS_KEY: ${STORAGE_ACCESS_KEY} STORAGE_SECRET_KEY: ${STORAGE_SECRET_KEY} STORAGE_PUBLIC_BASE_URL: ${STORAGE_PUBLIC_BASE_URL} - + # Optional settings BOT_MAX_PER_USER: ${BOT_MAX_PER_USER:-5} - + + # Port configuration (auto or specific port) + PORT: ${PORT:-3000} + # Node environment NODE_ENV: production volumes: # Uploads directory (if using local file storage) - uploads_data:/app/uploads + # Shared volume for port coordination with Caddy + - port_data:/var/run/synapsis networks: - synapsis-network depends_on: @@ -108,9 +113,12 @@ services: - "80:80" - "443:443" volumes: - - ./Caddyfile:/etc/caddy/Caddyfile:ro + - ./docker/Caddyfile:/etc/caddy/Caddyfile:ro + - ./scripts/caddy-entrypoint.sh:/usr/local/bin/caddy-entrypoint.sh:ro - caddy_data:/data - caddy_config:/config + # Shared volume for port coordination with app + - port_data:/var/run/synapsis networks: - synapsis-network depends_on: @@ -118,6 +126,8 @@ services: condition: service_healthy environment: - DOMAIN=${DOMAIN:-localhost} + # Use custom entrypoint to read dynamic port from app + entrypoint: ["/usr/local/bin/caddy-entrypoint.sh"] # ============================================ # Persistent Volumes @@ -131,6 +141,8 @@ volumes: driver: local caddy_config: driver: local + port_data: + driver: local # ============================================ # Networks diff --git a/docker/.env.example b/docker/.env.example index d54b873..031bde3 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -1,34 +1,33 @@ # =========================================== -# Synapsis Docker Environment Configuration +# Synapsis Environment Configuration # =========================================== # Copy this file to .env and configure your values # =========================================== -# Required Settings +# REQUIRED: Core Settings # =========================================== -# Domain Configuration -# Replace with your actual domain +# Your domain name (e.g., synapsis.example.com) DOMAIN=your-domain.com -# Database Credentials -# Change these to secure values! +# Admin email addresses (comma-separated for multiple) +ADMIN_EMAILS=admin@your-domain.com + +# Authentication secret - generate with: openssl rand -hex 32 +AUTH_SECRET=your-secret-key-here-minimum-32-characters + +# =========================================== +# REQUIRED: Database +# =========================================== + DB_USER=synapsis DB_PASSWORD=your-secure-password-here DB_NAME=synapsis -# Authentication Secret -# Generate with: openssl rand -hex 32 -AUTH_SECRET=your-secret-key-here-minimum-32-characters - -# Admin Users -# Comma-separated list of email addresses with admin access -ADMIN_EMAILS=admin@your-domain.com - # =========================================== -# S3-Compatible Storage (Required for uploads) +# REQUIRED: S3-Compatible Storage # =========================================== -# You can use AWS S3, MinIO, Wasabi, Backblaze B2, etc. +# Supports AWS S3, MinIO, Wasabi, Backblaze B2, Cloudflare R2, etc. STORAGE_ENDPOINT=https://s3.your-provider.com STORAGE_REGION=us-east-1 @@ -38,17 +37,22 @@ STORAGE_SECRET_KEY=your-secret-key STORAGE_PUBLIC_BASE_URL=https://cdn.your-domain.com # =========================================== -# Optional Settings +# OPTIONAL: General Settings # =========================================== -# Maximum bots per user (default: 5) +# Application port - 'auto' scans 3000-3020 for first available (recommended) +# Or set a specific port: PORT=3000 +PORT=auto + +# Maximum AI bots per user (default: 5) # BOT_MAX_PER_USER=5 -# Redis configuration (if using external Redis) +# =========================================== +# OPTIONAL: Advanced +# =========================================== + +# External Redis URL (if not using the built-in Redis) # REDIS_URL=redis://redis:6379 -# =========================================== -# Docker Compose Settings -# =========================================== - +# Docker Compose project name # COMPOSE_PROJECT_NAME=synapsis diff --git a/docker/Caddyfile b/docker/Caddyfile index aba0d41..401e905 100644 --- a/docker/Caddyfile +++ b/docker/Caddyfile @@ -2,8 +2,8 @@ # Automatic HTTPS via Let's Encrypt {$DOMAIN} { - # Reverse proxy to Synapsis app - reverse_proxy app:3000 + # Reverse proxy to Synapsis app (port is set via APP_PORT env var) + reverse_proxy app:{$APP_PORT:3000} # Enable compression encode gzip diff --git a/docker/Dockerfile b/docker/Dockerfile index 2fa3b03..003d9c0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -51,6 +51,9 @@ WORKDIR /app RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs +# Create shared directory for port file (must be writable by nextjs) +RUN mkdir -p /var/run/synapsis && chown nextjs:nodejs /var/run/synapsis + # Set production environment ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 @@ -80,16 +83,13 @@ RUN chmod +x ./docker-entrypoint.sh # Switch to non-root user USER nextjs -# Expose the application port -EXPOSE 3000 +# Expose port range for auto-detection +EXPOSE 3000-3020 # Health check - longer start period to allow migrations to run -# Using 127.0.0.1 instead of localhost to avoid IPv6 issues +# Uses PORT environment variable (set by entrypoint) HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=5 \ - CMD node -e "require('http').get('http://127.0.0.1:3000/api/health', (r) => r.statusCode === 200 ? process.exit(0) : process.exit(1))" + CMD wget -q --spider http://127.0.0.1:${PORT:-3000}/api/health || exit 1 # Set the entrypoint ENTRYPOINT ["./docker-entrypoint.sh"] - -# Default command (can be overridden) -CMD ["node", "server.js"] diff --git a/docker/README.md b/docker/README.md index 1de913e..35091b1 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,223 +1,115 @@ -# Synapsis Docker Production Deployment +# Synapsis Docker Deployment -One-command Docker deployment for Synapsis using pre-built images from GitHub Container Registry. +Production Docker deployment using pre-built images from GitHub Container Registry. + +--- ## 🚀 Quick Start ```bash -# 1. Create a directory for your Synapsis instance -mkdir -p /opt/synapsis -cd /opt/synapsis - -# 2. Download the required files +mkdir -p /opt/synapsis && cd /opt/synapsis curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker-compose.yml curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker/Caddyfile curl -O https://raw.githubusercontent.com/cyph3rasi/synapsis/main/docker/.env.example - -# 3. Set up environment variables cp .env.example .env -nano .env # Edit all required values - -# 4. Start the stack +nano .env # Add your domain and secrets docker compose up -d - -# 5. Check logs -docker compose logs -f ``` -Your Synapsis instance will be available at `https://your-domain.com` (Caddy automatically handles SSL). +Your node is live at `https://your-domain.com` with automatic SSL. --- -## 📋 Server Requirements +## 📋 What You Need -| Resource | Minimum | Recommended | -|----------|---------|-------------| -| RAM | 2 GB | 4 GB | -| CPU | 2 cores | 4 cores | -| Disk | 20 GB SSD | 50 GB SSD | -| OS | Ubuntu 22.04/24.04, Debian 12, or compatible | +| Requirement | Details | +|-------------|---------| +| **Server** | 2GB RAM, 2 CPU cores, 20GB SSD (minimum) | +| **Domain** | A domain or subdomain pointing to your server | +| **Docker** | Version 24.0+ with Docker Compose 2.20+ | +| **Storage** | S3-compatible service (AWS S3, MinIO, Wasabi, Backblaze B2, etc.) | -### Required Software -- Docker 24.0+ -- Docker Compose 2.20+ - -### Installation (Ubuntu/Debian) +**Install Docker (Ubuntu/Debian):** ```bash -# Install Docker curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER newgrp docker - -# Verify -docker --version -docker compose version ``` --- -## 🔧 Configuration +## ⚙️ Configuration -### 1. Environment Variables +Edit `.env` and set these required values: -Copy `.env.example` to `.env` and configure: +| Variable | What to put | +|----------|-------------| +| `DOMAIN` | Your domain (e.g., `synapsis.example.com`) | +| `DB_PASSWORD` | Strong password for PostgreSQL | +| `AUTH_SECRET` | Run: `openssl rand -hex 32` | +| `ADMIN_EMAILS` | Your email address | +| `STORAGE_*` | Your S3-compatible storage credentials | -```bash -cp .env.example .env -nano .env -``` - -**Required settings:** -- `DOMAIN` - Your domain name (e.g., `synapsis.example.com`) -- `DB_PASSWORD` - Strong database password -- `AUTH_SECRET` - Generate with `openssl rand -hex 32` -- `ADMIN_EMAILS` - Admin user email(s) -- `STORAGE_*` - S3-compatible storage credentials - -### 2. DNS Setup - -Point your domain's A record to your server's IP address: -``` -A synapsis.example.com → YOUR_SERVER_IP -``` - -Caddy will automatically obtain and renew SSL certificates via Let's Encrypt. +**Port Configuration:** +- `PORT=auto` (default) — Automatically finds an available port between 3000-3020 +- `PORT=3000` — Use a specific port instead --- ## 🔄 Updates -Updating is now simple - just pull the latest image: - ```bash cd /opt/synapsis +docker compose pull && docker compose up -d +``` -# Pull the latest image -docker compose pull +--- -# Restart with new image -docker compose up -d +## 🛠️ Common Commands -# Run migrations if needed -docker compose exec app npx drizzle-kit push - -# Verify -docker compose ps +```bash +# View logs docker compose logs -f app -``` -### One-Command Update Script - -Create `update.sh`: -```bash -#!/bin/bash -cd /opt/synapsis -docker compose pull -docker compose up -d -docker compose exec -T app npx drizzle-kit push || true -echo "✅ Update complete!" -``` - -Make it executable and run: -```bash -chmod +x update.sh -./update.sh -``` - ---- - -## 📁 Directory Structure - -``` -/opt/synapsis/ -├── docker-compose.yml # Main orchestration file (downloaded from GitHub) -├── Caddyfile # Caddy reverse proxy config -├── .env # Your environment variables -└── update.sh # Optional update script -``` - -**Note:** You no longer need to clone the entire Git repository. Just download the three files above. - ---- - -## 🛠️ Management Commands - -### View Logs -```bash -docker compose logs -f app # Application logs -docker compose logs -f caddy # Caddy logs -docker compose logs -f postgres # Database logs -docker compose logs -f # All logs -``` - -### Database Operations -```bash -# Access database shell -docker compose exec postgres psql -U synapsis -d synapsis - -# Backup database -docker compose exec postgres pg_dump -U synapsis synapsis > backup.sql - -# Restore database -docker compose exec -T postgres psql -U synapsis -d synapsis < backup.sql - -# Run migrations manually -docker compose exec app npx drizzle-kit push -``` - -### Container Management -```bash # Restart services docker compose restart app -docker compose restart caddy # Stop everything docker compose down -# Stop and remove volumes (⚠️ destroys data!) -docker compose down -v +# Database backup +docker compose exec postgres pg_dump -U synapsis synapsis > backup.sql -# View running containers -docker compose ps - -# Enter container shell -docker compose exec app sh -docker compose exec postgres sh +# Access database +docker compose exec postgres psql -U synapsis -d synapsis ``` --- -## 🔒 Security Features - -This Docker setup includes: - -- **Immutable source code** - Application runs from pre-built image -- **Non-root execution** - App runs as unprivileged user -- **Network isolation** - Services communicate via internal Docker network -- **Automatic HTTPS** - Caddy handles SSL certificates -- **Security headers** - X-Frame-Options, X-Content-Type-Options, etc. -- **Resource limits** - Memory constraints on all containers -- **Health checks** - Automatic container health monitoring - ---- - ## 🔍 Troubleshooting ### Container won't start ```bash -# Check for configuration errors -docker compose config +docker compose config # Validate config +docker compose logs app --tail=50 # Check errors +``` -# View detailed logs -docker compose logs app --tail=100 +### Port already in use +`PORT=auto` (default) automatically finds an available port. If you set a specific port that's taken: +```bash +# Check what's using the port +sudo netstat -tlnp | grep :3000 + +# Switch back to auto or choose a different port +# Edit .env: PORT=auto ``` ### Database connection failed ```bash -# Check database is healthy +# Check database health docker compose ps -# Verify environment variables +# Verify environment variables loaded docker compose exec app env | grep DATABASE ``` @@ -226,51 +118,25 @@ docker compose exec app env | grep DATABASE # Check Caddy logs docker compose logs caddy -# Test Caddy configuration +# Test Caddy config docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile ``` -### Port already in use -```bash -# Find process using port 80/443 -sudo netstat -tlnp | grep :80 - -# Change ports in docker-compose.yml if needed -``` - ### Image pull fails ```bash -# Check if image exists +# Verify image exists docker pull ghcr.io/cyph3rasi/synapsis:latest -# View available tags at: +# Check available tags at: # https://github.com/cyph3rasi/synapsis/pkgs/container/synapsis ``` --- -## 📊 Monitoring - -### Health Checks -- App: `https://your-domain.com/api/health` -- Caddy: Built into the Caddyfile - -### Resource Usage -```bash -# Container stats -docker stats - -# Disk usage -docker system df -``` - ---- - ## 💾 Backup Strategy -### Automated Backup Script - Create `/opt/synapsis/backup.sh`: + ```bash #!/bin/bash BACKUP_DIR="/var/backups/synapsis" @@ -280,48 +146,27 @@ mkdir -p $BACKUP_DIR # Database backup docker compose exec -T postgres pg_dump -U synapsis synapsis > "$BACKUP_DIR/db_$DATE.sql" -# Uploads backup -tar -czf "$BACKUP_DIR/uploads_$DATE.tar.gz" -C /var/lib/docker/volumes/synapsis_uploads_data/_data . - -# Keep only last 7 days -find $BACKUP_DIR -type f -mtime +7 -delete - echo "✅ Backup complete: $DATE" ``` -Add to crontab: +Schedule daily backups: ```bash -0 2 * * * /opt/synapsis/backup.sh >> /var/log/synapsis-backup.log 2>&1 +chmod +x /opt/synapsis/backup.sh +echo "0 2 * * * /opt/synapsis/backup.sh" | sudo crontab - ``` --- -## 🏗️ Building from Source (Advanced) +## 🏗️ Building from Source -If you prefer to build the image locally instead of using the pre-built one: +To build locally instead of using pre-built images: ```bash -# Clone the repository git clone https://github.com/cyph3rasi/synapsis.git cd synapsis/docker - -# Build and run docker compose -f docker-compose.build.yml up -d --build ``` -See `docker-compose.yml` in the docker/ directory for the build configuration. - --- -## 📞 Support - -For issues or questions: -1. Check logs: `docker compose logs -f` -2. Review configuration: `docker compose config` -3. Consult the main Synapsis documentation: https://docs.synapsis.social - ---- - -## 📝 License - -This Docker configuration follows the same license as Synapsis (Apache 2.0). +For full documentation, visit [docs.synapsis.social](https://docs.synapsis.social) diff --git a/docker/caddy-entrypoint.sh b/docker/caddy-entrypoint.sh new file mode 100755 index 0000000..dc99f1b --- /dev/null +++ b/docker/caddy-entrypoint.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Caddy Entrypoint Script for Synapsis +# Reads the dynamic port from the shared file and starts Caddy + +set -e + +SHARED_PORT_FILE="/var/run/synapsis/port" +DEFAULT_PORT=3000 + +# Wait for the port file to exist (with timeout) +echo "⏳ Waiting for Synapsis app to announce its port..." +TIMEOUT=60 +ELAPSED=0 + +while [ ! -f "$SHARED_PORT_FILE" ] && [ $ELAPSED -lt $TIMEOUT ]; do + sleep 1 + ELAPSED=$((ELAPSED + 1)) +done + +if [ -f "$SHARED_PORT_FILE" ]; then + APP_PORT=$(cat "$SHARED_PORT_FILE") + echo "✅ Found Synapsis app on port: $APP_PORT" +else + echo "⚠️ Port file not found after ${TIMEOUT}s, using default port: $DEFAULT_PORT" + APP_PORT=$DEFAULT_PORT +fi + +export APP_PORT + +# Validate that APP_PORT is a number +if ! echo "$APP_PORT" | grep -qE '^[0-9]+$'; then + echo "❌ Invalid port number: $APP_PORT" + exit 1 +fi + +echo "🌐 Starting Caddy reverse proxy to app:$APP_PORT..." +echo "" + +# Start Caddy with the environment variable set +exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7ef630b..29b1774 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -52,16 +52,16 @@ services: environment: # Database connection DATABASE_URL: postgresql://${DB_USER:-synapsis}:${DB_PASSWORD:-changeme}@postgres:5432/${DB_NAME:-synapsis} - + # Authentication AUTH_SECRET: ${AUTH_SECRET} - + # Domain configuration NEXT_PUBLIC_NODE_DOMAIN: ${DOMAIN:-localhost} - + # Admin emails ADMIN_EMAILS: ${ADMIN_EMAILS} - + # S3 Storage configuration STORAGE_ENDPOINT: ${STORAGE_ENDPOINT} STORAGE_REGION: ${STORAGE_REGION:-us-east-1} @@ -69,15 +69,20 @@ services: STORAGE_ACCESS_KEY: ${STORAGE_ACCESS_KEY} STORAGE_SECRET_KEY: ${STORAGE_SECRET_KEY} STORAGE_PUBLIC_BASE_URL: ${STORAGE_PUBLIC_BASE_URL} - + # Optional settings BOT_MAX_PER_USER: ${BOT_MAX_PER_USER:-5} - + + # Port configuration (auto or specific port) + PORT: ${PORT:-3000} + # Node environment NODE_ENV: production volumes: # Uploads directory (if using local file storage) - uploads_data:/app/uploads + # Shared volume for port coordination with Caddy + - port_data:/var/run/synapsis networks: - synapsis-network depends_on: @@ -108,8 +113,11 @@ services: - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro + - ./caddy-entrypoint.sh:/usr/local/bin/caddy-entrypoint.sh:ro - caddy_data:/data - caddy_config:/config + # Shared volume for port coordination with app + - port_data:/var/run/synapsis networks: - synapsis-network depends_on: @@ -117,6 +125,8 @@ services: condition: service_healthy environment: - DOMAIN=${DOMAIN:-localhost} + # Use custom entrypoint to read dynamic port from app + entrypoint: ["/usr/local/bin/caddy-entrypoint.sh"] # ============================================ # Persistent Volumes @@ -130,6 +140,8 @@ volumes: driver: local caddy_config: driver: local + port_data: + driver: local # ============================================ # Networks diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh old mode 100644 new mode 100755 index 3f99df9..b46cb8c --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -1,15 +1,85 @@ #!/bin/sh # Synapsis Docker Entrypoint Script -# Handles database migrations and application startup +# Handles database migrations, port detection, and application startup set -e +# ============================================ +# Port Detection Configuration +# ============================================ +PORT_START=${PORT_START:-3000} +PORT_END=${PORT_END:-3020} + +# Function to check if a port is available +check_port_available() { + local port=$1 + if ! nc -z localhost "$port" 2>/dev/null; then + return 0 # Port is available + else + return 1 # Port is in use + fi +} + +# Function to find first available port in range +find_available_port() { + local start=$1 + local end=$2 + + for port in $(seq "$start" "$end"); do + if check_port_available "$port"; then + echo "$port" + return 0 + fi + done + + echo "ERROR: No available ports found in range $start-$end" >&2 + return 1 +} + +# Handle PORT=auto +if [ "${PORT}" = "auto" ]; then + echo "🔍 PORT=auto detected, scanning for available port in range ${PORT_START}-${PORT_END}..." + + DETECTED_PORT=$(find_available_port "$PORT_START" "$PORT_END") + + if [ $? -ne 0 ]; then + echo "❌ Failed to find an available port. Exiting." + exit 1 + fi + + export PORT="$DETECTED_PORT" + echo "✅ Using automatically detected port: $PORT" +else + echo "📡 Using configured port: $PORT" +fi + +# Ensure PORT is set +if [ -z "$PORT" ]; then + echo "⚠️ PORT not set, defaulting to 3000" + export PORT=3000 +fi + +# Write port to shared file for Caddy to read +SHARED_PORT_FILE="/var/run/synapsis/port" +mkdir -p "$(dirname "$SHARED_PORT_FILE")" +echo "$PORT" > "$SHARED_PORT_FILE" +echo "📝 Port $PORT written to $SHARED_PORT_FILE" + +# Export HOSTNAME for Next.js +export HOSTNAME="0.0.0.0" + +# ============================================ +# Database Migrations +# ============================================ + +echo "" echo "========================================" echo " Synapsis - Starting Application" echo "========================================" echo " Time: $(date)" echo " Working Dir: $(pwd)" echo " Database URL: ${DATABASE_URL%%:*}://***@***" +echo " Port: $PORT" echo "========================================" # Function to wait for database @@ -48,7 +118,6 @@ run_migrations() { ls -la drizzle/ 2>/dev/null || echo " (drizzle dir not found or empty)" # Run migrations using npm script - # This uses the drizzle.config.ts which should be in the app root echo " Executing: npm run db:push" npm run db:push 2>&1 || { echo "⚠️ Migration command exited with error (may be already up to date)" @@ -74,5 +143,5 @@ echo " Node Version: $(node --version)" echo "========================================" echo "" -# Execute the main command -exec "$@" +# Start the application +exec node server.js diff --git a/scripts/caddy-entrypoint.sh b/scripts/caddy-entrypoint.sh new file mode 100755 index 0000000..dc99f1b --- /dev/null +++ b/scripts/caddy-entrypoint.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Caddy Entrypoint Script for Synapsis +# Reads the dynamic port from the shared file and starts Caddy + +set -e + +SHARED_PORT_FILE="/var/run/synapsis/port" +DEFAULT_PORT=3000 + +# Wait for the port file to exist (with timeout) +echo "⏳ Waiting for Synapsis app to announce its port..." +TIMEOUT=60 +ELAPSED=0 + +while [ ! -f "$SHARED_PORT_FILE" ] && [ $ELAPSED -lt $TIMEOUT ]; do + sleep 1 + ELAPSED=$((ELAPSED + 1)) +done + +if [ -f "$SHARED_PORT_FILE" ]; then + APP_PORT=$(cat "$SHARED_PORT_FILE") + echo "✅ Found Synapsis app on port: $APP_PORT" +else + echo "⚠️ Port file not found after ${TIMEOUT}s, using default port: $DEFAULT_PORT" + APP_PORT=$DEFAULT_PORT +fi + +export APP_PORT + +# Validate that APP_PORT is a number +if ! echo "$APP_PORT" | grep -qE '^[0-9]+$'; then + echo "❌ Invalid port number: $APP_PORT" + exit 1 +fi + +echo "🌐 Starting Caddy reverse proxy to app:$APP_PORT..." +echo "" + +# Start Caddy with the environment variable set +exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100755 index 0000000..b0b8656 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# Synapsis Docker Entrypoint Script +# Handles automatic port detection when PORT=auto + +set -e + +# Default port range for auto-detection +PORT_START=${PORT_START:-3000} +PORT_END=${PORT_END:-3020} + +# Function to check if a port is available +check_port_available() { + local port=$1 + if ! nc -z localhost "$port" 2>/dev/null; then + return 0 # Port is available + else + return 1 # Port is in use + fi +} + +# Function to find first available port in range +find_available_port() { + local start=$1 + local end=$2 + + for port in $(seq "$start" "$end"); do + if check_port_available "$port"; then + echo "$port" + return 0 + fi + done + + echo "ERROR: No available ports found in range $start-$end" >&2 + return 1 +} + +# Handle PORT=auto +if [ "${PORT}" = "auto" ]; then + echo "🔍 PORT=auto detected, scanning for available port in range ${PORT_START}-${PORT_END}..." + + DETECTED_PORT=$(find_available_port "$PORT_START" "$PORT_END") + + if [ $? -ne 0 ]; then + echo "❌ Failed to find an available port. Exiting." + exit 1 + fi + + export PORT="$DETECTED_PORT" + echo "✅ Using automatically detected port: $PORT" +else + echo "📡 Using configured port: $PORT" +fi + +# Ensure PORT is set +if [ -z "$PORT" ]; then + echo "⚠️ PORT not set, defaulting to 3000" + export PORT=3000 +fi + +# Write port to shared file for Caddy to read +SHARED_PORT_FILE="/var/run/synapsis/port" +mkdir -p "$(dirname "$SHARED_PORT_FILE")" +echo "$PORT" > "$SHARED_PORT_FILE" +echo "📝 Port $PORT written to $SHARED_PORT_FILE" + +# Export HOSTNAME for Next.js +export HOSTNAME="0.0.0.0" + +echo "🚀 Starting Synapsis on port $PORT..." +echo "" + +# Start the Next.js application +exec node server.js