2026-03-10 00:14:50 -04:00
2026-03-10 00:14:50 -04:00
2026-03-10 00:14:50 -04:00
2026-03-09 14:36:19 -04:00
2026-03-10 00:14:50 -04:00
2026-03-09 14:36:19 -04:00
2026-03-09 14:36:19 -04:00
2026-03-06 16:42:33 +00:00
2026-03-09 14:36:19 -04:00

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

Messaging

  • Real-time messaging — WebSocket-powered (Socket.io); messages appear instantly across all clients
  • Image attachments — Attach and send images; auto-compressed client-side before upload
  • Message replies — Quote and reply to any message with an inline preview
  • Emoji reactions — Quick-react with common emojis or open the full emoji picker; one reaction per user, replaceable
  • @Mentions — Type @ to search and tag users with autocomplete; mentioned users receive a notification
  • Link previews — URLs are automatically expanded with Open Graph metadata (title, image, site name)
  • Typing indicators — See when others are composing a message
  • Image lightbox — Tap any image to open it full-screen with pinch-to-zoom support

Channels & Groups

  • Public channels — Admin-created; all users are automatically added
  • Private groups / DMs — Any user can create; membership is invite-only by the owner
  • Read-only channels — Admin-configurable announcement-style channels; only admins can post
  • Support group — A private admin-only group that receives submissions from the login page contact form

Users & Profiles

  • Authentication — Email/password login with optional Remember Me (30-day session)
  • Forced password change — New users must change their password on first login
  • User profiles — Custom display name, avatar upload, About Me text
  • Profile popup — Click any user's avatar in chat to view their profile card
  • Admin badge — Admins display a role badge; can be hidden per-user in Profile settings

Notifications

  • In-app notifications — Mention alerts with toast notifications
  • Unread indicators — Private groups with new unread messages are highlighted and bolded in the sidebar
  • Web Push notifications — Badge and push notifications for mentions and new private messages when the app is backgrounded or closed (requires HTTPS)

Admin & Settings

  • User Manager — Create, suspend, activate, delete users; reset passwords; change roles
  • Bulk CSV import — Import multiple users at once from a CSV file
  • App branding — Customize app name, logo, New Chat icon, and Group Info icon via the Settings panel
  • Reset to defaults — One-click reset of all branding customizations
  • Version display — Current app version shown in the Settings panel

PWA

  • Installable — Install to home screen on mobile and desktop via the browser install prompt
  • Dynamic app icon — Uploaded logo is automatically resized to 192×192 and 512×512 and used as the PWA shortcut icon
  • Dynamic manifest — App name and icons in the PWA manifest update live when changed in Settings
  • Offline fallback — Basic offline support via service worker caching

Contact Form

  • Login page contact form — A "Contact Support" button on the login page opens a form (name, email, message, math captcha) that posts directly into the admin Support group

Tech Stack

Layer Technology
Backend Node.js, Express, Socket.io
Database SQLite (better-sqlite3)
Frontend React 18, Vite
Image processing sharp
Push notifications web-push (VAPID)
Containerization Docker, Docker Compose
Reverse proxy / SSL Caddy (recommended)

Requirements

  • Docker and Docker Compose v2
  • A domain name with DNS pointed at your server (required for HTTPS and Web Push notifications)
  • Ports 80 and 443 open on your server firewall (if using Caddy for SSL)

Building the Image

All builds use build.sh. No host Node.js installation is required — npm install and the Vite build run inside Docker.

# 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

git clone https://github.com/yourorg/jama.git
cd jama

2. Build the Docker image

./build.sh 1.0.0

3. Configure environment

cp .env.example .env
nano .env

At minimum, change ADMIN_EMAIL, ADMIN_PASS, and JWT_SECRET. See Environment Variables for all options.

4. Start the container

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)

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:

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

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

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

# 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

# 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

# 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

# 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

Description
A light weight self-contained, self-hosted, team based, messaging app.
Readme AGPL-3.0 4.1 MiB
Languages
JavaScript 94.1%
CSS 4.7%
HTML 0.4%
PLpgSQL 0.3%
Shell 0.3%
Other 0.2%