This file helps two kinds of agents work on ExcaliDash.
ExcaliDash is a self-hosted dashboard and organizer for Excalidraw drawings, with persistent storage and live collaboration. Core user-facing features include organizing drawings into collections, search, export/import for backup, and configurable authentication (local and optional OIDC).
- Official supported deployment flow (production):
docker-compose.prod.ymlcurl -OL https://raw.githubusercontent.com/ZimengXiong/ExcaliDash/main/docker-compose.prod.ymldocker compose -f docker-compose.prod.yml pulldocker compose -f docker-compose.prod.yml up -d
- Production hardening quick checklist:
- Pin images to a specific release tag (or digest) instead of
:latestfor reproducible upgrades/rollbacks. - Stable tags are published as
zimengxiong/excalidash-backend:<VERSION>andzimengxiong/excalidash-frontend:<VERSION>(and also:latest). - Pre-release tags are published as
:devand:<VERSION>-dev(and do not update:latest). - Set fixed
JWT_SECRETandCSRF_SECRETfor portability and multi-instance/redeploy scenarios. - Set
FRONTEND_URLto your public URL(s) and keepTRUST_PROXY=falseunless you are behind a trusted proxy hop. - Ensure the backend volume is backed up (SQLite DB + persisted secrets).
- Pin images to a specific release tag (or digest) instead of
- Re-check production health:
docker compose -f docker-compose.prod.yml psdocker compose -f docker-compose.prod.yml logs backend --tail=200docker compose -f docker-compose.prod.yml logs -f backend
- Upgrade production:
docker compose -f docker-compose.prod.yml down(if needed)docker compose -f docker-compose.prod.yml pulldocker compose -f docker-compose.prod.yml up -d
- Check bootstrap/setup signal if onboarding blocks access:
docker compose -f docker-compose.prod.yml logs backend --tail=200 | grep "BOOTSTRAP SETUP"
- For local helper debugging with compose (repo checkout):
docker compose up -d(usesdocker-compose.yml; use-f docker-compose.prod.ymlif you deployed from Docker Hub images) - Must-read first for common user issues:
README.md
Pin to a stable release (recommended for production):
services:
backend:
image: zimengxiong/excalidash-backend:0.4.18
frontend:
image: zimengxiong/excalidash-frontend:0.4.18Switch to pre-release images (rolling :dev tag):
services:
backend:
image: zimengxiong/excalidash-backend:dev
frontend:
image: zimengxiong/excalidash-frontend:devSwitch to a specific pre-release build (pinned :<VERSION>-dev tag):
services:
backend:
image: zimengxiong/excalidash-backend:0.4.18-dev
frontend:
image: zimengxiong/excalidash-frontend:0.4.18-devSwitch to a one-off custom dev tag (published by make dev-release NAME=...):
services:
backend:
image: zimengxiong/excalidash-backend:0.4.18-dev-issue38
frontend:
image: zimengxiong/excalidash-frontend:0.4.18-dev-issue38- Architecture map: backend (
backend/src), frontend (frontend/src) - Config source of truth:
backend/src/config.ts(env + validation) - Entry server init:
backend/src/index.ts - API client:
frontend/src/api/index.ts - Realtime/editor behavior:
frontend/src/pages/Editor.tsx - Deployment flow:
docker-compose*.yml,backend/Dockerfile,frontend/Dockerfile, entrypoint scripts - Build/dev commands:
make build,npm test,make test-all,make test-e2e - Development contributor flow:
make install→make dev- stop with
make dev-stop make dev-backend/make dev-frontendif you only need one side
Prioritize operational steps, then point to the exact commands or files.
For setup and troubleshooting, start here.
- Goal check: confirm whether the user needs local dev, Docker Compose from Docker Hub images, locally built Docker, or E2E.
- Check whether the problem is already known:
- If the agent has GitHub access, search issues for the exact error text/symptom and link the closest match.
- If the agent cannot browse, ask the user to search and share the issue link (or paste issue text).
- Note:
gitdoes not provide a native way to browse GitHub Issues. Use the web UI or GitHub CLI (gh). - Issue tracker:
https://github.com/ZimengXiong/ExcaliDash/issues - Search tip:
https://github.com/ZimengXiong/ExcaliDash/issues?q=is%3Aissue+<paste+error+snippet> - If
ghis available:- Set repo:
gh repo set-default ZimengXiong/ExcaliDash - Search/list:
gh issue list --search "<error snippet>" - View:
gh issue view <number> -w - Create (guided):
gh issue create - If missing, prompt the user to install
gh(recommended) or use the web UI.
- Set repo:
- Use the
README.mdande2e/README.mdas the primary operator references. - If the question is about one error, find the nearest environment or script path before proposing code changes.
- Encourage filing an issue when needed (new bug / unclear docs / missing troubleshooting):
- Ask the user to include:
- Their deployment mode: Docker Hub compose (
docker-compose.prod.yml) vs repo compose (docker-compose.yml) vsmakevs directnpm - App version tag(s) and image tags (or
VERSIONif built from source) - OS/arch (for example
linux/arm64) and whether a reverse proxy is used - Sanitized relevant env vars:
AUTH_MODE,TRUST_PROXY,FRONTEND_URL(do not share secrets) - Logs:
docker compose ... logs backend --tail=200and the exact error text - Repro steps and expected vs actual behavior
- Their deployment mode: Docker Hub compose (
- If the agent can reproduce or pinpoint the root cause, summarize findings and link relevant files/lines.
- Ask the user to include:
- If startup is blocked, collect these values from the user first:
- Are they using Docker Hub compose (
docker-compose.prod.yml), repo compose (docker-compose.yml),make, or directnpm? - If using Docker Hub compose:
docker compose -f docker-compose.prod.yml psanddocker compose -f docker-compose.prod.yml logs backend --tail=200 - If using repo compose:
docker compose psanddocker compose logs backend --tail=200 - If using a git clone:
git status - Which auth mode is configured (
AUTH_MODE)?
- Are they using Docker Hub compose (
Understand runtime first, then touch code with local tests if requested.
- Confirm behavior in three layers before editing:
- docs (
README.md, this file) - runtime wiring (
docker-*.yml,Dockerfile, entrypoints) - source (
backend/src,frontend/src)
- docs (
- Preserve patterns used in the repo:
- backend TypeScript server in
backend/src - frontend React/Vite app in
frontend/src
- backend TypeScript server in
- Prefer minimal edits and keep env-sensitive behavior documented before/after changes.
- Do not change migration/secret handling unless explicitly requested.
backend/: Express API, Prisma schema, auth, sockets, scripts, Docker runtime.frontend/: React UI, API client wiring, Vite config/build pipeline.e2e/: Playwright tests and compose-based test runner.docker-compose.yml: local compose setup for source builds.docker-compose.prod.yml: production-style compose using published images.Makefile: repo-wide orchestration commands.README.md: user-facing installation and operational docs.VERSION: version string used in builds.- Local OIDC helper:
docker-compose.oidc.yml+oidc/keycloak/realm-excalidash.json(Keycloak container + realm seed; no users/passwords committed)
- Local OIDC helper:
Dependencies:
- Node.js 20+
- npm
- SQLite-supported environment (default)
- Docker + Docker Compose if using compose path
Install:
npm iin each package:make install(orcd backend && npm install,cd frontend && npm install,cd e2e && npm install)
Start backend + frontend in tmux:
make dev(startsbackendandfrontendin a split tmux session; requirestmux)make dev-stopto stop- Backend dev env:
cd backendcp .env.example .envnpx prisma generatenpx prisma db pushnpm run dev
- Frontend dev env:
cd frontendcp .env.example .envnpm installnpm run dev
Docker quickstart:
docker compose -f docker-compose.prod.yml up -d(official production path, pulls and runs image-based containers)docker compose -f docker-compose.prod.yml pullandup -dfor image-based deploy- Optional local-compose quickstart for source testing:
docker compose up -d(usesdocker-compose.yml) - App default host ports:
- Compose production/frontend:
http://localhost:6767 - Backend internal port:
8000
- Compose production/frontend:
E2E quickstart:
cd e2e && npm installnpx playwright install chromiumnpm test- If using existing services:
NO_SERVER=true npm test - Dockerized:
npm run docker:test
Backend runtime reads .env via backend/.env and backend/src/config.ts.
Frontend runtime uses Vite import.meta.env values from frontend/.env and build-time defines.
Source of truth:
- Backend:
backend/src/config.tsandbackend/.env.example - Frontend:
frontend/.env.exampleandfrontend/vite.config.ts
Backend base variables:
PORT(default8000)NODE_ENV(development/production)DATABASE_URL(file:...default viabackend/.envand resolver)FRONTEND_URL(comma-separated allowed origins)TRUST_PROXY(true,false, or positive hop count)AUTH_MODE(local,hybrid,oidc_enforced)JWT_SECRET(required when running backend withNODE_ENV=production; Docker entrypoint can auto-generate + persist for single-instance setups)CSRF_SECRET(recommended for stable CSRF across restarts; Docker entrypoint can auto-generate + persist for single-instance setups)JWT_ACCESS_EXPIRES_IN(default15m)JWT_REFRESH_EXPIRES_IN(default7d)RATE_LIMIT_MAX_REQUESTS(default1000)CSRF_MAX_REQUESTS(default60)ENABLE_PASSWORD_RESET(trueto enable)ENABLE_REFRESH_TOKEN_ROTATION(true/false, defaulttrue)ENABLE_AUDIT_LOGGING(true/false, defaultfalse)ENFORCE_HTTPS_REDIRECT(true/false, defaulttrue) — whenFRONTEND_URLuseshttps://, the backend auto-redirects plain-HTTP requests; set tofalsewhen the outer gateway already enforces HTTPS to avoid redirect loopsBOOTSTRAP_SETUP_CODE_TTL_MS(default900000)BOOTSTRAP_SETUP_CODE_MAX_ATTEMPTS(default10)UPDATE_CHECK_OUTBOUND(true/false/1/yes, defaulttrue)UPDATE_CHECK_GITHUB_TOKEN(optional token for GitHub API)GITHUB_TOKEN(fallback token if update token missing)DRAWINGS_CACHE_TTL_MS(ms, default5000)DEBUG_CSRF(trueenables debug logs)DISABLE_ONBOARDING_GATE(truebypasses onboarding gate; not recommended)OIDC_PROVIDER_NAME(defaultOIDC, optional unless OIDC mode enabled)OIDC_ISSUER_URL(required inhybrid/oidc_enforced)OIDC_CLIENT_ID(required in OIDC modes)OIDC_CLIENT_SECRET(optional; required for confidential clients, omitted for public clients)OIDC_REDIRECT_URI(required and HTTPS in production in OIDC modes)OIDC_SCOPES(defaultopenid profile email)OIDC_EMAIL_CLAIM(defaultemail)OIDC_EMAIL_VERIFIED_CLAIM(defaultemail_verified)OIDC_REQUIRE_EMAIL_VERIFIED(defaulttrue)OIDC_JIT_PROVISIONING(defaulttrue)OIDC_FIRST_USER_ADMIN(defaulttrue)
Backend Docker/env control variables:
RUN_MIGRATIONS(true/1default true in entrypoint)MIGRATION_LOCK_TIMEOUT_SECONDS(default120)JWT_SECRETandCSRF_SECRETpersistence support (.jwt_secret,.csrf_secretin volume)
Frontend variables:
VITE_API_URL(default/api)VITE_APP_VERSION(from build-time metadata)VITE_APP_BUILD_LABEL(build metadata label)BACKEND_URL(frontend container entrypoint only; defaultbackend:8000, injected into nginx template)
E2E variables:
BASE_URL(defaulthttp://localhost:5173)API_URL(defaulthttp://localhost:8000)HEADED(trueto show browser)NO_SERVER(trueto skip starting servers)CI(ci-mode behavior in Playwright config)
ENABLE_PASSWORD_RESET: enable/disable password reset flow.ENABLE_REFRESH_TOKEN_ROTATION: control refresh-token rotation behavior.ENABLE_AUDIT_LOGGING: enable/disable audit event logging.ENFORCE_HTTPS_REDIRECT: disable built-in HTTP→HTTPS redirect when outer gateway handles it (set tofalse; defaulttrue).UPDATE_CHECK_OUTBOUND: disable outbound version check traffic.DISABLE_ONBOARDING_GATE: bypass first-run onboarding guard (not recommended in production).RUN_MIGRATIONS: run migrations in backend container startup (truedefault).MIGRATION_LOCK_TIMEOUT_SECONDS: wait window for startup migration lock.DRAWINGS_CACHE_TTL_MS: cache duration for list endpoint responses.DEBUG_CSRF: log CSRF debug output for troubleshooting.BOOTSTRAP_SETUP_CODE_TTL_MS: bootstrap setup code expiry.BOOTSTRAP_SETUP_CODE_MAX_ATTEMPTS: max bootstrap attempts before code refresh.
FRONTEND_URLis the origin allow-list for frontend callbacks and API origin checks.TRUST_PROXYshould be1for one trusted proxy hop, orfalsewhen traffic is direct.BACKEND_URLis injected intofrontend/nginx.conf.templateto route:/api/to backend API/socket.io/to backend websocket
- For Kubernetes, set
BACKEND_URLto service DNS (exampleexcalidash-backend.default.svc.cluster.local:8000). - Ensure TLS termination and
X-Forwarded-Protoare consistent with proxy headers; auth + sockets depend on this.
Backend entrypoint flow:
backend/src/config.tsloads and validates environment variables.backend/src/index.tscreates express app, socket.io server, middleware, routes, and startup-time guards.backend/src/auth.ts,backend/src/auth/*, andbackend/src/routes/*contain auth/session/onboarding logic.backend/src/db/prisma.tswraps Prisma client and caches in non-production.backend/src/security.tsandbackend/src/routes/system/update.tscontain request security and update-check controls.- Migration handling for runtime is in
backend/docker-entrypoint.sh. - Build pipeline for runtime includes Prisma generation and TypeScript compile in
backend/Dockerfile.
Frontend architecture notes:
frontend/src/api/index.tsholds API client and auth/update endpoints.frontend/src/pages/contains route-level features.frontend/src/context/contains auth/theme state.frontend/src/pages/Editor.tsxwires Socket.IO and live collaboration.frontend/vite.config.tssets Vite proxy to backend in local dev and compile-time app metadata.- Production serving and backend proxy are handled by
frontend/Dockerfile,frontend/nginx.conf.template,frontend/docker-entrypoint.sh.
- Install:
make install,make dev,make dev-backend,make dev-frontend - Build/test/lint:
make build,make lint,make test,make test-all,make test-e2e,make test-e2e-docker - Docker:
make docker-build,make docker-run,make docker-run-detached,make docker-ps,make docker-logs,make docker-down - Admin/ops helpers: backend scripts under
backend/package.json(admin:recover,dev:simulate-auth-onboarding:*) andbackend/scripts/*
- For helper agents: prefer concise operational steps, likely culprit ordering, and exact command snippets.
- For contributor agents: include file-level references and rationale in your diff note (why file was touched, where config is validated).
- When asked for design or behavior explanations, include:
- Config source file
- Route or component entry point
- Why this logic exists based on existing defaults/constraints
- Confirm env file presence and variables
- Check compose/backend logs before code changes
- Reproduce with minimal path:
make dev- open frontend
http://localhost:6767
- For startup crashes: inspect missing environment validation errors from
backend/src/config.tsand entrypoint migration/secrets log lines.