634 lines
31 KiB
Markdown
634 lines
31 KiB
Markdown
# Neural Hunt — V3.1 RIFT Task-Style Collection + Profile Cleanup
|
||
|
||
|
||
|
||
> **V3.1 Admin Profile Cleanup:** Im Admin-Tab **RUNTIME** gibt es ein manuelles Bereinigungstool für alte Identitäten. Die Inaktivitätsdauer ist in Stunden/Tagen/Wochen einstellbar. Vor dem Löschen zeigt **PRÜFEN** die Anzahl löschbarer Profile. Gelöscht werden ausschließlich Profile, deren letzte Aktivität älter als die gewählte Grenze ist, die aktuell nicht verbunden sind und die niemals Gewinner eines Tasks waren. Gewinner werden immer geschützt; aktuell verbundene Clients ebenfalls. Beim Löschen werden die per Foreign Key abhängigen `task_points`, `client_unlocks` und `client_task_selection` mit entfernt. WebSocket-Verbindungsaufbau und -ende aktualisieren `clients.last_seen`, damit die Inaktivitätsgrenze tatsächliche Nutzung besser abbildet.
|
||
|
||
> **V2.8 RIFT Task-Styles:** RIFT-Identität und Rendering-Stil sind jetzt sauber getrennt. `/data/artifacts/_collection/character_anchor.png` ist ein globaler, neutraler Identity-Lock für den Waschbären RIFT und kann im Admin-Tab **ARTIFACT** einmalig manuell erzeugt und geprüft werden. Jeder Task kann im Admin-Tab **TASK ACTIONS** ein eigenes JPEG-/PNG-Style-Referenzbild erhalten; Nutzer sehen dieses Stylebild bereits in der Task-Auswahl und wählen damit indirekt die gewünschte NFT-Art. Bei jeder RIFT-Karte sendet Neural Hunt **Image 1 = Character Anchor** und **Image 2 = Task Style Reference** an die Images Edit API. Ohne eigenen Task-Style bleibt `internal/artifact/assets/style_reference.jpg` nur noch der Default-Fallback. Folge-Tasks erben ihren Style. `medium` bleibt Standard und die OpenAI-Usage-/Kosten-KPIs aus V2.7 bleiben erhalten.
|
||
|
||
|
||
> **V2.5.2 Hotfix:** Der 3-Sekunden-Telemetrie-Poll hat in V2.5.1 den kompletten Control-Plane-DOM neu aufgebaut. Dadurch wurden native `<select>`-Dropdowns geschlossen und Cursor-/Scrollzustände in Eingabefeldern zerstört, obwohl die Werte über Drafts erhalten blieben. V2.5.2 trennt Telemetrie-Refresh und Formular-Rendering: Overview, Performance, Taskliste und Map aktualisieren sich weiterhin live, die Formular-Controls werden aber nur noch bei explizitem Tab-/Taskwechsel oder nach einer Admin-Aktion neu aufgebaut. Offene Dropdowns und laufende Eingaben bleiben damit stabil.
|
||
|
||
> **V2.5.1 Hotfix:** Ein WebSocket-Heartbeat-Fehler in V2.5 konnte Browser-Verbindungen sehr regelmäßig nach ungefähr 90 Sekunden beenden. Der Server hatte zwar eine Pong-Deadline gesetzt, aber nur ein JSON-`ping` statt eines echten WebSocket-Control-Pings gesendet. Danach fehlte die Presence-Lease und der nächste Tipp erhielt HTTP 409, bis F5 eine neue Verbindung öffnete. V2.5.1 sendet echte Ping-Frames, schützt Reconnects mit generationsgebundenen Presence-Leases und verbindet Browser/Shell automatisch neu. HTTP-409-Antworten besitzen zusätzlich maschinenlesbare Fehlercodes und werden clientseitig selbstständig resynchronisiert.
|
||
|
||
> **V2.5:** Nutzer wählen ihren Task auf einer eigenen Landing-Page und können jederzeit wechseln. Jeder Task besitzt eigene NFT-Prompt-Anweisungen, Anzeigename und Beschreibung; sein Folge-Task erbt diese Konfiguration. Admin-Formulare behalten ungespeicherte Eingaben auch bei Auto-Refresh und Browser-Reload. Zusätzlich gibt es mit `cmd/client` einen vollwertigen signierten Shell-Client für unbeaufsichtigten Betrieb.
|
||
>
|
||
> Die **V2.4-SCALE-Architektur** bleibt erhalten: Verlierende Tipps bleiben im RAM, Map-Updates werden gebatcht, WebSockets schreiben asynchron und Map-Snapshots sind serverseitig begrenzt. Bitte weiterhin in ein **neues/leeres Verzeichnis** entpacken.
|
||
|
||
|
||
Neural Hunt ist eine eigenständige Go-Webanwendung für das beschriebene soziale Wahrscheinlichkeitsexperiment. Sie benötigt für den Normalbetrieb **nur das Go-Binary und SQLite über `modernc.org/sqlite`**. Es gibt kein PostgreSQL, Redis, S3/MinIO und keinen separaten Frontend-Build.
|
||
|
||
Die Weboberfläche liegt unter `internal/webui/dist/` und wird mit `//go:embed` in das Binary eingebettet. Der Server lädt beim lokalen Start automatisch eine vorhandene `.env` (bereits gesetzte Prozess-Umgebungsvariablen haben Vorrang), daher reicht `go run ./cmd/server` für Backend, Client-UI, Admin-UI und öffentliches Echtzeit-Leaderboard.
|
||
|
||
## Schnellstart
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# JWT_SECRET, ADMIN_PASSWORD und OPENAI_API_KEY setzen.
|
||
# ARTIFACT_MODEL nur ändern, wenn du bewusst ein anderes GPT-Image-Modell nutzen willst.
|
||
|
||
go run ./cmd/server
|
||
```
|
||
|
||
oder:
|
||
|
||
```bash
|
||
docker compose up --build
|
||
```
|
||
|
||
Danach:
|
||
|
||
- Client: `http://localhost:8080/`
|
||
- Echtzeit-Leaderboard: `http://localhost:8080/leaderboard`
|
||
- Admin: `http://localhost:8080/admin`
|
||
|
||
|
||
## V2.5: Task-Landing-Page und Task-Serien
|
||
|
||
Der Browser startet nach der Anmeldung nicht mehr stillschweigend in einem zufällig zugewiesenen Task. Stattdessen erscheint eine responsive **Task-Landing-Page** mit allen aktiven Tasks. Jede Karte zeigt Anzeigename, Beschreibung, Zahlenraum, Teilnehmerzahl, eigenen Score/Rank und Pause-Status. Ein Klick wählt den Task aus; über **TASKS** kann der Client die laufende Session sauber verlassen und später auf einen anderen aktiven Task wechseln.
|
||
|
||
Die Auswahl wird serverseitig in SQLite an die kryptografische Client-ID gebunden. Wird dieselbe Identität exportiert und auf einem anderen Gerät importiert, bleibt damit auch die zuletzt gewählte Task-Serie erhalten. Die Regel **eine Identity = eine aktive WebSocket-Verbindung** gilt weiterhin.
|
||
|
||
Wenn ein Task gewonnen oder administrativ geschlossen wird, erzeugt Neural Hunt genau einen Folge-Task. Dieser erbt vom Vorgänger:
|
||
|
||
- Zahlenraum (`range_bits`),
|
||
- task-spezifisches Server- und Client-Tippintervall,
|
||
- Anzeigename,
|
||
- Landing-Page-Beschreibung,
|
||
- NFT-Prompt-Anweisungen,
|
||
- task-spezifischen Negative Prompt,
|
||
- NFT-Style-Referenz.
|
||
|
||
`paused` wird absichtlich nicht vererbt: Ein neuer Folge-Task startet aktiv. Bestehende Client-Auswahlen des Vorgängers werden atomar auf den Folge-Task verschoben.
|
||
|
||
### Task-spezifische NFT-Prompts
|
||
|
||
Im Admin-Tab **TASK** besitzt jeder Task jetzt einen eigenen Bereich **TASK-KONFIGURATION & NFT-PROMPT**. Der globale Artifact-Prompt bleibt die stilistische Basis; die Task-Anweisungen werden anschließend angefügt. Damit kann beispielsweise eine Serie ihren eigenen visuellen Stil oder ihr eigenes Motiv erhalten, ohne die globale Provider-Konfiguration zu verändern.
|
||
|
||
Beispiel für einen Task:
|
||
|
||
```text
|
||
Anzeigename: Aurora Vault
|
||
Beschreibung: 48-Bit-Raum, Gewinner erhält die Aurora-Serie.
|
||
|
||
NFT-Prompt-Anweisungen:
|
||
Create a crystalline aurora data-vault, radial neural filaments,
|
||
no readable words, emphasize the winner as a singular luminous core.
|
||
|
||
Negative Prompt:
|
||
text, watermark, logo, duplicated core, low detail
|
||
```
|
||
|
||
Der Folge-Task übernimmt diese Werte automatisch. OpenAI verwendet die positiven Task-Anweisungen zusammen mit dem globalen Prompt; ComfyUI und A1111 erhalten zusätzlich den kombinierten globalen + task-spezifischen Negative Prompt.
|
||
|
||
### Admin-Entwürfe überleben Refresh
|
||
|
||
Die dynamischen Admin-Formulare speichern ungespeicherte Eingaben lokal als Draft. Das betrifft Runtime-/Artifact-Settings, Task-Konfiguration und die Felder für geplante Aktionen. Der 3-Sekunden-Auto-Refresh und ein normaler Browser-Reload überschreiben diese Eingaben nicht mehr. Nach erfolgreichem Speichern wird nur der zugehörige Draft-Bereich gelöscht.
|
||
|
||
## Shell-Client
|
||
|
||
`cmd/client` ist ein eigenständiger Neural-Hunt-Client ohne Browser. Er verwendet dieselbe P-256-Identität, dieselbe Challenge/Login-Signatur, denselben deterministischen Guess und denselben WebSocket-Presence-Mechanismus wie die Weboberfläche.
|
||
|
||
Interaktiv starten:
|
||
|
||
```bash
|
||
go run ./cmd/client -url http://127.0.0.1:8080
|
||
```
|
||
|
||
Beim ersten Start wird standardmäßig `~/.neuralhunt/identity.json` mit Dateirechten `0600` erzeugt. Danach zeigt die Shell eine Task-Auswahl ähnlich der Browser-Landing-Page.
|
||
|
||
Wichtige Befehle:
|
||
|
||
```text
|
||
tasks aktive Tasks + eigener Score/Rank
|
||
use <nr|id|name> Task wechseln
|
||
status aktueller Task, Score, Rank, Sequence, Wins
|
||
map [n] textuelles TARGET FIELD nach Nähe-Zonen
|
||
leaderboard [n] Echtzeit-Leaderboard abrufen
|
||
leaderboard watch Leaderboard alle 5 Sekunden anzeigen
|
||
leaderboard stop Watch beenden
|
||
nfts [n] Winner-Artefakte mit Wasserzeichen-URLs
|
||
nft get <task-id> <datei> öffentliche Wasserzeichen-Preview speichern
|
||
identity Client-ID + lokale Identity-Datei
|
||
identity export <datei> browser-kompatibler verschlüsselter Export
|
||
quit
|
||
```
|
||
|
||
Für `tmux`, `screen`, systemd oder einen Server ohne Vordergrund-Browser:
|
||
|
||
```bash
|
||
go run ./cmd/client \
|
||
-url https://hunt.example.org \
|
||
-non-interactive \
|
||
-task "Aurora Vault" \
|
||
-max-nodes 250
|
||
```
|
||
|
||
Im Non-Interactive-Modus bleibt der WebSocket offen, sendet automatisch im vom Task vorgegebenen Intervall signierte Tipps und folgt einem abgeschlossenen Task automatisch auf dessen Nachfolger. `SIGINT`/`SIGTERM` beendet sauber.
|
||
|
||
Für eine feste Binary:
|
||
|
||
```bash
|
||
go build -trimpath -o neuralhunt-client ./cmd/client
|
||
./neuralhunt-client -url https://hunt.example.org -non-interactive -task "Aurora Vault"
|
||
```
|
||
|
||
Ein minimales systemd-Beispiel:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Neural Hunt Shell Client
|
||
After=network-online.target
|
||
|
||
[Service]
|
||
ExecStart=/opt/neuralhunt/neuralhunt-client -url https://hunt.example.org -non-interactive -task "Aurora Vault"
|
||
Environment=NEURALHUNT_IDENTITY=/var/lib/neuralhunt-client/identity.json
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
User=neuralhunt
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
### Browser-Identität im Terminal verwenden
|
||
|
||
Die Browseroberfläche exportiert die Identität verschlüsselt mit PBKDF2-HMAC-SHA256 (250.000 Iterationen) + AES-256-GCM. Der Shell-Client versteht exakt dieses Format:
|
||
|
||
```bash
|
||
export NEURALHUNT_IDENTITY_PASSPHRASE='ein-langes-passwort'
|
||
go run ./cmd/client \
|
||
-import ./neuralhunt-identity-abc123.json \
|
||
-identity ~/.neuralhunt/identity.json
|
||
```
|
||
|
||
Um die Terminal-Identity wieder browser-kompatibel zu exportieren:
|
||
|
||
```bash
|
||
export NEURALHUNT_IDENTITY_PASSPHRASE='ein-langes-passwort'
|
||
go run ./cmd/client \
|
||
-identity ~/.neuralhunt/identity.json \
|
||
-export ./neuralhunt-browser-import.json
|
||
```
|
||
|
||
**Nicht dieselbe Identity gleichzeitig im Browser und im Shell-Client verbinden.** Das ist absichtlich durch die Single-Connection-Regel gesperrt. Unterschiedliche Identities dürfen natürlich vom selben Rechner bzw. derselben IP verbunden sein.
|
||
|
||
Unterstützte Shell-Parameter:
|
||
|
||
```text
|
||
-url Server-URL (oder NEURALHUNT_URL)
|
||
-identity persistente Raw-Identity (oder NEURALHUNT_IDENTITY)
|
||
-task Start-Task per Nummer, ID-Präfix oder Anzeigename
|
||
-max-nodes Größe des lokalen Target-Field-Working-Sets
|
||
-non-interactive unbeaufsichtigter Betrieb
|
||
-quiet weniger Konsolenausgabe
|
||
-import Browser-/Terminal-Identity importieren
|
||
-export verschlüsselten Browser-Export schreiben und beenden
|
||
-passphrase Import/Export-Passphrase; bevorzugt ENV verwenden
|
||
```
|
||
|
||
## Faire signierte Tipps
|
||
|
||
Der Browser darf den Tipp nicht frei wählen. Die Zahl ist deterministisch:
|
||
|
||
```text
|
||
SHA-256(task_id | public_seed | client_id | sequence) mod 2^range_bits
|
||
```
|
||
|
||
Danach signiert der Browser:
|
||
|
||
```text
|
||
guess|task_id|sequence|guess
|
||
```
|
||
|
||
mit seinem persistenten P-256-Schlüssel. Der Server berechnet den erwarteten Guess erneut und prüft die Signatur. Standardmäßig akzeptiert der Server höchstens einen Tipp alle 10 Sekunden; der Browser sendet alle 11 Sekunden.
|
||
|
||
Falsche Roh-Tipps und deren Signaturen werden **nicht gespeichert**. In V2.4 erzeugt ein akzeptierter Tipp, der den persönlichen Best-Score nicht verbessert, sogar **gar keinen SQLite-Write und gar kein WebSocket-Map-Event**. `next_seq`, Rate-Limit, Guess-Zähler und Bestwert liegen auf dem Hotpath im RAM. Erst eine Score-Verbesserung checkpointet den aggregierten Zustand in SQLite; beim Gewinner bleiben zusätzlich der korrekte Guess und dessen Signatur erhalten.
|
||
|
||
## TARGET FIELD — lesbare 3D-Nähe
|
||
|
||
Die vorherige Visualisierung hatte ein grundsätzliches Wahrnehmungsproblem: Eine echte 3D-Kugel um den Task kann in der 2D-Projektion einen weit entfernten Punkt scheinbar direkt neben dem Task zeigen, wenn er entlang der Kameratiefe liegt. Damit war „wer ist wirklich näher?“ trotz korrekter Daten nicht zuverlässig ablesbar.
|
||
|
||
V2.3 verwendet deshalb standardmäßig ein **3D-TARGET-FIELD mit unverzerrter Score-Ebene**. Der Score bestimmt ausschließlich den sichtbaren Orbitalradius; die Client-ID bestimmt nur den Winkel. Die dritte Dimension wird über Tiefensortierung, Perspektivgröße, Helligkeit und Vorder-/Rückseiten-Cues codiert, verschiebt den Node aber nicht radial. Dadurch kann Kameratiefe die Nähe nicht mehr optisch umkehren. Die Ringe und Nodes benutzen exakt dieselbe Radiusfunktion:
|
||
|
||
```text
|
||
kleinerer Ringradius = höherer Score = näher am Task
|
||
```
|
||
|
||
Die Wahrnehmungsfunktion ist bewusst im Bereich 90–100 gedehnt:
|
||
|
||
```text
|
||
field radius = 0.045 + 0.955 * (1 - score/100)^0.38
|
||
```
|
||
|
||
Damit sind z. B. 90, 95, 99 und 99.9 noch klar getrennt. Zusätzlich:
|
||
|
||
- Score-Ringe bei `0 / 25 / 50 / 75 / 90 / 95 / 99+`,
|
||
- Vorder-/Rückseite der Ringe mit unterschiedlicher Intensität als 3D-Tiefencue,
|
||
- Top-10 und eigene ID mit Score-Label,
|
||
- eigene ID mit weißem Doppelring,
|
||
- Kandidaten ab 95 mit zusätzlichem Nähe-Ring,
|
||
- Hover zeigt Score, Rank und eine verständliche Distanzzone,
|
||
- `TARGET RADAR` zeigt die besten Kandidaten zusätzlich numerisch,
|
||
- `TARGET FIELD` kann weiterhin auf `RAW 3D` umgeschaltet werden, um die serverseitigen Rohkoordinaten zu inspizieren.
|
||
|
||
### Keine erfundenen Synapsen mehr
|
||
|
||
Die alte Option `SYNAPSEN` verband Clients pseudozufällig miteinander. Dafür gibt es im Experiment keine fachliche Beziehung und die Linien erzeugten nur visuelles Rauschen. Diese Darstellung wurde entfernt.
|
||
|
||
`SIGNALWEGE` zeichnen jetzt nur noch **Client → Task**:
|
||
|
||
- nur Top-Kandidaten und der eigene Client erhalten einen Pfad,
|
||
- höhere Scores machen den Pfad deutlicher,
|
||
- unter 90 werden Pfade gestrichelt,
|
||
- wenn ein Client seinen Best-Score verbessert, laufen drei kurze Signalimpulse entlang genau dieses Pfades zum Task,
|
||
- es gibt keine zufälligen Ambient-Partikel und keine Client-zu-Client-Kanten mehr.
|
||
|
||
Der gleiche Renderer wird im Admin-Bereich verwendet.
|
||
|
||
## Mobile-Modus
|
||
|
||
Neural Hunt aktiviert auf kleinen Displays bzw. Geräten mit grobem Pointer automatisch einen Mobile-Modus. Er kann über den Button **MOBILE** auch manuell an- und ausgeschaltet werden; die Auswahl wird im Browser gespeichert.
|
||
|
||
Im Client reduziert Mobile Mode die Renderlast automatisch (ECO, weniger Nodes, keine optionalen Signalwege/Labels), zeigt Rank/Score/Countdown kompakt im Header und bietet eine einklappbare Detailkarte. Die 3D-Steuerung wird auf die wichtigsten Touch-Aktionen reduziert.
|
||
|
||
Im Admin-Bereich gibt es auf Mobile drei klare Ansichten **MAP / TASKS / CONTROL**, damit Task-Liste und Control Plane nicht mehr durch Responsive-CSS verschwinden. Das Leaderboard wechselt auf Karten statt horizontaler Tabellen.
|
||
|
||
## Öffentliches Echtzeit-Leaderboard
|
||
|
||
`/leaderboard` ist eine eigene, nicht authentifizierungspflichtige Ansicht. Sie bietet:
|
||
|
||
- Live-Ranking nach aktuellem Score,
|
||
- All-Time-Ranking nach Wins / Best Score,
|
||
- Online/Offline-Indikator,
|
||
- Best Score, Wins, Tippanzahl und Unlocks,
|
||
- Client-ID-Suche,
|
||
- WebSocket-getriggerte Aktualisierung bei neuen Punkten, Task-Änderungen und Task-Abschlüssen.
|
||
|
||
Zusätzlich zeigt das Leaderboard eine Gewinner-Galerie. Die öffentliche API liefert **keine Original-Artifact-URI**, sondern nur eine Wasserzeichen-Vorschau. Für PNG/JPEG/GIF wird das Wasserzeichen serverseitig in eine neue PNG-Vorschau gerastert; lokale SVG-Artefakte erhalten eine sichtbare, wiederholte SVG-Wasserzeichenebene.
|
||
|
||
Die Originaldatei und das Manifest sind nur noch über authentifizierte Admin-Endpunkte erreichbar. Der frühere öffentliche `/artifacts/*`-File-Server wurde entfernt.
|
||
|
||
API:
|
||
|
||
```text
|
||
GET /api/public/leaderboard?mode=live&limit=500
|
||
GET /api/public/leaderboard?mode=alltime&limit=500
|
||
GET /api/public/artifacts?limit=96
|
||
GET /api/public/artifacts/{task_id}/preview
|
||
WS /api/leaderboard/ws
|
||
|
||
# nur Admin-JWT
|
||
GET /api/admin/tasks/{task_id}/artifact
|
||
GET /api/admin/tasks/{task_id}/manifest
|
||
```
|
||
|
||
## Admin: planbare Task-Aktionen
|
||
|
||
Jeder Task besitzt jetzt einen persistierten Aktionsplan/Audit-Log. Aktionen können **sofort** oder für einen Zeitpunkt in der Zukunft geplant werden. Ein 1-Sekunden-Scheduler führt fällige Aktionen aus.
|
||
|
||
Unterstützt sind:
|
||
|
||
- `set_range_bits` — Zahlenraum eines laufenden Tasks ändern,
|
||
- `set_intervals` — Tippintervalle pro Task überschreiben,
|
||
- `clear_intervals` — wieder globale Defaults verwenden,
|
||
- `pause` — Tippabgaben pausieren,
|
||
- `resume` — fortsetzen,
|
||
- `reroll` — neues Secret + neuer Public Seed, Scores/Sequenzen zurücksetzen,
|
||
- `close` — Task beenden und Ersatz-Task sicherstellen,
|
||
- `regenerate_artifact` — Gewinnerbild erneut in die Queue legen.
|
||
|
||
Damit lassen sich z. B. Difficulty-Ramps planen:
|
||
|
||
```text
|
||
20:00 24 Bit
|
||
20:15 28 Bit
|
||
20:30 32 Bit
|
||
20:45 pause
|
||
20:50 resume
|
||
```
|
||
|
||
### Zahlenraum live ändern
|
||
|
||
Es gibt zwei Modi:
|
||
|
||
**preserve**
|
||
|
||
- das geheime Ziel und der Public Seed bleiben bestehen,
|
||
- Sequenzen bleiben bestehen,
|
||
- Scores werden auf den neuen Bit-Denominator re-skaliert,
|
||
- die räumlichen Positionen werden neu berechnet,
|
||
- Verkleinern ist nur möglich, wenn das bestehende Secret in den neuen Zahlenraum passt.
|
||
|
||
**reroll**
|
||
|
||
- neues Secret,
|
||
- neuer Public Seed,
|
||
- Score/Position/Sequenz werden zurückgesetzt,
|
||
- kumulative Tippanzahl bleibt erhalten.
|
||
|
||
Die Clients empfangen `task_changed` via WebSocket und laden `range_bits`, Seed, Revision und Intervalle neu. Ein Browser verwendet deshalb nach einer Live-Änderung nicht versehentlich die alte Bitzahl.
|
||
|
||
## KI-/NFT-artige Gewinnerbilder
|
||
|
||
Ein abgeschlossenes Gewinnerbild ist ein **NFT-artiges digitales Artefakt**, aber noch kein Blockchain-NFT. Neural Hunt mintet nichts on-chain. Es erzeugt Bild + Manifest mit eindeutiger ID und kryptografischen Bindungen.
|
||
|
||
Das Manifest enthält u. a.:
|
||
|
||
```text
|
||
artifact_id
|
||
artifact_preset
|
||
task_id
|
||
task_range_bits
|
||
winner_client_id
|
||
winner_public_jwk
|
||
winning_guess
|
||
winner_guess_signature
|
||
raw_art_sha256
|
||
image_sha256
|
||
prompt_sha256
|
||
collection_character
|
||
collection_traits
|
||
layout
|
||
provider
|
||
provider_meta
|
||
```
|
||
|
||
Wichtig: Die Gewinner-Signatur authentifiziert den erfolgreichen Guess. Sie ist keine nachträgliche Signatur der vom Bildprovider erzeugten Pixel. Das erzeugte Bild wird stattdessen per `image_sha256` an das Server-Manifest gebunden.
|
||
|
||
### Provider
|
||
|
||
Das Standard-Preset `raccoon_full_art_v1` ist im Admin-Tab **ARTIFACT** bewusst auf OpenAI + Character-Anchor-Workflow vorkonfiguriert; dort muss normalerweise nur das Bildmodell geändert werden. Die älteren Provider `local`, `comfyui`, `a1111` und `auto` bleiben für `artifact_preset=legacy` kompatibel.
|
||
|
||
Provider-Secrets und Provider-URLs bleiben absichtlich ENV-only und werden nicht an den Browser ausgeliefert. Das Dashboard zeigt für OpenAI nur, ob der API-Key vorhanden ist, und ob der kanonische Character Anchor bereits erzeugt wurde.
|
||
|
||
### 1. Local
|
||
|
||
Kein externer Dienst. Erzeugt ein deterministisches SVG wie bisher.
|
||
|
||
```env
|
||
ARTIFACT_PROVIDER=local
|
||
```
|
||
|
||
### 2. OpenAI Images API — RIFT Collection (Standard)
|
||
|
||
Für das neue Full-Art-Preset reichen im Normalfall genau zwei Werte:
|
||
|
||
```env
|
||
ARTIFACT_MODEL=gpt-image-2
|
||
OPENAI_API_KEY=...
|
||
```
|
||
|
||
`OPENAI_BASE_URL` kann optional überschrieben werden; standardmäßig wird `https://api.openai.com` verwendet. Provider, Portrait-Größe (`1024x1536`), Quality (`medium`) und das Collection-Preset sind bereits als Defaults hinterlegt. Eine mit älteren Projektversionen ausgelieferte lokale 1024×1024-Artifact-Konfiguration wird beim ersten Start einmalig auf das neue Preset migriert.
|
||
|
||
Die RIFT-Quality wird außerdem einmalig von einem früheren `high`-Default auf `medium` migriert. Eine später bewusst gesetzte `high`-Einstellung wird durch diese Migration nicht erneut überschrieben.
|
||
|
||
Ablauf:
|
||
|
||
1. Im Admin-Tab **ARTIFACT** kann `/data/artifacts/_collection/character_anchor.png` einmalig manuell erzeugt werden. Der Anchor entsteht nur aus dem neutralen RIFT-Masterprompt und enthält bewusst **keinen Task-Style**. Ist beim ersten Gewinner noch kein Anchor vorhanden, erzeugt der Worker ihn weiterhin automatisch als Sicherheits-Fallback.
|
||
2. Im Admin-Tab **TASK ACTIONS** kann jeder Task ein eigenes JPEG-/PNG-Stylebild erhalten. Es wird content-addressed unter `data/artifacts/_styles/<sha256>.<ext>` gespeichert und über `tasks.nft_style_reference` dem Task zugeordnet. Ein Folge-Task erbt dieselbe Style-Referenz. Ohne Custom-Style wird das eingebettete `internal/artifact/assets/style_reference.jpg` als Default verwendet.
|
||
3. Für jede RIFT-Karte sendet Neural Hunt zwei Bildinputs an `/v1/images/edits`: **Image 1 = globaler Character Anchor (Identität)** und **Image 2 = Task Style Reference (Rendering-Look)**. Der Prompt weist das Modell explizit an, Gesicht/Fell/Proportionen aus Image 1 und Rendering-Technik/Material/Licht/Farbverhalten aus Image 2 zu übernehmen. Der Kartenprompt erzwingt dabei bewusst keinen festen 3D-Look mehr: ein Task-Style darf z. B. 3D-Cartoon, Cel-Shading, Comic, malerisch, Watercolor-artig, Clay/Toy, Low-Poly oder Retro-Game sein, solange RIFT als Charakter erkennbar und die Ausgabe nicht fotorealistisch bleibt.
|
||
4. `internal/artifact/collection.go` wählt deterministisch Theme, Outfit, Accessoires, Szene, Mood, Pose, Atmosphäre, Rarity und Akzentfarben. Task-spezifische kreative Vorgaben und Ausschlüsse bleiben optionale Overrides.
|
||
5. Das generierte PNG wird als `art.png` gespeichert. `internal/artifact/card.go` baut daraus anschließend das finale `image.svg` im Format 1024×1536. Die KI muss daher keine Karten-Typografie oder UI exakt rendern.
|
||
6. Das Manifest bindet sowohl Roh-Art als auch fertige Karte per SHA-256 und speichert Collection-Traits sowie die verwendete Task-Style-Referenz/Provider-Metadaten.
|
||
|
||
Der Character Anchor ist absichtlich global und wird im Admin nach Erstellung als **LOCKED** behandelt. Ein Wechsel des NFT-Stils erfolgt deshalb nicht mehr durch Austausch des Anchors, sondern durch ein anderes Style-Referenzbild am jeweiligen Task. Das eingebettete `style_reference.jpg` muss dafür nicht neu kompiliert oder ausgetauscht werden.
|
||
|
||
### OpenAI-Nutzung und Kosten
|
||
|
||
Jeder erfolgreiche OpenAI-Bildaufruf (Character-Anchor und Gewinnerkarte) wird lokal in `artifact_api_usage` protokolliert. Wenn OpenAI im Images-Response die Usage-Felder liefert, speichert Neural Hunt die gemeldeten Text-/Bild-Input-Tokens sowie Output-/Total-Tokens und berechnet daraus eine lokale USD-Kostenschätzung. Die Preisbasis ist im Datensatz fixiert, damit historische Werte reproduzierbar bleiben; unbekannte Modelle werden weiter protokolliert, aber ohne erfundene Kostenschätzung.
|
||
|
||
Im Adminbereich zeigt **OPENAI NUTZUNG & KOSTEN** die Kosten des heutigen lokalen Kalendertags, den durchschnittlichen Preis pro fertiger Gewinnerkarte und die hochgerechneten Kosten pro 1.000 Karten sowie die letzten API-Aufrufe. Character-Anchor-Aufrufe zählen zu den heutigen API-Kosten, werden aber bewusst nicht in den Karten-Durchschnitt eingerechnet. Die USD-Werte sind eine aus den Provider-Tokens berechnete Standardpreis-Schätzung (ohne separat ausgewiesenen Cached-Input-Rabatt) und keine Rechnungs-/Billing-Abstimmung; dafür ist kein zusätzlicher OpenAI-Admin-Key nötig. Die Rohdaten sind zusätzlich über `GET /api/admin/artifact/usage` verfügbar.
|
||
|
||
### 3. ComfyUI
|
||
|
||
```env
|
||
ARTIFACT_PROVIDER=comfyui
|
||
COMFYUI_URL=http://127.0.0.1:8188
|
||
COMFYUI_WORKFLOW_PATH=/absolute/path/workflow-api.json
|
||
ARTIFACT_MODEL=your-checkpoint.safetensors
|
||
```
|
||
|
||
Der Workflow muss im **API-Format** vorliegen. Neural Hunt ersetzt rekursiv folgende String-Platzhalter:
|
||
|
||
```text
|
||
{{PROMPT}}
|
||
{{NEGATIVE_PROMPT}}
|
||
{{SEED}}
|
||
{{WIDTH}}
|
||
{{HEIGHT}}
|
||
{{STEPS}}
|
||
{{MODEL}}
|
||
```
|
||
|
||
Beispiel in einem KSampler-/Text-Encode-Input:
|
||
|
||
```json
|
||
{
|
||
"text": "{{PROMPT}}",
|
||
"seed": "{{SEED}}",
|
||
"steps": "{{STEPS}}"
|
||
}
|
||
```
|
||
|
||
Der Worker sendet den Workflow an `/prompt`, pollt `/history/{prompt_id}` und lädt das erste gefundene Ergebnis über `/view`.
|
||
|
||
### 4. AUTOMATIC1111
|
||
|
||
A1111 muss mit API gestartet werden, z. B. `--api`. Optional ist `--api-auth user:pass` möglich.
|
||
|
||
```env
|
||
ARTIFACT_PROVIDER=a1111
|
||
A1111_URL=http://127.0.0.1:7860
|
||
A1111_USER=
|
||
A1111_PASSWORD=
|
||
ARTIFACT_MODEL=my-checkpoint.safetensors
|
||
ARTIFACT_STEPS=28
|
||
A1111_CFG_SCALE=7
|
||
```
|
||
|
||
Neural Hunt verwendet `/sdapi/v1/txt2img`, übernimmt das Base64-Ergebnis und speichert es lokal. Ist `ARTIFACT_MODEL` kein `gpt-image-*`-Name, wird es für A1111 als `sd_model_checkpoint` pro Request gesetzt.
|
||
|
||
### 5. Auto
|
||
|
||
```env
|
||
ARTIFACT_PROVIDER=auto
|
||
```
|
||
|
||
Reihenfolge:
|
||
|
||
```text
|
||
OpenAI (wenn Key gesetzt)
|
||
→ ComfyUI (wenn URL + Workflow gesetzt)
|
||
→ A1111 (wenn URL gesetzt)
|
||
→ lokales SVG
|
||
```
|
||
|
||
Fehler der fehlgeschlagenen Provider werden im Manifest des lokalen Fallbacks vermerkt.
|
||
|
||
### Docker + lokale ComfyUI/A1111-Instanz
|
||
|
||
`docker-compose.yml` enthält `host.docker.internal:host-gateway`. Dadurch kann der App-Container unter Linux/Windows/macOS typischerweise einen Bildserver auf dem Docker-Host erreichen:
|
||
|
||
```env
|
||
COMFYUI_URL=http://host.docker.internal:8188
|
||
A1111_URL=http://host.docker.internal:7860
|
||
```
|
||
|
||
Ein ComfyUI-Workflow, den der Container lesen soll, muss zusätzlich in den Container gemountet bzw. unter `/data` abgelegt werden.
|
||
|
||
## Architektur
|
||
|
||
```text
|
||
Browser
|
||
├─ Client UI + P-256 Identity
|
||
├─ /leaderboard
|
||
└─ Admin Control Plane
|
||
│ HTTP / WS
|
||
▼
|
||
+------------------------------------------+
|
||
| Go Binary |
|
||
| auth | tasks | scheduler | WS | artifact |
|
||
+--------------------+---------------------+
|
||
│
|
||
modernc.org/sqlite
|
||
/data/neuralhunt.db
|
||
│
|
||
└─ /data/artifacts/
|
||
└─ artifact_<id>/
|
||
├─ image.png|svg
|
||
└─ manifest.json
|
||
|
||
Optional nur für Bildgenerierung:
|
||
OpenAI API ODER ComfyUI ODER A1111
|
||
```
|
||
|
||
## V2.4 Scale-Architektur
|
||
|
||
Die Standalone-Edition bleibt bewusst **ein Go-Prozess auf einem Host**. Dafür ist der heiße Pfad jetzt konsequent aus SQLite herausgezogen:
|
||
|
||
```text
|
||
normaler falscher Guess
|
||
-> JWT / Signatur / deterministischen Guess prüfen
|
||
-> Sequence + Rate Limit + GuessCount im RAM
|
||
-> Score berechnen
|
||
-> schlechter als Bestwert
|
||
-> Antwort false
|
||
|
||
SQLite writes: 0
|
||
Map events: 0
|
||
```
|
||
|
||
Nur Registrierung, erstmaliger Task-Punkt, Score-Verbesserungen, Gewinner, Unlocks, Admin-/Task-Änderungen und Artefakte werden dauerhaft geschrieben. Presence (`eine ID = eine aktive Verbindung`) liegt ebenfalls im Prozess und verursacht keine regelmäßigen SQLite-Lease-Writes mehr. Die alte Tabelle `presence_leases` kann aus Kompatibilitätsgründen in bestehenden Datenbanken vorhanden bleiben, wird aber vom V2.4-Hotpath nicht genutzt.
|
||
|
||
### Gebatchte WebSockets
|
||
|
||
Verbesserungen werden pro Task und Client koalesziert und alle **250 ms** als ein `points`-Frame gesendet. Jeder Socket hat eine bounded Outbound-Queue; ein langsamer Browser blockiert daher niemals einen Guess-Request. Ist seine Queue voll, darf ein transienter Map-Frame verworfen werden. Der persistierte Zustand bleibt maßgeblich.
|
||
|
||
### Server-seitig begrenzte Map-Snapshots
|
||
|
||
Der Browser lädt nicht mehr pauschal 50.000 Nodes. `max_nodes` wird an den WebSocket übergeben; der Server liefert höchstens `3 × max_nodes` Overscan (hart gedeckelt auf 10.000) und garantiert, dass die eigene Node enthalten ist. Nachfolgende Events werden clientseitig ebenfalls auf dieses Overscan-Budget gekürzt. Das vorhandene LOD arbeitet nur noch auf diesem begrenzten Working Set.
|
||
|
||
### Performance-Telemetrie
|
||
|
||
`GET /api/admin/performance` liefert live u. a. `Guess/s`, `Improvements/s`, `SQLite Writes/s`, verworfene Requests, WebSocket Frames/s, WebSocket Bytes/s, Queue-Drops, Goroutines und Heap. Diese Werte erscheinen zusätzlich im Admin-Dashboard.
|
||
|
||
### Lastgenerator
|
||
|
||
Der integrierte Lastgenerator erzeugt echte P-256-Identitäten, führt Challenge/Login aus, öffnet WebSockets und sendet deterministische signierte Tipps:
|
||
|
||
```bash
|
||
go run ./cmd/loadtest \
|
||
-url http://127.0.0.1:8080 \
|
||
-clients 10000 \
|
||
-ramp 60s \
|
||
-duration 5m \
|
||
-max-nodes 250
|
||
```
|
||
|
||
Für einen realen 10k-Test sollten Server und Lastgenerator nach Möglichkeit auf getrennten Hosts laufen. Die erreichbare Obergrenze hängt von CPU, RAM, TLS, Dateideskriptoren und Netzwerk ab; V2.4 setzt **10.000 gleichzeitige Clients als Lasttest-Ziel**, nicht als hardwareunabhängige Garantie.
|
||
|
||
## SQLite und Skalierungsgrenze
|
||
|
||
SQLite läuft mit Foreign Keys, WAL, `busy_timeout` und kurzen Transaktionen. Weil normale falsche Tipps SQLite nicht mehr berühren, ist die Single-Writer-Eigenschaft deutlich weniger dominant. Für echtes Multi-Host-Horizontal-Scaling wäre trotzdem wieder eine gemeinsame Koordinations-/Datenbankschicht nötig.
|
||
|
||
## Mehrere lokale Test-Clients
|
||
|
||
Die IP-Adresse ist nicht Teil der Identität. Mehrere Clients auf `127.0.0.1` funktionieren. Sie brauchen lediglich unterschiedliche Browser-Identitäten, z. B. normales Fenster + Inkognito oder getrennte Browserprofile.
|
||
|
||
Zwei Tabs desselben Profils teilen `localStorage` und damit dieselbe ID. Pro ID ist weiterhin nur eine aktive Client-WebSocket-Verbindung erlaubt.
|
||
|
||
## Persistenz / Backup
|
||
|
||
Relevant sind:
|
||
|
||
```text
|
||
/data/neuralhunt.db
|
||
/data/artifacts/
|
||
```
|
||
|
||
In SQLite liegen u. a.:
|
||
|
||
```text
|
||
clients
|
||
tasks
|
||
task_points
|
||
settings
|
||
presence_leases
|
||
task_actions
|
||
```
|
||
|
||
## API-Überblick
|
||
|
||
User:
|
||
|
||
```text
|
||
POST /api/auth/challenge
|
||
POST /api/auth/login
|
||
GET /api/tasks/current
|
||
POST /api/tasks/{id}/guess
|
||
GET /api/tasks/{id}/points
|
||
GET /api/me
|
||
GET /api/leaderboard
|
||
GET /api/ws
|
||
```
|
||
|
||
Public:
|
||
|
||
```text
|
||
GET /api/public/leaderboard
|
||
WS /api/leaderboard/ws
|
||
```
|
||
|
||
Admin:
|
||
|
||
```text
|
||
POST /api/admin/login
|
||
GET /api/admin/overview
|
||
GET /api/admin/performance
|
||
GET /api/admin/settings
|
||
PUT /api/admin/settings
|
||
GET /api/admin/tasks
|
||
GET /api/admin/tasks/{id}/points
|
||
GET /api/admin/tasks/{id}/actions
|
||
POST /api/admin/tasks/{id}/actions
|
||
POST /api/admin/actions/{id}/cancel
|
||
GET /api/admin/artifact/providers
|
||
POST /api/admin/tasks/{id}/close
|
||
POST /api/admin/tasks/ensure
|
||
```
|
||
|
||
## Produktions-Hinweise
|
||
|
||
- TLS erzwingen.
|
||
- `JWT_SECRET` und Admin-Passwort ändern.
|
||
- `CheckOrigin` für WebSockets auf eine Origin-Allowlist begrenzen.
|
||
- `/data` sichern und freien Speicher überwachen.
|
||
- API-Keys nur als ENV/Secret injizieren, niemals in Runtime-Settings speichern.
|
||
- Bei ComfyUI/A1111 nur vertrauenswürdige lokale/netzinterne Instanzen konfigurieren.
|
||
- Datenschutz-/Einwilligungstexte für das soziale Experiment ergänzen.
|
||
|
||
## V2.9: lokaler RIFT-Pipeline-Test
|
||
|
||
Im Adminbereich unter **TASK ACTIONS** kann für den ausgewählten Task eine komplette lokale Testkarte erzeugt werden. Der Test verwendet den vorhandenen `character_anchor.png` als Mock-Character-Artwork und die Style-Referenz des Tasks als Hintergrund, führt die deterministische Trait-Auswahl, das programmatische Kartenlayout und das Schreiben der Testdateien aus, ruft aber **keine externe Bild-API** auf. Die Dateien landen unter `data/artifacts/_test/<task-id>/`; der echte Task- und Artifact-Status bleibt unverändert.
|