2026-04-10 13:18:59 -04:00
2026-03-27 15:04:52 -04:00
2026-04-10 13:18:59 -04:00
2026-03-10 19:24:06 -04:00
2026-03-23 19:34:13 -04:00
2026-04-10 13:18:59 -04:00
2026-04-07 11:29:21 -04:00
2026-04-09 16:25:19 +00:00
2026-04-10 13:18:59 -04:00
2026-04-10 13:18:59 -04:00
2026-03-09 14:36:19 -04:00
2026-04-10 13:18:59 -04:00
2026-04-07 11:29:21 -04:00
2026-04-10 13:18:59 -04:00
2026-04-10 13:18:59 -04:00
2026-03-13 16:59:45 -04:00
2026-03-30 15:45:40 -04:00
2026-03-06 16:42:33 +00:00
2026-04-10 13:18:59 -04:00
2026-03-30 15:45:40 -04:00

<<<<<<< HEAD

RosterChirp

A modern, self-hosted team messaging Progressive Web App (PWA) built for small to medium teams. RosterChirp runs via Docker Compose with PostgreSQL and supports both single-tenant (self-hosted) and multi-tenant (hosted) deployments.

Development was vibe-coded using Claude.ai.

Current version: 0.13.1

rosterchirp

A modern, self-hosted team messaging Progressive Web App (PWA) built for small to medium teams. rosterchirp runs entirely in a single Docker container with no external database dependencies — all data is stored locally using SQLite.

1af039ab0a


Features

Messaging

  • Real-time messaging — WebSocket-powered (Socket.io); messages appear instantly across all clients
  • Image attachments — Attach and send images via the + menu; auto-compressed client-side before upload
  • Camera capture — Take a photo directly from the + menu on mobile devices
  • Emoji picker — Send standalone emoji messages at large size via the + menu
  • 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 using @[Display Name] syntax; autocomplete scoped to group members; 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
  • Message grouping — Consecutive messages from the same user are visually grouped; avatar and name shown only on first message
  • Last message preview — Sidebar shows "You:" prefix when the current user sent the last message

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
  • Direct messages — One-to-one private conversations; sidebar title always shows the other user's real name
  • Duplicate group prevention — Creating a private group with the same member set as an existing group redirects to the existing group automatically
  • 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
  • Custom group names — Each user can set a personal display name for any group, visible only to them
  • Group Messages — Managed private groups (created and controlled by admins via Group Manager) appear in a separate "Private Group Messages" section in the sidebar

Schedule

  • Team schedule — Full calendar view for creating and managing team events (Team plan)
  • Desktop & mobile views — Dedicated layout for each; desktop shows a full monthly grid, mobile shows a scrollable event list
  • Event types — Colour-coded event categories (configurable by admins)
  • Recurring events — Create daily, weekly, or custom-interval recurring events; only future occurrences are shown
  • Availability — Users can mark their availability per event
  • Keyword filter — Search events by keyword with word-boundary matching; quoted terms match exactly
  • Type filter — Filter events by event type across the current month (including past events, shown greyed)
  • Past event protection — New events cannot be created with a start date/time in the past

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
  • Online presence — Real-time online/offline status tracked per user
  • Last seen — Users' last online timestamp updated on disconnect

Notifications

  • In-app notifications — Mention alerts with toast notifications
  • Unread indicators — Private groups with new unread messages are highlighted and bolded in the sidebar
  • Push notifications — Firebase Cloud Messaging (FCM) push notifications for mentions and new private messages when the app is backgrounded or closed (Android PWA; requires HTTPS and Firebase setup)

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
  • Group Manager — Create and manage private groups and their membership centrally (Team plan)
  • App branding — Customize app name, logo, and icons via the Settings panel (Brand+ plan)
  • Reset to defaults — One-click reset of all branding customizations
  • Version display — Current app version shown in the Settings panel
  • Default user password — Configurable via USER_PASS env var; shown live in User Manager
  • Feature flags — Plan-gated features (branding, group manager, schedule manager) controlled via settings

User Deletion

  • Deleting a user scrubs their email, name, and avatar immediately
  • Their messages are marked deleted (content removed); direct message threads become read-only
  • Group memberships, sessions, push subscriptions, and notifications are purged
  • Suspended users retain all data and can be re-activated

Help & Onboarding

  • Getting Started modal — Appears automatically on first login; users can dismiss permanently with "Do not show again"
  • Help menu item — Always accessible from the user menu regardless of dismissed state
  • Editable help contentdata/help.md is edited before build and baked into the image at build time

