# RefBoard AYON Kollaborative Referenz-Bildwand (PureRef-on-the-web), umgebaut für den AYON-only-Betrieb. Fork von [metalfinger/refboard](https://github.com/metalfinger/refboard) (vollständige History erhalten). **Das Besondere:** Es gibt keinen RefBoard-Login mehr. Nutzer öffnen Boards aus dem AYON-Launcher heraus und sind automatisch mit ihrer AYON-Identität eingeloggt. Boards gehören zu AYON-Tasks (`/`), nicht zu Personen. Alle Kollegen mit Projektzugriff arbeiten kollaborativ am selben Board (Echtzeit, Socket.IO). ## Architektur ``` Launcher (Task ausgewählt) → "RefBoard öffnen" (Server-Addon-Action) → POST /api/addons/refboard//session (AYON-Auth via Bearer) → Addon prüft Projektzugriff, legt Single-Use-Ticket an (60 s TTL) ← { url: "https:///b?ticket=…&project=…&task=…" } → Launcher öffnet Browser auf dieser URL → RefBoard-Frontend (Route /b) ruft POST /api/auth/ayon/exchange → RefBoard-Backend ⇄ Addon /exchange (server-to-server, X-API-Key) → Ticket wird atomar verbraucht (single-use) → Identität → RefBoard-Session-JWT wird gesetzt → weiter zum Task-Board ``` Beteiligte Komponenten: | Komponente | Repo | Aufgabe | |---|---|---| | RefBoard AYON (dieses Repo) | `Hermes/refboard-ayon` | Web-App: Canvas + Ticket-Auth + Task-Boards | | AYON Server-Addon | `Hermes/refboard-addon` (ab v0.2.0) | Ticket-Ausstellung, `/exchange`, Launcher-Action | | AYON Server (private Instanz) | — | Identitätsquelle, Token-Prüfung, Access-Groups | ### Warum ein Ticket statt des AYON-Tokens in der URL? Der AYON-Session-Token ist langlebig und dürfte nie in Browser-History/Logs landen. Das Ticket ist ein 32-Byte-Zufallswert, der genau einmal eingelöst werden kann und nach 60 s verfällt — die OAuth-"Code"-Äquivalenz. Die Identität kommt ausschließlich vom Addon (`/exchange`), niemals aus der URL. ### Task-Boards - Adresse/Name: `/` (z. B. `lumenfjord/assets/vegetation/fir/lookdev`) - Board entsteht lazy beim ersten Öffnen in der Sammlung `AYON` - `users`-Tabelle bleibt intern bestehen (SQLite-Fremdschlüssel), wird aber nicht mehr als Login angezeigt; AYON-Nutzer bekommen Einträge `@ayon.local` mit unbenutzbarem Passwort-Sentinel - Legacy-Login/Register-Routen sind noch vorhanden (für Alt-Instanz), im AYON-Betrieb jedoch ungenutzt ## Deployment (orange) - Container: `refboard-ayon`, Host-Port **8003** → 8000 - Stack-Ordner: `/media/orange/RocketChat/refboard-ayon/` (NVMe) - `repo/` — dieser Code (Branch `ayon-integration`) - `compose.yaml` — Stack (inkl. `ayon-private_default` external network: RefBoard erreicht AYON unter `http://ayon-private-server-1:5000`) - `.env` — `JWT_SECRET`, `REFBOARD_API_KEY`, `AYON_EXCHANGE_URL` - `data/` — SQLite + FS-Storage - nginx: `/etc/nginx/sites-enabled/refboard.conf` → `refboard.niklashmotion.art` (WebSocket-Upgrade, 250 M Upload) - Wichtig nach `docker compose up -d`: das `ayon-private_default`-Netz ist als `external: true` deklariert und überlebt Rebuilds dadurch ### Environment | Variable | Bedeutung | |---|---| | `JWT_SECRET` | Signatur-Secret der RefBoard-Sessions | | `REFBOARD_API_KEY` | Shared Secret für `/exchange` (muss mit dem AYON-Secret `refboard_api_key` übereinstimmen) | | `AYON_EXCHANGE_URL` | z. B. `http://ayon-private-server-1:5000/api/addons/refboard/0.2.0/exchange` | | `STORAGE_BACKEND` | `fs` (FS-Storage) | | `CORS_ORIGIN` | CORS-Origin (Default `*`) | ## Setup in AYON (Schritt für Schritt) > Ausführen, sobald `refboard-0.2.0.zip` und der A-Record existieren. ### 1. Addon installieren (im AYON-Klon, NICHT im Original) ```bash # zip in den Server-Container entpacken ssh orange@niklashmotion.art 'docker exec -i ayon-private-server-1 python3 -c " import zipfile,io,os,sys z=zipfile.ZipFile(io.BytesIO(sys.stdin.buffer.read())) b=\"/addons/refboard/0.2.0\" os.makedirs(b,exist_ok=True) [open(os.path.join(b,n),\"wb\").write(z.read(n)) for n in z.namelist() if not n.endswith(\"/\")] "' < refboard-0.2.0.zip # AYON-Server neu starten (scannt /addons nur beim Start) ssh orange@niklashmotion.art 'docker restart ayon-private-server-1' ``` ### 2. Bundle (WebUI: Studio Settings → Bundles) Neues Bundle anlegen, das die Addons des aktuellen Produktions-Bundles enthält, aber `refboard: 0.2.0`, und als Produktion aktivieren. ### 3. Settings prüfen (WebUI: Studio Settings → Addons → RefBoard) - `refboard_url`: `https://refboard.niklashmotion.art` - `api_key`: `refboard_api_key` (Secret existiert bereits im AYON-Secret-Store und muss denselben Wert wie `REFBOARD_API_KEY` in der RefBoard-`.env` haben) ### 4. Benutzen 1. AYON-Launcher starten und ein Projekt öffnen 2. Task auswählen → Kontext-/Aktionsmenü → **„RefBoard öffnen"** 3. Browser geht auf, automatisch eingeloggt als der eigene AYON-User, im Board der Task 4. Teilen: Kollegen wählen dieselbe Task → „RefBoard öffnen" → alle landen im selben Board und arbeiten in Echtzeit zusammen Hinweise: - Der Board-Name ist `/`; im Editor oben wird er als Titel angezeigt - Ohne Task-Auswahl gibt es keinen Action-Eintrag (Bewusst so gebaut: Boards sind taskbezogen) - Tickets sind einmalig und 60 s gültig — bei „Invalid or expired ticket" einfach im Launcher erneut klicken ## AYON-REST-API des Addons (v0.2.0) Alle Routen unter `/api/addons/refboard/0.2.0/`: | Endpoint | Methode | Auth | Zweck | |---|---|---|---| | `/health` | GET | AYON-User | Status + konfigurierte URL | | `/session` | POST | AYON-User | `{project_name, task_id?}` → `{url}` (Ticket-URL) | | `/exchange` | POST | `X-API-Key` | `{ticket}` → `{ayon_user, display_name, project, task}` (single-use) | ## Lokale Entwicklung / Tests ```bash # Image bauen (ARM64-nativ auf orange getestet) docker build -t refboard-ayon:latest . # Container + Mock-Addon (Test-Double statt echtem AYON-Addon): # /media/orange/RocketChat/refboard-ayon/mock-exchange.js (container :9000) # /media/orange/RocketChat/refboard-ayon/e2e.sh (Container-interner E2E) ``` Der E2E (Ticket-Ausstellung → Exchange → Board-Zugriff → Single-Use → 2. Nutzer am selben Board) ist mit dem Mock grün getestet. ## Status: PRODUKTIV (seit 05.09.2026) - DNS + Let's-Encrypt-Zertifikat aktiv (`refboard.niklashmotion.art`) - Launcher-Klick → eingeloggt im Task-Board — produktiv im Einsatz ## Fehler & Lektionen (Session 04.–05.09.2026) | # | Fehler | Ursache | Fix/Lektion | |---|---|---|---| | 1 | Launcher zeigte fast keine Actions mehr (`KeyError: 'url'` in `_get_webactions`) | `SimpleActionManifest(icon=…)` erzeugte `{"type":"url"}` **ohne** `url`-Feld; eine einzige kaputte Action kippte **alle** Webactions im Client (try/except um den ganzen Fetch) | Icon-Feld weggelassen; Lektion: Action-Icons brauchen `{"type":"material-symbols","name":…}` oder vollständige URL | | 2 | `/session` → 500 `AttributeError: 'UserAttribModel' object has no attribute 'name'` | AYON-User-attrib heißt `fullName`, nicht `name` | `user.attrib.fullName` | | 3 | Exchange → 500 `uuidv4 is not defined` | Import im neuen db.js-Code vergessen | Lektion: py_compile fängt undefined names nicht — Smoke-Test im Container statt nur Syntax-Check | | 4 | RefBoard konnte AYON nicht erreichen nach `compose up` | Externes Docker-Netz nur top-level deklariert, Service nicht attached; manuelles `docker network connect` überlebt kein Recreate | Netz **im Service-Block** als `external: true` deklarieren | | 5 | Exchange → 403 trotz „richtigem" Secret | `REFBOARD_API_KEY`-Wert enthält `=`; Extraktion mit `cut -d= -f2` bricht am ersten `=` → falscher (gekürzter) Wert in AYON-Secret | Werte mit `sed "s/^KEY=//"` extrahieren; Lektion: Secret-Sync immer per Längen-/Hash-Vergleich verifizieren | | 6 | Settings-POST (204) änderte die DB **nicht** | AYON speichert Settings als Ganzes, filtert aber „identity"-Felder (Werte == Defaults) heraus — ein Feld, das dem Default entspricht, ist nie speicherbar | Lektion: Settings-API ist Full-Replace + Identity-Filter; gezielte Feld-Fixe über die API sind nicht immer möglich | | 7 | App-Kacheln fehlten in jeder Task (Launcher leer bis auf 3 Actions) | **Ynput-Bug in applications 1.4.4:** Runtime liest Pydantic-Defaults (`profiles=[]`), nicht `DEFAULT_VALUES` (mit `all_applications`-Profil); das UI zeigt die Defaults trotzdem — Diskrepanz UI/Runtime | Umgehend: Profil per API explizit setzen (Full-Replace aus Backup + Profil-Feld); langfristig: Ynput-Issue | | 8 | Ticket-Wiederverwendung zeigte 502 statt 401 | RefBoard behandelte 401/403 vom Addon als Infrastruktur-Fehler | 401/403 aus `/exchange` → sauberes 401 „Invalid or expired ticket" | | 9 | Nach Secret-Löschung + Neuanlage Exchange-Fehler | AYON-Secret-Wert ≠ Container-Env (Container läuft mit altem Env bis Recreate) | Nach Secret-Änderungen: `docker compose up -d --force-recreate` | ## Noch offen - ~~A-Record~~ ✓ erledigt (05.09.) - ~~Zertifikat~~ ✓ erledigt (05.09.) - Nuke-17.0-Pfad-Mismatch in den Applications-Settings (`/usr/local/Nuke17.0v1` erwartet, installiert ist `/opt/Nuke17.0v3`) — Fix im Studio-Settings-WebUI: Executable auf `/usr/local/bin/nuke-ayon` setzen - Alt-Container `refboard` (:8002, standalone) stilllegen, sobald Bestands- Boards nicht mehr gebraucht werden - Ynput-Issue zu #7 (applications 1.4.4 Runtime-Defaults) optional melden