# jama πŸ’¬ ### *just another messaging app* A modern, self-hosted team messaging Progressive Web App (PWA) built for small to medium teams. jama runs entirely in a single Docker container with no external database dependencies β€” all data is stored locally using SQLite. --- ## Features - πŸ” **Authentication** β€” Login, remember me, forced password change on first login - πŸ’¬ **Real-time messaging** β€” WebSocket (Socket.io) powered chat - πŸ‘₯ **Public channels** β€” Admin-created, all users auto-joined - πŸ”’ **Private groups** β€” User-created, owner-managed - πŸ“· **Image uploads** β€” Attach images to messages - πŸ’¬ **Message quoting** β€” Reply to any message with preview - 😎 **Emoji reactions** β€” Quick reactions + full emoji picker - @**Mentions** β€” @mention users with autocomplete, they get notified - πŸ”— **Link previews** β€” Auto-fetches OG metadata for URLs - πŸ“± **PWA** β€” Install to home screen, works offline - πŸ‘€ **Profiles** β€” Custom avatars, display names, about me - βš™οΈ **Admin settings** β€” Custom logo, app name - πŸ‘¨β€πŸ’Ό **User management** β€” Create, suspend, delete, bulk CSV import - πŸ“’ **Read-only channels** β€” Announcement-style public channels --- ## Quick Start ### Prerequisites - Docker & Docker Compose ### 1. Build a versioned image ```bash # Build and tag as v1.0.0 (also tags :latest) ./build.sh 1.0.0 # Build latest only ./build.sh ``` ### 2. Deploy with Docker Compose ```bash cp .env.example .env # Edit .env β€” set TEAMCHAT_VERSION, admin credentials, JWT_SECRET nano .env docker compose up -d # View logs docker compose logs -f ``` App will be available at **http://localhost:3000** --- ## Release Workflow TeamChat uses a **build-then-run** pattern. You build the image once on your build machine (or CI), then the compose file just runs the pre-built image β€” no build step at deploy time. ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Build machine / CI β”‚ β”‚ Server / Portainer β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ./build.sh 1.2.0 │─────▢│ TEAMCHAT_VERSION=1.2.0 β”‚ β”‚ (or push to β”‚ β”‚ docker compose up -d β”‚ β”‚ registry first) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Build script usage ```bash # Build and tag as :latest only ./build.sh # Build and tag as a specific version (also tags :latest) ./build.sh 1.0.0 # Build and push to Docker Hub REGISTRY=yourdockerhubuser ./build.sh 1.0.0 push # Build and push to GitHub Container Registry REGISTRY=ghcr.io/yourorg ./build.sh 1.0.0 push ``` After a successful build the script prints the exact `.env` and `docker compose` commands needed to deploy. --- ## Installation ### 1. Clone the repository ```bash git clone https://github.com/yourorg/jama.git cd jama ``` ### 2. Build the Docker image ```bash ./build.sh 1.0.0 ``` ### 3. Configure environment ```bash cp .env.example .env nano .env ``` At minimum, change `ADMIN_EMAIL`, `ADMIN_PASS`, and `JWT_SECRET`. See [Environment Variables](#environment-variables) for all options. ### 4. Start the container ```bash docker compose up -d # Follow startup logs docker compose logs -f jama ``` On first startup you should see: ``` [DB] Default admin created: admin@yourdomain.com [DB] Default jama group created [DB] Support group created ``` ### 5. Log in Open `http://your-server:3000` in a browser, log in with your `ADMIN_EMAIL` and `ADMIN_PASS`, and change your password when prompted. --- ## HTTPS & SSL (Required for Web Push and PWA install prompt) jama does not manage SSL itself. Use **Caddy** as a reverse proxy β€” it obtains and renews Let's Encrypt certificates automatically. ### docker-compose.yaml (with Caddy) ```yaml version: '3.8' services: jama: image: jama:${JAMA_VERSION:-latest} container_name: jama restart: unless-stopped expose: - "3000" # internal only β€” Caddy is the sole entry point environment: - NODE_ENV=production - ADMIN_NAME=${ADMIN_NAME:-Admin User} - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@jama.local} - ADMIN_PASS=${ADMIN_PASS:-Admin@1234} - PW_RESET=${PW_RESET:-false} - JWT_SECRET=${JWT_SECRET:-changeme} - APP_NAME=${APP_NAME:-jama} - JAMA_VERSION=${JAMA_VERSION:-latest} volumes: - jama_db:/app/data - jama_uploads:/app/uploads healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"] interval: 30s timeout: 10s retries: 3 caddy: image: caddy:alpine container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" # HTTP/3 volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_certs:/config depends_on: - jama volumes: jama_db: jama_uploads: caddy_data: caddy_certs: ``` ### Caddyfile Create a `Caddyfile` in the same directory as `docker-compose.yaml`: ``` chat.yourdomain.com { reverse_proxy jama:3000 } ``` > **Prerequisites:** Your domain's DNS A record must point to your server's public IP *before* starting Caddy, so the Let's Encrypt HTTP challenge can complete. --- ## docker-compose.yaml Reference (without Caddy) The default `docker-compose.yaml` exposes jama directly on a host port: ```yaml version: '3.8' services: jama: image: jama:${JAMA_VERSION:-latest} container_name: jama restart: unless-stopped ports: - "${PORT:-3000}:3000" # change PORT in .env to use a different host port environment: - NODE_ENV=production - ADMIN_NAME=${ADMIN_NAME:-Admin User} - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@jama.local} - ADMIN_PASS=${ADMIN_PASS:-Admin@1234} - PW_RESET=${PW_RESET:-false} - JWT_SECRET=${JWT_SECRET:-changeme_super_secret_jwt_key_2024} - APP_NAME=${APP_NAME:-jama} volumes: - jama_db:/app/data # SQLite database - jama_uploads:/app/uploads # avatars, logos, message images healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"] interval: 30s timeout: 10s retries: 3 volumes: jama_db: driver: local jama_uploads: driver: local ``` --- ## Environment Variables | Variable | Default | Description | |---|---|---| | `JAMA_VERSION` | `latest` | Docker image tag to run. Set by `build.sh` or manually. | | `ADMIN_NAME` | `Admin User` | Display name of the default admin account | | `ADMIN_EMAIL` | `admin@jama.local` | Login email for the default admin account | | `ADMIN_PASS` | `Admin@1234` | Initial password for the default admin account | | `PW_RESET` | `false` | If `true`, resets the admin password to `ADMIN_PASS` on every container restart. Shows a warning banner on the login page. For emergency access recovery only. | | `JWT_SECRET` | *(insecure default)* | Secret used to sign auth tokens. **Must be changed in production.** Use a long random string. | | `PORT` | `3000` | Host port to bind (only applies when not using Caddy's `expose` setup) | | `APP_NAME` | `jama` | Initial application name. Can also be changed at any time in the Settings UI. | > **Note:** `ADMIN_EMAIL` and `ADMIN_PASS` are only used on the **very first run** to seed the admin account. Once the database exists these values are ignored β€” unless `PW_RESET=true`. ### Example `.env` ```env JAMA_VERSION=1.0.0 ADMIN_NAME=Your Name ADMIN_EMAIL=admin@yourdomain.com ADMIN_PASS=ChangeThisNow! PW_RESET=false JWT_SECRET=replace-this-with-a-long-random-string-at-least-32-chars PORT=3000 APP_NAME=jama ``` --- ## First Login & Setup Checklist 1. Open your app URL and log in with `ADMIN_EMAIL` / `ADMIN_PASS` 2. Change your password when prompted 3. Open βš™οΈ **Settings** (bottom-left menu β†’ Settings): - Upload a custom logo - Set the app name - Optionally upload custom New Chat and Group Info icons 4. Open πŸ‘₯ **User Manager** to create accounts for your team 5. Create public channels or let users create private groups --- ## User Management Accessible from the bottom-left menu (admin only). | Action | Description | |---|---| | Create user | Set name, email, temporary password, and role | | Bulk CSV import | Upload a CSV to create multiple users at once | | Reset password | User is forced to set a new password on next login | | Suspend | Blocks login; messages are preserved | | Activate | Re-enables a suspended account | | Delete | Removes account; messages remain attributed to user | | Change role | Promote member β†’ admin or demote admin β†’ member | ### CSV Import Format ```csv name,email,password,role John Doe,john@example.com,TempPass123,member Jane Smith,jane@example.com,Admin@456,admin ``` - `role` must be `member` or `admin` - `password` is optional β€” defaults to `TempPass@123` if omitted - All imported users must change their password on first login --- ## Group Types | | Public Channels | Private Groups | |---|---|---| | Who can create | Admin only | Any user | | Membership | All users (automatic) | Invite-only by owner | | Visible to admins | βœ… Yes | ❌ No | | Leave | ❌ Not allowed | βœ… Yes | | Rename | Admin only | Owner only | | Read-only mode | βœ… Optional | ❌ N/A | --- ## Data Persistence | Volume | Container path | Contents | |---|---|---| | `jama_db` | `/app/data` | SQLite database (`jama.db`) | | `jama_uploads` | `/app/uploads` | Avatars, logos, PWA icons, message images | Both volumes survive container restarts, image upgrades, and rollbacks. ### Backup ```bash # Backup database docker run --rm \ -v jama_db:/data \ -v $(pwd):/backup alpine \ tar czf /backup/jama_db_$(date +%Y%m%d).tar.gz -C /data . # Backup uploads docker run --rm \ -v jama_uploads:/data \ -v $(pwd):/backup alpine \ tar czf /backup/jama_uploads_$(date +%Y%m%d).tar.gz -C /data . ``` --- ## Versioning, Upgrades & Rollbacks jama uses a build-once, deploy-anywhere pattern: ``` Build machine Server ./build.sh 1.1.0 β†’ JAMA_VERSION=1.1.0 β†’ docker compose up -d ``` ### Upgrade ```bash # 1. Build new version ./build.sh 1.1.0 # 2. Update .env JAMA_VERSION=1.1.0 # 3. Redeploy (data volumes untouched) docker compose up -d ``` ### Rollback ```bash # 1. Set previous version in .env JAMA_VERSION=1.0.0 # 2. Redeploy docker compose up -d ``` --- ## PWA Installation HTTPS is required for the browser install prompt to appear. | Platform | How to install | |---|---| | Android (Chrome) | Tap the install banner, or Menu β†’ Add to Home Screen | | iOS (Safari) | Share β†’ Add to Home Screen | | Desktop Chrome/Edge | Click the install icon (βŠ•) in the address bar | After uploading a custom logo in Settings, the PWA shortcut icon updates automatically on the next app load. --- ## PW_RESET Flag Setting `PW_RESET=true` resets the default admin password to `ADMIN_PASS` on **every container restart**. Use only for emergency access recovery. When active, a ⚠️ warning banner is shown on the login page and in the Settings panel. **Always set `PW_RESET=false` and redeploy after recovering access.** --- ## Development ```bash # Start backend (port 3000) cd backend && npm install && npm run dev # Start frontend in a separate terminal (port 5173) cd frontend && npm install && npm run dev ``` The Vite dev server proxies all `/api` and `/socket.io` requests to the backend automatically. --- ## License MIT