PWA

  • Installable — Install to home screen on mobile and desktop via the browser install prompt
  • Adaptive icons — Separate any and maskable icon entries; maskable icons sized for Android circular crop
  • Dynamic app icon — Uploaded logo is automatically resized and used as the PWA shortcut icon
  • Dynamic manifest — App name and icons update live when changed in Settings
  • Pull-to-refresh disabled — In PWA standalone mode, pull-to-refresh is disabled to prevent a layout shift bug on mobile

Contact Form

  • Login page contact form — A "Contact Support" button on the login page opens a form that posts directly into the admin Support group

Deployment Modes

Mode Description
selfhost Single tenant — one team, one database schema. Default.
host Multi-tenant — one schema per tenant, provisioned via subdomains. Requires APP_DOMAIN, HOST_SLUG, and HOST_ADMIN_KEY.

Set APP_TYPE=selfhost or APP_TYPE=host in .env.


Plans & Feature Flags

Plan Features
RosterChirp-Chat Messaging, channels, DMs, profiles, push notifications
RosterChirp-Brand Everything in Chat + custom branding (logo, app name, icons)
RosterChirp-Team Everything in Brand + Group Manager + Schedule Manager

Feature flags are stored in the database settings table and can be toggled by the admin.


Tech Stack

Layer Technology
Backend Node.js, Express, Socket.io
Database PostgreSQL 16 (via pg)
Frontend React 18, Vite
Push notifications Firebase Cloud Messaging (FCM)
Image processing sharp
Containerization Docker, Docker Compose v2
Reverse proxy / SSL Caddy (recommended)

Requirements

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

Building the Image

All builds use build.sh. No host Node.js installation is required.

Tip: Edit data/help.md before running build.sh to customise the Getting Started help content baked into the image.

# Build and tag as :latest only
./build.sh

# Build and tag as a specific version
./build.sh 0.13.1

Installation

1. Clone the repository

<<<<<<< HEAD
git clone https://your-git/youruser/rosterchirp.git
=======
git clone https://your-gitea/youruser/rosterchirp.git
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28
cd rosterchirp

2. Build the Docker image

./build.sh 0.13.1

3. Configure environment

cp .env.example .env
nano .env

At minimum, set ADMIN_EMAIL, ADMIN_PASS, ADMIN_NAME, JWT_SECRET, and DB_PASSWORD.

4. Start the services

docker compose up -d
docker compose logs -f rosterchirp

5. Log in

Open http://your-server:3000, log in with your ADMIN_EMAIL and ADMIN_PASS, and change your password when prompted.


HTTPS & SSL

<<<<<<< HEAD RosterChirp does not manage SSL itself. Use Caddy as a reverse proxy.

rosterchirp does not manage SSL itself. Use Caddy as a reverse proxy.

1af039ab0a

Caddyfile

chat.yourdomain.com {
    reverse_proxy rosterchirp:3000
}

docker-compose.yaml (with Caddy)

services:
  rosterchirp:
<<<<<<< HEAD
    image: rosterchirp:${ROSTERCHIRP_VERSION:-latest}
=======
    image: rosterchirp:${rosterchirp_VERSION:-latest}
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28
    container_name: rosterchirp
    restart: unless-stopped
    expose:
      - "3000"
    environment:
      - NODE_ENV=production
      - APP_TYPE=${APP_TYPE:-selfhost}
      - ADMIN_NAME=${ADMIN_NAME:-Admin User}
      - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@rosterchirp.local}
      - ADMIN_PASS=${ADMIN_PASS:-Admin@1234}
      - USER_PASS=${USER_PASS:-user@1234}
      - ADMPW_RESET=${ADMPW_RESET:-false}
      - JWT_SECRET=${JWT_SECRET:-changeme}
<<<<<<< HEAD
      - APP_NAME=${APP_NAME:-RosterChirp}
      - DEFCHAT_NAME=${DEFCHAT_NAME:-General Chat}
      - DB_HOST=db
      - DB_NAME=${DB_NAME:-rosterchirp}
      - DB_USER=${DB_USER:-rosterchirp}
      - DB_PASSWORD=${DB_PASSWORD}
      - ROSTERCHIRP_VERSION=${ROSTERCHIRP_VERSION:-latest}
    volumes:
      - rosterchirp_uploads:/app/uploads
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    container_name: rosterchirp_db
    restart: unless-stopped
    environment:
      - POSTGRES_DB=${DB_NAME:-rosterchirp}
      - POSTGRES_USER=${DB_USER:-rosterchirp}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - rosterchirp_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-rosterchirp}"]
      interval: 10s
      timeout: 5s
      retries: 5
