MCPcopy Index your code
hub / github.com/ZimengXiong/ExcaliDash

github.com/ZimengXiong/ExcaliDash @v0.5.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.5.1 ↗ · + Follow
779 symbols 1,999 edges 172 files 1 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

ExcaliDash Logo

ExcaliDash

License PRs Welcome Docker

A self-hosted dashboard and organizer for Excalidraw with live collaboration features.

Table of Contents

Features

Persistent storage for all your drawings

Real time collaboration

Version history and restore

Automatically retain recent drawing snapshots, preview past versions from the editor, and restore a previous state when needed.

(Optional) Multi User Authentication, OIDC Support

Sign in with OIDC

Migration from v0.3

Admin Bootstrap

Admin Dashboard

Scoped internal & external sharing

Search your drawings

Drag and drop drawings into collections

Export/import your drawings for backup

Excalidash uses a non-proprietary archival format that stores your drawings in plain .excalidraw format

Upgrading

See release notes for a specific release.

ExcaliDash includes an in-app update notifier that checks GitHub Releases. If your deployment must not make outbound network calls, disable it on the backend:

UPDATE_CHECK_OUTBOUND=false

Docker Hub Upgrades

If you deployed using docker-compose.prod.yml (Docker Hub images), upgrade by pulling the latest images and recreating containers:

docker compose -f docker-compose.prod.yml pull && \
  docker compose -f docker-compose.prod.yml up -d

If you prefer a clean stop/start (more downtime, but simpler), you can do:

docker compose -f docker-compose.prod.yml down && \
  docker compose -f docker-compose.prod.yml pull && \
  docker compose -f docker-compose.prod.yml up -d

Notes:

  • Don’t add -v to down unless you intend to delete the persistent backend volume (your SQLite DB + secrets).
  • Only add --remove-orphans if you previously ran a different Compose file for the same project name and need to remove old/renamed services.

Installation

[!CAUTION] This is a BETA deployment and production-readiness depends on deployment controls: use TLS, trusted reverse proxy, fixed secrets, backups, and endpoint rate limits.

[!CAUTION] ExcaliDash is in BETA. Please backup your data regularly.

Quickstart

Prereqs: Docker + Docker Compose v2.

Docker Hub (Recommended)

Docker Hub (Recommended)

# Download docker-compose.prod.yml
curl -OL https://raw.githubusercontent.com/ZimengXiong/ExcaliDash/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

For single-container deployments, JWT_SECRET can be omitted and will be auto-generated and persisted in the backend volume on first start. For portability and most production deployments, set a fixed JWT_SECRET explicitly.

By default, the provided Compose files set TRUST_PROXY=false for safer setup. Only set TRUST_PROXY to a positive hop count (for example, 1) when requests always pass through a trusted reverse proxy that correctly sets forwarded headers.

Docker Build

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

Advanced

Reverse Proxy / Traefik

When running ExcaliDash behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly:

