A local-first chat application built with different frameworks. It
- Has secure user logins
- Works offline
- Loads faster than server side rendering
- Can be deployed like any static website
- Does not need a server
To get user logins, sign up for Feathers Cloud Auth at app.feathers.cloud and create a new organization and application. Make sure to copy the application id (did:key:) and customize the theme 🤩
Then run the following in a terminal:
git clone git@github.com:featherscloud/chat.git
cd chat
npm install
npm run initWhen prompted, paste your application id and choose your framework. Make sure to visit the development server (default localhost:3000) that will be started to finalize initialization.
Once initialized, the development server for any framework can be started like this:
npm run dev:<framework>
npm run dev:react
npm run dev:svelteThe chat clients need to know where to connect to the sync server. Update the WebSocket URL in the client's .env file:
For Svelte Chat:
# svelte-chat/.env
VITE_CLOUD_APP_ID=did:key:z6Mkno3UyEMfCGLcKM1ZRp9eooyrKj4VLeathb77V7nthUUw
VITE_AUTOMERGE_URL=ws://localhost:3030For React Chat:
# react-chat/.env
VITE_CLOUD_APP_ID=did:key:z6Mkno3UyEMfCGLcKM1ZRp9eooyrKj4VLeathb77V7nthUUw
VITE_AUTOMERGE_URL=ws://localhost:3030VITE_CLOUD_APP_ID- Your Feathers Cloud Auth application IDVITE_AUTOMERGE_URL- WebSocket URL for the sync server- Local development:
ws://localhost:3030 - Production HTTPS:
wss://yourdomain.com:443 - Custom port:
ws://localhost:8080
- Local development:
To run both client and server for development:
# Terminal 1: Start sync server
npm run dev:sync
# Terminal 2: Start client (choose one)
npm run dev:svelte
# or
npm run dev:reactThen open http://localhost:3000 in your browser.
The sync server enables real-time synchronization between chat clients. It automatically detects SSL certificates and runs in HTTP or HTTPS mode.
For local development without SSL certificates:
npm run dev:sync
# or
cd sync-server && npm run devThis runs the server on HTTP port 3030.
For production deployment with SSL certificates:
npm run dev:sync
# or
cd sync-server && npm startThe server automatically detects SSL certificates and runs in HTTPS mode on port 443 (requires sudo).
The server automatically uses HTTPS if SSL certificates are found at:
/etc/letsencrypt/live/dweb.feathers.cloud/privkey.pem
/etc/letsencrypt/live/dweb.feathers.cloud/fullchain.pem
Use the CERT_PATH environment variable for custom certificate locations:
# Custom certificate directory
CERT_PATH="/path/to/your/certs" npm start
# Self-signed certificates for development
CERT_PATH="./certs" npm run dev
# Different domain certificates
CERT_PATH="/etc/letsencrypt/live/yourdomain.com" npm startprivkey.pem- Private key filefullchain.pem- Certificate chain file- Standard PEM format
- Works with any certificate provider (Let's Encrypt, commercial CAs, self-signed, etc.)
To force HTTPS mode even if certificates don't exist (useful for testing):
FORCE_HTTPS=true npm startLocal Development:
npm run dev:sync # HTTP on port 3030Production with Let's Encrypt:
sudo npm start # HTTPS on port 443 (auto-detected)Production with Custom Certificates:
CERT_PATH="/etc/ssl/private/mydomain" sudo npm startFor running a local-first demo on a Raspberry Pi with HTTPS on your local network:
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js (if not already installed)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Install git (if not already installed)
sudo apt install -y git# Clone the repository
git clone <your-repo-url>
cd chat
# Install dependencies
npm install
# Switch to sync branch
git checkout sync# Create certificate directory
sudo mkdir -p /etc/ssl/localcerts
# Find your Pi's IP address
hostname -I
# Generate self-signed certificate (replace YOUR_PI_IP with actual IP)
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/localcerts/privkey.pem \
-out /etc/ssl/localcerts/fullchain.pem \
-subj "/C=US/ST=Local/L=Local/O=Demo/CN=YOUR_PI_IP"
# Or for hostname-based access:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/localcerts/privkey.pem \
-out /etc/ssl/localcerts/fullchain.pem \
-subj "/C=US/ST=Local/L=Local/O=Demo/CN=raspberrypi.local"Update the client's .env file to point to your Pi:
For Svelte Chat:
# svelte-chat/.env
VITE_CLOUD_APP_ID=did:key:z6Mkno3UyEMfCGLcKM1ZRp9eooyrKj4VLeathb77V7nthUUw
VITE_AUTOMERGE_URL=wss://YOUR_PI_IP:3030
# or
VITE_AUTOMERGE_URL=wss://piradio.local:3030For React Chat:
# react-chat/.env
VITE_CLOUD_APP_ID=did:key:z6Mkno3UyEMfCGLcKM1ZRp9eooyrKj4VLeathb77V7nthUUw
VITE_AUTOMERGE_URL=wss://YOUR_PI_IP:3030
# or
VITE_AUTOMERGE_URL=wss://piradio.local:3030# Build the client
npm run build:svelte
# Start the sync server with custom certificate path
CERT_PATH="/etc/ssl/localcerts" PORT=3030 sudo -E npm run dev:sync- Find your Pi's IP:
hostname -I - Access via browser:
https://YOUR_PI_IP:3030orhttps://piradio.local:3030 - Accept security warning (since it's self-signed certificate)
- Share with demo participants: They'll also need to accept the security warning
Create a start-pi-demo.sh script for easy startup:
#!/bin/bash
export CERT_PATH="/etc/ssl/localcerts"
export PORT=3030
sudo -E npm run dev:syncMake it executable and run:
chmod +x start-pi-demo.sh
./start-pi-demo.shThis setup provides HTTPS for your local-first demo while keeping everything on your local network.
This section provides specific instructions for setting up the Svelte chat application and sync server on a Raspberry Pi with the hostname piradio.local.
- Raspberry Pi with Raspberry Pi OS installed
- Network connection (WiFi or Ethernet)
- SSH access or physical access to the Pi
# SSH into your Raspberry Pi or use the terminal
ssh pi@piradio.local
# Update the system
sudo apt update && sudo apt upgrade -y
# Install Node.js 18.x (recommended for this project)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Verify installation
node --version # Should show v18.x.x
npm --version # Should show v9.x.x
# Install git if not present
sudo apt install -y git
# Install build tools (needed for some npm packages)
sudo apt install -y build-essential python3# Set hostname to piradio
sudo hostnamectl set-hostname piradio
# Update hosts file
sudo nano /etc/hosts
# Change the line with old hostname to:
# 127.0.0.1 piradio
# Reboot to apply hostname changes
sudo reboot# Clone the repository
git clone https://github.com/featherscloud/chat.git
cd chat
# Install dependencies (this may take several minutes on Pi)
npm install
# Switch to the sync branch
git checkout sync
# Navigate to svelte-chat directory
cd svelte-chatCreate the environment configuration for piradio.local:
# Create/edit the .env file in svelte-chat directory
nano .envAdd the following content:
# Svelte Chat Environment Configuration for piradio.local
VITE_CLOUD_APP_ID=did:key:z6Mkno3UyEMfCGLcKM1ZRp9eooyrKj4VLeathb77V7nthUUw
VITE_AUTOMERGE_URL=ws://piradio.local:3030# From the svelte-chat directory
npm run build
# The built files will be in svelte-chat/dist/# Navigate back to project root
cd ..
# Navigate to sync-server directory
cd sync-server
# Install sync server dependencies if needed
npm installCreate convenient scripts to start the services:
# Create a startup script for the sync server
cd ~/chat
nano start-sync-server.shAdd this content:
#!/bin/bash
echo "Starting Sync Server for piradio.local..."
cd ~/chat/sync-server
export PORT=3030
npm run devCreate a script to serve the built chat application:
nano start-chat-client.shAdd this content:
#!/bin/bash
echo "Starting Chat Client Server for piradio.local..."
cd ~/chat/svelte-chat/dist
python3 -m http.server 3000Make scripts executable:
chmod +x start-sync-server.sh
chmod +x start-chat-client.sh# Terminal 1: Start the sync server
cd ~/chat
./start-sync-server.sh
# Terminal 2: Start the chat client server
./start-chat-client.shInstall PM2 for process management:
# Install PM2 globally
sudo npm install -g pm2
# Start the sync server with PM2
cd ~/chat/sync-server
pm2 start npm --name "sync-server" -- run dev
# Start the chat client server with PM2
cd ~/chat/svelte-chat/dist
pm2 start "python3 -m http.server 3000" --name "chat-client"
# Save PM2 process list
pm2 save
# Setup PM2 to start on boot
pm2 startup
# Follow the instructions provided by PM2
# Check status
pm2 list# Install ufw firewall
sudo apt install -y ufw
# Allow SSH
sudo ufw allow ssh
# Allow our application ports
sudo ufw allow 3000 # Chat client
sudo ufw allow 3030 # Sync server
# Enable firewall
sudo ufw enable
# Check status
sudo ufw statusEnsure your Raspberry Pi is accessible on the local network:
# Check your Pi's IP address
hostname -I
# Test local hostname resolution
ping piradio.localOnce both services are running:
-
From the same network: Open a web browser and go to:
http://piradio.local:3000(Chat client)- The chat will automatically connect to the sync server at
piradio.local:3030
-
From other devices: Make sure they're on the same network and can resolve
piradio.local
If piradio.local doesn't resolve:
- Use the Pi's IP address directly:
http://192.168.1.XXX:3000 - Update the .env file with the IP address instead of hostname
- Install Avahi for better hostname resolution:
sudo apt install avahi-daemon
If sync server connection fails:
- Check firewall settings:
sudo ufw status - Verify the sync server is running:
pm2 listor check terminal output - Test WebSocket connection manually from browser console
Performance optimization for Raspberry Pi:
# Increase swap space if needed (for building)
sudo dphys-swapfile swapoff
sudo nano /etc/dphys-swapfile
# Set CONF_SWAPSIZE=1024
sudo dphys-swapfile setup
sudo dphys-swapfile swaponTo make services start automatically when the Pi boots:
# Create systemd service for sync server
sudo nano /etc/systemd/system/piradio-sync.serviceAdd:
[Unit]
Description=PiRadio Chat Sync Server
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/chat/sync-server
Environment=PORT=3030
ExecStart=/usr/bin/npm run dev
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetCreate service for chat client:
sudo nano /etc/systemd/system/piradio-chat.serviceAdd:
[Unit]
Description=PiRadio Chat Client Server
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/chat/svelte-chat/dist
ExecStart=/usr/bin/python3 -m http.server 3000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetEnable and start services:
# Enable services to start on boot
sudo systemctl enable piradio-sync.service
sudo systemctl enable piradio-chat.service
# Start services immediately
sudo systemctl start piradio-sync.service
sudo systemctl start piradio-chat.service
# Check status
sudo systemctl status piradio-sync.service
sudo systemctl status piradio-chat.serviceYour Raspberry Pi is now configured to run the chat application at http://piradio.local:3000 with automatic startup and the sync server running in the background!
The chat application can be deployed like any static website. The build can be run with
npm run build:<framework>
npm run build:react
npm run build:svelteNote that in a CI environment, the VITE_CLOUD_APP_ID and VITE_AUTOMERGE_URL from the .env files need to be set.
Then the <framework>-chat/dist/ folder can be deployed like any static website.