The demonstration tests had incorrect assumptions about how Express trust proxy works in supertest (no real socket connection). Updated assertions to match actual behavior while preserving the test's purpose of showing that trust proxy: true extracts the correct client IP.
ExcaliDash
A self-hosted dashboard and organizer for Excalidraw with live collaboration features.
Screenshots
Table of Contents
Features
Upgrading
See release notes for a specific release.
Installation
Caution
NOT for production use. While attempts have been made at hardening (XSS/dompurify, CORS, rate-limiting, sanitization), they are inadequate for public deployment. Do not expose any ports.
Caution
ExcaliDash is in BETA. Please backup your data regularly (e.g. with cron).
Docker Hub (Recommended)
# Download docker-compose.prod.yml
curl -OL https://raw.githubusercontent.com/ZimengXiong/ExcaliDash/refs/heads/main/docker-compose.prod.yml
# Pull images
docker compose -f docker-compose.prod.yml pull
# Run container
docker compose -f docker-compose.prod.yml up -d
# Access the frontend at localhost:6767
Docker Build
# Clone the repository (recommended)
git clone git@github.com:ZimengXiong/ExcaliDash.git
# or, clone with HTTPS
# git clone https://github.com/ZimengXiong/ExcaliDash.git
docker compose build
docker compose up -d
# Access the frontend at localhost:6767
Reverse Proxy / Traefik Setups (Docker)
When running ExcaliDash behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly:
FRONTEND_URL(backend) must match the public URL that users hit (e.g.https://excalidash.example.com). This controls CORS and Socket.IO origin checks. Supports multiple comma-separated URLs for accessing from different addresses.BACKEND_URL(frontend) tells the Nginx container how to reach the backend from inside Docker/Kubernetes. Override it if your reverse proxy exposes the backend under a different hostname.
# docker-compose.yml example
backend:
environment:
# Single URL
- FRONTEND_URL=https://excalidash.example.com
# Or multiple URLs (comma-separated) for local + network access
# - FRONTEND_URL=http://localhost:6767,http://192.168.1.100:6767,http://nas.local:6767
frontend:
environment:
# For standard Docker Compose (default)
# - BACKEND_URL=backend:8000
# For Kubernetes, use the service DNS name:
- BACKEND_URL=excalidash-backend.default.svc.cluster.local:8000
Multi-Container / Kubernetes Deployments
When running multiple backend replicas (e.g., Kubernetes, Docker Swarm, or load-balanced containers), you must set the CSRF_SECRET environment variable to the same value across all instances.
# Generate a secure secret
openssl rand -base64 32
# docker-compose.yml or k8s deployment
backend:
environment:
- CSRF_SECRET=your-generated-secret-here
Without this, each container generates its own ephemeral CSRF secret, causing token validation failures when requests are routed to different replicas. Single-container deployments work without this setting.
Development
Clone the Repository
# Clone the repository (recommended)
git clone git@github.com:ZimengXiong/ExcaliDash.git
# or, clone with HTTPS
# git clone https://github.com/ZimengXiong/ExcaliDash.git
Frontend
cd ExcaliDash/frontend
npm install
# Copy environment file and customize if needed
cp .env.example .env
npm run dev
Backend
cd ExcaliDash/backend
npm install
# Copy environment file and customize if needed
cp .env.example .env
# Generate Prisma client and setup database
npx prisma generate
npx prisma db push
npm run dev
Project Structure
ExcaliDash/
├── backend/ # Node.js + Express + Prisma
│ ├── src/
│ │ └── index.ts # Main server file
│ ├── prisma/
│ │ ├── schema.prisma # Database schema
│ │ └── dev.db # SQLite database
│ └── package.json
├── frontend/ # React + TypeScript + Vite
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── pages/ # Page components
│ │ ├── hooks/ # Custom hooks
│ │ └── api/ # API client
│ └── package.json
└── README.md