Variable Purpose
FRONTEND_URL Backend allowed origin(s). Must match the public URL users access (for example https://excalidash.example.com). Supports comma-separated values for multiple addresses.
TRUST_PROXY Set to 1 when traffic passes through one trusted reverse-proxy hop (for example frontend nginx -> backend) and headers are sanitized.
BACKEND_URL Frontend container-to-backend target used by Nginx. Override when backend host differs from default service DNS/host.
ENFORCE_HTTPS_REDIRECT When FRONTEND_URL uses https://, the backend automatically redirects plain-HTTP requests to HTTPS. Set to false if your outer gateway already enforces HTTPS and you want to disable the built-in redirect (avoids redirect loops when X-Forwarded-Proto is not forwarded). Default: true.
# docker-compose.yml example
backend:
  environment:
    # Single URL
    - FRONTEND_URL=https://excalidash.example.com
    # Trust exactly one reverse-proxy hop
    - TRUST_PROXY=1
    # Or multiple URLs (comma-separated) for local + network access
    # - FRONTEND_URL=http://localhost:6767,http://192.168.1.100:6767,http://nas.local:6767
    # If your outer gateway enforces HTTPS and X-Forwarded-Proto is not forwarded,
    # disable the built-in redirect to prevent redirect loops:
    # - ENFORCE_HTTPS_REDIRECT=false
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

Scaling / HA (Current Limitations)

ExcaliDash currently supports running one backend instance.

Why:

Area Limitation
Database The backend uses SQLite by default. For production deployments requiring high availability, use PostgreSQL (DATABASE_PROVIDER=postgresql). SQLite still works for single-instance deployments but is not recommended for multi-replica setups.
Collaboration Real-time presence state is tracked in-memory in the backend process, so multiple replicas will fragment presence/collaboration unless a shared Socket.IO adapter is added.

Recommended deployment pattern:

Component Guidance
Backend 1 replica, persistent volume, regular backups.
Frontend 1 replica is simplest; scaling is generally fine since it is stateless.

Auth, Onboarding, and First Admin Setup

ExcaliDash supports local login and OIDC, and includes a one-time first-admin bootstrap key to protect initial setup/migration flows.

Auth modes:

AUTH_MODE Behavior
local (default) Native email/password login only.
hybrid Native login plus OIDC login.
oidc_enforced OIDC-only login (/auth/register and /auth/login disabled).

If you upgrade and see an onboarding/setup flow, follow the UI. For emergency-only operator access, you can temporarily bypass the onboarding gate:

DISABLE_ONBOARDING_GATE=true docker compose -f docker-compose.prod.yml up -d

One-time first-admin bootstrap setup code (local auth only):

What Notes
When required Auth enabled and no active users (fresh install or certain migrations).
Where to find it Backend logs: [BOOTSTRAP SETUP] One-time admin setup code ....
Behavior Single-use; if you enter an invalid/expired code, check logs for the refreshed code.

Find the current code in logs:

docker compose -f docker-compose.prod.yml logs backend --tail=200 | grep "BOOTSTRAP SETUP"

OIDC configuration (for hybrid / oidc_enforced) requires these backend env vars:

backend:
  environment:
    - AUTH_MODE=oidc_enforced
    - OIDC_PROVIDER_NAME=Authentik
    - OIDC_ISSUER_URL=https://auth.example.com/application/o/excalidash/
    # Optional split-horizon setup when backend reaches IdP via internal DNS.
    # Keep OIDC_ISSUER_URL browser-routable; set OIDC_DISCOVERY_URL for backend-only access.
    # - OIDC_DISCOVERY_URL=http://auth-internal:9000/application/o/excalidash/
    - OIDC_CLIENT_ID=your-client-id
    # Optional for public clients; required for confidential clients
    # - OIDC_CLIENT_SECRET=your-client-secret
    # Optional token endpoint auth override (useful for some IdPs/HS setups)
    # - OIDC_TOKEN_ENDPOINT_AUTH_METHOD=client_secret_post
    # Optional override when your IdP client is configured for a non-default ID token alg
    # - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=HS256
    - OIDC_REDIRECT_URI=https://excalidash.example.com/api/auth/oidc/callback
    - OIDC_SCOPES=openid profile email
    # Optional: path to groups/roles claim in ID token/user claims (supports dot path)
    - OIDC_GROUPS_CLAIM=groups
    # Optional: comma-separated group names that should be ADMIN in ExcaliDash
    - OIDC_ADMIN_GROUPS=excalidash-admins,platform-admins

Quick preflight check (recommended before starting backend):

cd backend
npm run oidc:doctor

Provider-specific env templates for existing IdPs:

  • backend/.env.oidc.keycloak.example
  • backend/.env.oidc.authentik.example

Copy one to backend/.env, update issuer/client/redirect values, then run npm run oidc:doctor.

Notes:

Topic Notes
OIDC-only (oidc_enforced) You typically do not use local bootstrap admin registration; first admin can be created through your IdP depending on config.
Reverse proxy Set FRONTEND_URL and TRUST_PROXY correctly or auth + websockets may fail.
ID token algorithm ExcaliDash defaults to RS256. If your IdP client is explicitly configured for another signed ID-token algorithm such as HS256, set OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG to match that exact client setting. none is not allowed, and HS* requires OIDC_CLIENT_SECRET.
Keycloak issuer format Use realm issuer URL: https://<keycloak-host>/realms/<realm>.
Authentik issuer format Use provider issuer URL: https://<authentik-host>/application/o/<provider-slug>/.
Authentik email_verified If Authentik does not emit email_verified=true, either add the scope mapping or set OIDC_REQUIRE_EMAIL_VERIFIED=false.
Redirect URI Must be exact callback: https://<excalidash-host>/api/auth/oidc/callback.
Split-horizon IdP networking Set OIDC_ISSUER_URL to the browser-reachable issuer and optionally OIDC_DISCOVERY_URL to a backend-reachable internal URL.
OIDC admin mapping If OIDC_ADMIN_GROUPS is set, admin role is reconciled on each authenticated request for OIDC users: users in those groups are promoted to ADMIN, users not in those groups are demoted to USER.
Legacy sessions

Extension points exported contracts — how you extend this code

DrawingSummary (Interface)
(no doc)
frontend/src/types/index.ts
SecurityConfig (Interface)
(no doc)
backend/src/security.ts
DrawingRecord (Interface)
(no doc)
e2e/tests/helpers/api.ts
Drawing (Interface)
(no doc)
frontend/src/types/index.ts
Config (Interface)
(no doc)
backend/src/config.ts
CollectionRecord (Interface)
(no doc)
e2e/tests/helpers/api.ts
Collection (Interface)
(no doc)
frontend/src/types/index.ts
OidcConfig (Interface)
(no doc)
backend/src/config.ts

Core symbols most depended-on inside this repo

createDrawing
called by 54
e2e/tests/helpers/api.ts
parseJsonField
called by 29
backend/src/index.ts
logAuditEvent
called by 27
backend/src/utils/audit.ts
asyncHandler
called by 27
backend/src/middleware/errorHandler.ts
log
called by 27
scripts/version-manager.js
ensureAuthEnabled
called by 26
backend/src/auth.ts
sanitizeDrawingData
called by 25
backend/src/security.ts
sanitizeText
called by 23
backend/src/security.ts

Shape

Function 716
Interface 37
Class 14
Method 12

Languages

TypeScript100%

Modules by API surface

frontend/src/api/index.ts49 symbols
frontend/src/pages/Editor.tsx31 symbols
frontend/src/pages/Dashboard.tsx29 symbols
backend/src/auth.ts29 symbols
backend/src/auth/oidcRoutes.ts25 symbols
backend/src/routes/importExport/shared.ts23 symbols
backend/src/security.ts20 symbols
e2e/tests/helpers/api.ts18 symbols
backend/src/index.ts18 symbols
frontend/src/pages/Admin.tsx17 symbols
frontend/src/components/UpdateBanner.tsx15 symbols
backend/src/middleware/auth.ts15 symbols

Datastores touched

excalidashDatabase · 1 repos
(mysql)Database · 1 repos
excalidashDatabase · 1 repos

For agents

$ claude mcp add ExcaliDash \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page