=======
      - APP_NAME=${APP_NAME:-rosterchirp}
      - rosterchirp_VERSION=${rosterchirp_VERSION:-latest}
    volumes:
      - rosterchirp_db:/app/data
      - rosterchirp_uploads:/app/uploads
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28

  caddy:
    image: caddy:alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_certs:/config
    depends_on:
      - rosterchirp

volumes:
  rosterchirp_db:
  rosterchirp_uploads:
  caddy_data:
  caddy_certs:

Environment Variables

Variable Default Description
<<<<<<< HEAD
APP_TYPE selfhost Deployment mode: selfhost (single tenant) or host (multi-tenant)
ROSTERCHIRP_VERSION latest Docker image tag to run

======= | rosterchirp_VERSION | latest | Docker image tag to run |

1af039ab0a | TZ | UTC | Container timezone (e.g. America/Toronto) | | ADMIN_NAME | Admin User | Display name of the default admin account | | ADMIN_EMAIL | admin@rosterchirp.local | Login email for the default admin account | | ADMIN_PASS | Admin@1234 | Initial password for the default admin account | | USER_PASS | user@1234 | Default temporary password for bulk-imported users when no password is specified in CSV | | ADMPW_RESET | false | If true, resets the admin password to ADMIN_PASS on every restart. Emergency recovery only. | | JWT_SECRET | (insecure default) | Secret used to sign auth tokens. Must be changed in production. | <<<<<<< HEAD | APP_NAME | RosterChirp | Initial application name (can also be changed in Settings UI) | | DEFCHAT_NAME | General Chat | Name of the default public channel created on first run | | DB_HOST | db | PostgreSQL hostname | | DB_NAME | rosterchirp | PostgreSQL database name | | DB_USER | rosterchirp | PostgreSQL username | | DB_PASSWORD | (required) | PostgreSQL password. Avoid ! — shell interpolation issue with Docker Compose. | | APP_DOMAIN | — | Base domain for multi-tenant host mode (e.g. example.com) | | HOST_SLUG | — | Subdomain slug for the host control panel (e.g. chathostchathost.example.com) | | HOST_ADMIN_KEY | — | Secret key for the host control plane API | ======= | PORT | 3000 | Host port to bind (without Caddy) | | APP_NAME | rosterchirp | Initial application name (can also be changed in Settings UI) | | DEFCHAT_NAME | General Chat | Name of the default public group created on first run | 1af039ab0a

Firebase Push Notification Variables (optional)

Variable Description
FIREBASE_API_KEY Firebase web app API key
FIREBASE_PROJECT_ID Firebase project ID
FIREBASE_MESSAGING_SENDER_ID Firebase messaging sender ID
FIREBASE_APP_ID Firebase web app ID
FIREBASE_VAPID_KEY Web Push certificate public key (from Firebase Cloud Messaging tab)
FIREBASE_SERVICE_ACCOUNT Full service account JSON, stringified (remove all newlines)

ADMIN_EMAIL and ADMIN_PASS are only used on the first run. Once the database is seeded they are ignored — unless ADMPW_RESET=true.

Example .env

<<<<<<< HEAD
ROSTERCHIRP_VERSION=0.13.1
APP_TYPE=selfhost
=======
rosterchirp_VERSION=1.0.0
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28
TZ=America/Toronto

ADMIN_NAME=Your Name
ADMIN_EMAIL=admin@yourdomain.com
ADMIN_PASS=ChangeThisNow!

USER_PASS=Welcome@123
ADMPW_RESET=false

JWT_SECRET=replace-this-with-a-long-random-string-at-least-32-chars

<<<<<<< HEAD
APP_NAME=RosterChirp
=======
PORT=3000
APP_NAME=rosterchirp
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28
DEFCHAT_NAME=General Chat

DB_NAME=rosterchirp
DB_USER=rosterchirp
DB_PASSWORD=a-strong-db-password

First Login & Setup Checklist

  1. Log in with ADMIN_EMAIL / ADMIN_PASS
  2. Change your password when prompted
  3. Read the Getting Started guide that appears on first login
  4. Open ⚙️ Settings → upload a logo and set the app name
  5. Open 👥 User Manager to create accounts for your team

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 Scrubs account data; messages are removed; threads become read-only
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
  • password is optional — defaults to the value of USER_PASS if omitted
  • All imported users must change their password on first login

Group Types

Public Channels Private Groups Direct Messages
Who can create Admin only Any user Any user
Membership All users (automatic) Invite-only by owner Two users only
Sidebar title Group name Group name (customisable per user) Other user's real name
Rename Admin only Owner only Not allowed
Read-only mode Optional N/A N/A
Duplicate prevention N/A Redirects to existing Redirects to existing
Managed (Group Manager) Optional

