Files
excalidraw-mcp-sentinel/docs/HANDOFF-agent-setup.md
T
forkless a06c3d2e36
Docker Build (ARM64) / Build & Push (push) Successful in 2m1s
cleanup: remove broken patch script ref + unused collab props
2026-06-24 20:25:09 +02:00

169 lines
7.3 KiB
Markdown

# Agent Handoff: ARM64 Docker Build & Collaboration Setup
## Overview
Gitea server at `gitea.forkless.com` (RPi 5, native ARM64) hosts the repo
`forkless/excalidraw-mcp-sentinel`. An ARM64 Gitea Actions runner is registered
on the Gitea server itself (labels: `arm64`, `ubuntu-latest`). Runner #1.
Local workspace: `/home/pe1085/development/excalidraw-mcp-sentinel`
Remote: `https://gitea.forkless.com/forkless/excalidraw-mcp-sentinel`
---
## What Was Done
### 1. Gitea Actions Workflow
`.gitea/workflows/docker.yml` — builds both Docker images on ARM64 runner:
- `gitea.forkless.com/forkless/excalidraw-mcp-sentinel:latest`
- `gitea.forkless.com/forkless/excalidraw-mcp-sentinel-canvas:latest`
Triggers on push to `main` (paths: Dockerfile, src/**, frontend/**, .gitea/**),
PRs, and `workflow_dispatch`. **Avoid empty commits** — they don't match the
`paths` filter and won't trigger a build.
### 2. Alpine Base Images
Both `Dockerfile` and `Dockerfile.canvas` changed from `node:20-slim` to
`node:20-alpine`. Build tools via `apk add --no-cache python3 make g++`.
### 3. AMD64 Runner Removed
There was a local x86_64 Gitea Actions runner (`wsl-runner`, id=1) on this
machine that competed with the ARM64 runner. It was stopped and its `.runner`
file deleted from `/home/pe1085/development/gitea-test/`. It had
`ubuntu-latest:docker://catthehacker/ubuntu:full-latest` — would build amd64
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.
### 5. 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
- The WebSocket upgrade headers (`Upgrade`, `Connection`) are forwarded
- **Do NOT** add custom nginx proxy config in the Advanced tab unless the
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
`src/server.ts` line 43:
```ts
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
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
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={() => {}}
```
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.
---
## 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
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
Only `:latest` and `:$sha` are pushed. Version tags (`v*`) trigger is in
the workflow but untested.
---
## Credentials & Access
- **Gitea API token**: stored in `~/.profile` as `GITEA_TOKEN`
- **Git credentials**: stored via `git credential-store` for
`gitea.forkless.com` — username `forkless`, password = Gitea PAT
- To use the token in exec_shell:
`source ~/.profile && echo "$GITEA_TOKEN"` (DO NOT source `~/.profile`
if the user objects — but the token is stored there)
- Alternative: read via git credential helper:
`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)`
### Git Credential Helper Fix
The system had a broken `credential.helper` for `gitea.forkless.com` pointing
to `!tea login helper` — but the binary is `tea-cli`, not `tea`. Fixed with:
```bash
git config --global --replace-all credential.https://gitea.forkless.com.helper '!tea-cli login helper'
```
---
## Quick Commands
```bash
# Check latest build
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 -H "Authorization: token $TOKEN" "https://gitea.forkless.com/api/v1/repos/forkless/excalidraw-mcp-sentinel/actions/runs?limit=5" | python3 -c "import sys,json;d=json.load(sys.stdin);[print(f'Run {r[\"id\"]}: {r[\"status\"]:>10} {r.get(\"conclusion\",\"-\"):>10} {r[\"head_sha\"][:12]}') for r in d.get('workflow_runs',[])]"
# Check Docker image manifest
docker manifest inspect gitea.forkless.com/forkless/excalidraw-mcp-sentinel:latest
# Push without credential helper issues
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"}'
```