1- # excaildraw
1+ # rustCanvas
22
3- A scalable, Excalidraw-like collaborative drawing app built in full-stack Rust.
3+ A scalable, Excalidraw-like collaborative whiteboard built in full-stack Rust.
44
5- - ** Frontend:** Yew + WASM + HTML5 Canvas (Trunk) on port ** 3000**
5+ - ** Frontend:** Yew + WASM + HTML5 Canvas (Trunk) — dev on port ** 3000**
66- ** Backend:** Axum + Tokio + WebSockets on port ** 8080**
7+ - ** Production:** Single server serves WASM UI + API from one port
78- ** Shared core:** Excalidraw-compatible elements, JSON I/O, reconciliation, undo/redo, SVG export, rough sketch paths
8- - ** Persistence:** PostgreSQL (optional via ` DATABASE_URL ` ) with in-memory fallback
9- - ** Scaling:** Redis pub/sub (optional via ` REDIS_URL ` ) for multi-instance WebSocket broadcast
9+ - ** Persistence:** PostgreSQL (optional) with in-memory fallback
10+ - ** Scaling:** Redis pub/sub (optional) for multi-instance WebSocket broadcast
1011- ** Desktop:** Tauri 2 shell in ` apps/desktop `
1112
1213## Repository
@@ -15,17 +16,15 @@ https://github.com/antcybersec/excaildraw
1516
1617## Features
1718
18- - ** Drawing tools:** select/move (multi-select with Shift/Cmd) , rectangle, ellipse, line, arrow, freehand, text, image
19- - ** Canvas:** infinite pan/zoom, grid , rough/sketch stroke rendering , selection highlights
19+ - ** Drawing tools:** select/move, rectangle, ellipse, line, arrow, freehand, text, image, eraser
20+ - ** Canvas:** infinite pan/zoom, light/dark theme , rough/sketch strokes , selection highlights
2021- ** Layers panel:** reorder elements, select from list
2122- ** History:** undo/redo (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y)
2223- ** Import/export:** ` .excalidraw ` JSON, SVG, PNG
2324- ** Collaboration:** WebSocket rooms, live sync, remote cursors
24- - ** E2E encryption:** optional room password — client encrypts sync payloads (AES-GCM); server relays ciphertext only
25- - ** Offline:** IndexedDB scene cache + pending update queue when disconnected
26- - ** Auth:** optional JWT (` POST /api/auth/token ` ); set ` REQUIRE_AUTH=1 ` to gate room creation
27- - ** Rate limiting:** in-memory per-IP limits on REST endpoints
28- - ** API:** REST room create/load/save + WebSocket ` /ws/{roomId} `
25+ - ** E2E encryption:** optional room password (AES-GCM)
26+ - ** Offline:** IndexedDB scene cache + pending update queue
27+ - ** Auth:** optional JWT; set ` REQUIRE_AUTH=1 ` to gate room creation
2928
3029## Project structure
3130
@@ -36,16 +35,16 @@ crates/
3635 server/ REST API, WebSocket sync, PostgreSQL, JWT, Redis broker
3736apps/
3837 desktop/ Tauri 2 native shell
39- docker/ Postgres + Redis compose + server Dockerfile
38+ docker/ Postgres + Redis compose + production Dockerfile
39+ scripts/ build-release.sh for local production builds
4040```
4141
4242## Prerequisites
4343
4444- Rust 1.75+
4545- ` wasm32-unknown-unknown ` target
4646- [ Trunk] ( https://trunkrs.dev/ )
47- - Docker (optional, for PostgreSQL + Redis)
48- - Node/npm (optional, only if building Tauri icons via ` tauri icon ` )
47+ - Docker (optional, for PostgreSQL + Redis + production deploy)
4948
5049``` bash
5150rustup target add wasm32-unknown-unknown
@@ -56,97 +55,119 @@ cargo install trunk
5655
5756| Variable | Default | Description |
5857| ----------| ---------| -------------|
59- | ` DATABASE_URL ` | * (none)* | PostgreSQL connection string; omit for in-memory rooms |
60- | ` REDIS_URL ` | * (none)* | Redis URL for cross-instance room broadcast |
58+ | ` PORT ` | ` 8080 ` | HTTP listen port |
59+ | ` FRONTEND_DIST ` | ` dist ` | Path to Trunk ` dist/ ` for serving the WASM UI |
60+ | ` DATABASE_URL ` | * (none)* | PostgreSQL connection string |
61+ | ` REDIS_URL ` | * (none)* | Redis URL for cross-instance broadcast |
6162| ` JWT_SECRET ` | dev secret | HMAC secret for JWT signing |
6263| ` REQUIRE_AUTH ` | * (off)* | Set to ` 1 ` to require Bearer token on ` POST /api/rooms ` |
6364
6465## Development
6566
66- ### 1. Start PostgreSQL + Redis (optional)
67+ ### 1. Run API server
6768
6869``` bash
69- docker compose -f docker/docker-compose.yml up -d
70- export DATABASE_URL=postgres://excaildraw:excaildraw@localhost:5432/excaildraw
71- export REDIS_URL=redis://localhost:6379
70+ cargo run -p excaildraw-server
71+ ```
72+
73+ ### 2. Run web client (separate terminal)
74+
75+ ``` bash
76+ cd crates/client && trunk serve --open
7277```
7378
74- Without ` DATABASE_URL ` , rooms persist in memory only for the server process lifetime.
79+ Open http://127.0.0.1:3000
80+
81+ > Port ** 8080** is API-only in dev. The drawing UI is on ** 3000** .
7582
76- ### 2. Run the API server
83+ ### 3. Optional: Postgres + Redis
7784
7885``` bash
79- cargo run -p excaildraw-server
86+ docker compose -f docker/docker-compose.yml up -d postgres redis
87+ export DATABASE_URL=postgres://excaildraw:excaildraw@localhost:5432/excaildraw
88+ export REDIS_URL=redis://localhost:6379
8089```
8190
82- Endpoints:
91+ ### Collaboration
8392
84- | Method | Path | Description |
85- | --------| ------| -------------|
86- | GET | ` /health ` | Health check |
87- | POST | ` /api/auth/token ` | Issue JWT (` {"username":"..."} ` ) |
88- | POST | ` /api/rooms ` | Create room |
89- | GET | ` /api/rooms/{id} ` | Load room |
90- | PUT | ` /api/rooms/{id} ` | Save room |
91- | WS | ` /ws/{id} ` | Real-time sync |
93+ 1 . Optionally set a ** room password** (E2E encryption)
94+ 2 . Click ** Live collab** — creates a room and updates URL (` ?room=... ` )
95+ 3 . Share the URL (and password if used)
96+ 4 . Draw together — live sync + remote cursors
9297
93- ### 3. Run the web client
98+ ### Keyboard shortcuts
99+
100+ | Key | Tool |
101+ | -----| ------|
102+ | 1–9 | Tools (eraser = E) |
103+ | Scroll | Zoom |
104+ | Space + drag | Pan |
105+ | Shift | Constrain shapes |
106+ | Ctrl/Cmd + Z | Undo |
107+
108+ ## Production deployment
109+
110+ ### Option A — Docker (recommended)
111+
112+ Builds WASM client + server into one image. Serves UI and API on port ** 8080** .
94113
95114``` bash
96- cd crates/client && trunk serve --open
115+ docker compose -f docker/docker-compose.yml up -d --build
97116```
98117
99- Open http://127.0.0.1:3000
118+ Open http://localhost:8080
100119
101- ### 4. Desktop app (Tauri)
120+ Includes Postgres + Redis for persistent rooms and horizontal scaling.
102121
103- Build the WASM client first, then run Tauri from the desktop crate:
122+ ### Option B — Bare metal / VPS
104123
105124``` bash
106- cd crates/client && trunk build -- release
107- cd ../../apps/desktop/src-tauri && cargo tauri dev
125+ ./scripts/ build- release.sh
126+ FRONTEND_DIST=./dist ./target/release/excaildraw-server
108127```
109128
110- Tauri loads the client from ` http://127.0.0.1:3000 ` in dev or from ` dist/ ` after ` trunk build ` .
129+ Put ** nginx** or ** Caddy** in front for HTTPS:
130+
131+ ``` nginx
132+ server {
133+ listen 443 ssl;
134+ server_name your-domain.com;
135+
136+ location / {
137+ proxy_pass http://127.0.0.1:8080;
138+ proxy_http_version 1.1;
139+ proxy_set_header Upgrade $http_upgrade;
140+ proxy_set_header Connection "upgrade";
141+ proxy_set_header Host $host;
142+ }
143+ }
144+ ```
111145
112- ### Collaboration workflow
146+ WebSocket collab requires the ` Upgrade ` headers above.
113147
114- 1 . Optionally enter a ** room password** for end-to-end encrypted sync
115- 2 . Click ** New Room** — creates a room and updates the URL (` ?room=... ` )
116- 3 . Share the URL (and password, if used) with others
117- 4 . Draw together — changes sync via WebSocket with reconciliation
118- 5 . Remote cursors appear with colored labels
119- 6 . Scene auto-saves to IndexedDB for offline reload
148+ ### Option C — Static hosting + separate API
120149
121- ### Keyboard shortcuts
150+ 1 . ` cd crates/client && trunk build --release ` → upload ` dist/ ` to Cloudflare Pages / Netlify / S3
151+ 2 . Deploy server to Fly.io / Railway / Render with ` DATABASE_URL ` + ` REDIS_URL `
152+ 3 . Set client API base via same-origin reverse proxy or configure CORS on server
122153
123- | Shortcut | Action |
124- | ----------| --------|
125- | Scroll | Zoom |
126- | Shift + drag | Pan |
127- | Shift/Cmd + click | Add to selection |
128- | Ctrl/Cmd + Z | Undo |
129- | Ctrl/Cmd + Shift + Z | Redo |
130- | Ctrl/Cmd + Y | Redo |
131- | Ctrl/Cmd + S | Export JSON |
132- | Delete / Backspace | Delete selection |
154+ ### Pre-deploy checklist
155+
156+ - [ ] Set ` JWT_SECRET ` to a strong random value
157+ - [ ] Set ` DATABASE_URL ` for room persistence
158+ - [ ] Set ` REDIS_URL ` if running multiple server instances
159+ - [ ] Build with ` --release ` (WASM + server)
160+ - [ ] Enable HTTPS (required for secure WebSockets in production)
161+ - [ ] Set ` REQUIRE_AUTH=1 ` if you need gated room creation
133162
134- ### Tests
163+ ## Tests
135164
136165``` bash
137166cargo test -p excaildraw-core -p excaildraw-server -p excaildraw-client
138167cargo clippy -p excaildraw-core -p excaildraw-server -p excaildraw-client --all-targets -- -D warnings
139168cd crates/client && trunk build --release
140169```
141170
142- ## Roadmap
143-
144- See [ excaildraw.md] ( ./excaildraw.md ) for full architecture. Future work:
145-
146- - OAuth providers, NATS alternative to Redis
147- - Rough strokes in SVG export, async image rendering
148- - Yrs CRDT migration for conflict-free offline edits
149-
150171## License
151172
152173MIT
0 commit comments