diff --git a/docs/HANDOFF-agent-setup.md b/docs/HANDOFF-agent-setup.md index d024f81..bb10808 100644 --- a/docs/HANDOFF-agent-setup.md +++ b/docs/HANDOFF-agent-setup.md @@ -34,16 +34,54 @@ file deleted from `/home/pe1085/development/gitea-test/`. It had images that can't run on ARM64. **Do not re-register it.** Only the RPi 5 ARM64 runner should exist. -### 4. Frontend WebSocket URL -`frontend/src/App.tsx` line 438: -```ts -const wsUrl = 'wss://excalidraw.forkless.com' -``` -Previously used `window.location.host` — hardcoded to the forkless domain. -This is for the app's custom sync protocol (JSON messages via `ws` library), -not the Excalidraw collaboration Socket.IO. +### 4. Frontend WebSocket URL — `CUSTOM_VITE_WS_URL` +**New env var** `CUSTOM_VITE_WS_URL` controls the frontend's WebSocket target. +Hardcoded `wss://excalidraw.forkless.com` replaced with a Vite build-time define. -### 5. Reverse Proxy Config (NPM v2.15.1 / OpenResty) +**Where:** +- `vite.config.js` — reads `process.env.CUSTOM_VITE_WS_URL`, defaults to + `wss://excalidraw.forkless.com`, exposes as `import.meta.env.CUSTOM_VITE_WS_URL` +- `frontend/src/App.tsx` — uses `import.meta.env.CUSTOM_VITE_WS_URL` + +**URL normalization** (auto handles user input): +- `excalidraw.forkless.com` → `wss://excalidraw.forkless.com` +- `wss://excalidraw.forkless.com/socket.io/foo` → `wss://excalidraw.forkless.com` +- `ws://dev.local:5173` → `ws://dev.local:5173` (preserves ws://) +- `https://example.com:3002/path` → `wss://example.com:3002` +- `10.0.101.100` → `wss://10.0.101.100` +- `[::1]:3000` → `wss://[::1]:3000` +- Port is preserved if present, omitted if absent. + +### 5. `.env.example` +Created at repo root. Documents all key env vars. +```env +CUSTOM_VITE_WS_URL=excalidraw.forkless.com # auto-normalized +CANVAS_PORT=3000 +HOST=0.0.0.0 +EXCALIDRAW_API_KEY= +ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000 +EXCALIDRAW_DB_PATH=~/.excalidraw-mcp/excalidraw.db +LOG_LEVEL=info +``` + +### 6. Docker Build Integration +`Dockerfile.canvas` frontend-builder stage accepts `ARG CUSTOM_VITE_WS_URL` +and sets it as `ENV` so Vite picks it up at build time. + +`docker-compose.yml` canvas service passes it as build arg: +```yaml +args: + - CUSTOM_VITE_WS_URL=${CUSTOM_VITE_WS_URL:-excalidraw.forkless.com} +``` + +To override at build time: +```bash +docker compose build --build-arg CUSTOM_VITE_WS_URL=wss://my-server.com canvas +# or +CUSTOM_VITE_WS_URL=my-server.com docker compose build canvas +``` + +### 7. Reverse Proxy Config (NPM v2.15.1 / OpenResty) The server at `excalidraw.forkless.com` runs behind Nginx Proxy Manager. **Critical settings for WebSocket to work:** - **WebSockets Support** toggle enabled in NPM @@ -52,7 +90,7 @@ The server at `excalidraw.forkless.com` runs behind Nginx Proxy Manager. toggle alone doesn't work — the toggle handles it. Custom config can conflict and break NPM entirely (requires container restart to recover). -### 6. `trust proxy` in Express +### 8. `trust proxy` in Express `src/server.ts` line 43: ```ts app.set('trust proxy', 1); @@ -60,72 +98,34 @@ app.set('trust proxy', 1); Required because NPM sets `X-Forwarded-For` and express-rate-limit rejects requests without this setting. -### 7. `ALLOWED_ORIGINS` Env Var +### 9. `ALLOWED_ORIGINS` Env Var The `verifyWsClient()` in `src/security.ts` checks `Origin` header against `ALLOWED_ORIGINS`. Default: `http://localhost:3000`, `http://127.0.0.1:3000`. **Must include `https://excalidraw.forkless.com` for production.** -### 8. Gitea Actions Secrets +### 10. Gitea Actions Secrets Set in Gitea repo → Settings → Actions → Secrets: - `GITEAUSER` = `forkless` - `GITEATOKEN` = Gitea PAT with `write:repository` scope -### 9. `collabServerUrl` Prop (Unused / Placeholder) -`frontend/src/App.tsx` line 1864-1865 still has: -```tsx -collabServerUrl="wss://excalidraw.forkless.com/socket.io" -onCollabDialogOpen={() => {}} -``` +### 11. `collabServerUrl` Prop (Removed) +`collabServerUrl` and `onCollabDialogOpen` props were removed from App.tsx. These are **not documented/working props** in `@excalidraw/excalidraw` v0.18.x -and are likely ignored by the library. They can be safely removed. The real -collaboration URL is hardcoded in the Excalidraw npm bundle and connects to -`wss://oss-collab.excalidraw.com` by default. +and were ignored by the library. The collab URL is hardcoded in the npm bundle +and connects to `wss://oss-collab.excalidraw.com` by default — this is fine. --- ## What Still Needs Work -### 1. Build-Time URL Patch (Broken / Remove) -Commit `6ed842d` added `scripts/patch-collab-url.mjs` and modified `package.json` -to run it after `vite build`. The script replaces `wss://oss-collab.excalidraw.com` -with a custom URL in the built bundle. - -Current state: -- `package.json` still has `"build:frontend": "vite build && node scripts/patch-collab-url.mjs"` -- The script file was deleted from disk but exists in git history -- **Next build will fail** because the script file is missing - -**Remove it:** -```bash -cd /home/pe1085/development/excalidraw-mcp-sentinel -# Option A: Remove the script reference from package.json -sed -i 's/ && node scripts\/patch-collab-url.mjs//' package.json -# Option B: Restore the script file from git -git restore scripts/patch-collab-url.mjs -``` - -The official collab server at `oss-collab.excalidraw.com` works fine with the -trust proxy + CORS + cache-flush fix. Only self-host this if the official -server is blocked. - -### 2. Docker Build Fails (Missing Patch Script) -Build 783 failed because of the missing script file. Fix above first, then -trigger a clean build (must modify a file matching the paths filter — -`Dockerfile`, `src/**`, `frontend/**`, `.gitea/**`). - -### 3. LocalStorage Cache -Browser caches the Excalidraw bundle aggressively. When switching between -collab URLs, users must clear localStorage + hard refresh (Ctrl+F5) or -the old URL persists in their browser. - -### 4. WebSocket Auth Hardening +### 1. WebSocket Auth Hardening When `EXCALIDRAW_API_KEY` is set, the WS requires auth. The frontend sends the key in a `hello` message after receiving `auth_required`. Without the key, the connection drops. Consider: - Rate-limit WS connections by IP - Reject no-origin connections in `verifyWsClient` (currently allows) -### 5. Docker Image Tags +### 2. Docker Image Tags Only `:latest` and `:$sha` are pushed. Version tags (`v*`) trigger is in the workflow but untested. @@ -160,9 +160,12 @@ TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath= # Check Docker image manifest docker manifest inspect gitea.forkless.com/forkless/excalidraw-mcp-sentinel:latest -# Push without credential helper issues +# Push (credential helper auto-handles auth) git push origin main # Trigger dispatch (if paths filter blocks push trigger) TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath=/forkless/excalidraw-mcp-sentinel.git\n' 2>/dev/null | grep "^password=" | cut -d= -f2) && curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" "https://gitea.forkless.com/api/v1/repos/forkless/excalidraw-mcp-sentinel/actions/workflows/docker.yml/dispatches" -d '{"ref":"main"}' + +# Build canvas with custom WS URL +CUSTOM_VITE_WS_URL=my-ws-server.com docker compose build canvas ```