@Mention Scoping

  • Public channels — all active users appear in the @ autocomplete
  • Private groups — only members of that group appear
  • Direct messages — only the other participant appears

Custom Group Names

Any user can set a personal display name for any group:

  1. Open the group and tap the ⓘ info icon
  2. Enter a name under Your custom name and tap Save
  3. The custom name appears in your sidebar and chat header only
  4. Message Info shows: Custom Name (Owner's Name)
  5. Clear the field and tap Save to revert to the owner's name

Schedule

The Schedule page (Team plan) provides a full team calendar:

  • Desktop view — Monthly grid with event cards per day
  • Mobile view — Scrollable event list with a date picker
  • Event types — Colour-coded categories created by admins
  • Recurring events — Set daily, weekly, or custom recurrence intervals
  • Availability — Members can mark availability per event
  • Keyword search — Unquoted terms match word prefixes; quoted terms match whole words exactly
  • Type filter — Filter by event type across the full current month

Push Notifications

RosterChirp uses Firebase Cloud Messaging (FCM) for push notifications. HTTPS is required.

Setup

  1. Create a Firebase project at console.firebase.google.com
  2. Add a Web app → copy the config values into .env
  3. Go to Project Settings → Cloud Messaging → Web Push certificates → generate a key pair → copy the public key as FIREBASE_VAPID_KEY
  4. Go to Project Settings → Service accounts → Generate new private key → download the JSON → stringify it (remove all newlines) → set as FIREBASE_SERVICE_ACCOUNT

Push notifications are sent for:

  • New messages in private groups (to all members except the sender)
  • New messages in public channels (to all subscribers except the sender)
  • Image messages show as 📷 Image

Help Content

The Getting Started guide is sourced from data/help.md. Edit before running build.sh — it is baked into the image at build time.

nano data/help.md
./build.sh 0.13.1

Users can access the guide at any time via User menu → Help.


Data Persistence

Volume Container path Contents
<<<<<<< HEAD
rosterchirp_db /var/lib/postgresql/data PostgreSQL data directory

======= | rosterchirp_db | /app/data | SQLite database (rosterchirp.db), help.md |

1af039ab0a | rosterchirp_uploads | /app/uploads | Avatars, logos, PWA icons, message images |

Backup

# Backup database
<<<<<<< HEAD
docker compose exec db pg_dump -U rosterchirp rosterchirp | gzip > rosterchirp_db_$(date +%Y%m%d).sql.gz

# Restore database
gunzip -c rosterchirp_db_20240101.sql.gz | docker compose exec -T db psql -U rosterchirp rosterchirp
=======
docker run --rm \
  -v rosterchirp_db:/data \
  -v $(pwd):/backup alpine \
  tar czf /backup/rosterchirp_db_$(date +%Y%m%d).tar.gz -C /data .
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28

# Backup uploads
docker run --rm \
  -v rosterchirp_uploads:/data \
  -v $(pwd):/backup alpine \
  tar czf /backup/rosterchirp_uploads_$(date +%Y%m%d).tar.gz -C /data .

Upgrades & Rollbacks

Database migrations run automatically on startup. There is no manual migration step.

# Upgrade
<<<<<<< HEAD
./build.sh 0.13.1
# Set ROSTERCHIRP_VERSION=0.13.1 in .env
docker compose up -d

# Rollback
# Set ROSTERCHIRP_VERSION=0.12.x in .env
=======
./build.sh 1.1.0
# Set rosterchirp_VERSION=1.1.0 in .env
docker compose up -d

# Rollback
# Set rosterchirp_VERSION=1.0.0 in .env
>>>>>>> 1af039ab0a72560aace9b284d541f5201c920e28
docker compose up -d

Data volumes are untouched in both cases.


PWA Icons

File Purpose
icon-192.png / icon-512.png Standard icons — desktop PWA shortcuts (purpose: any)
icon-192-maskable.png / icon-512-maskable.png Adaptive icons — Android home screen (purpose: maskable); logo at 75% scale on solid background

ADMPW_RESET Flag

Resets the admin account password to ADMIN_PASS on every container restart. Use only when the admin password has been lost.

# Enable for recovery
ADMPW_RESET=true

# Disable after recovering access
ADMPW_RESET=false

A ⚠️ warning banner is shown on the login page and in Settings when active.


Development

# Backend (port 3000)
cd backend && npm install && npm run dev

# Frontend (port 5173)
cd frontend && npm install && npm run dev

The Vite dev server proxies all /api and /socket.io requests to the backend automatically. You will need a running PostgreSQL instance and a .env file in the project root.


License

Proprietary — all rights reserved.

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