All checks were successful
release-tag / release-image (push) Successful in 3m57s
766 lines
45 KiB
Markdown
766 lines
45 KiB
Markdown
> **V4.5 Place Reward Economy:** Neural Place vergütet jetzt zusätzlich tatsächlich gezogene Lotterie-Tipps und aktive, signierte Hunt-Zeit. Draw-Rewards können automatisch mit dem effektiven Beacon-Pfadgewicht multipliziert werden; aktive Zeit wird nur zwischen gültigen Requests innerhalb einer konfigurierbaren Maximalpause aufgebaut, sodass kein Offline-/Idle-Backfill entsteht. Alle Quellen gelten auch für verknüpfte Hosted Worker und landen im Owner-Wallet. Admins können Place-Punkte direkt im RUNTIME-Portal vergeben; Draw-, Zeit- und Admin-Boni besitzen ein dauerhaftes Audit. Details: `V4.5_PLACE_REWARD_ECONOMY.md`.
|
||
|
||
> **V4.4 Neural Place:** Neural Hunt besitzt jetzt eine globale, r/place-inspirierte Pixel-Leinwand unter `/place`. Echte Score-Steigerungen erzeugen Place-Punkte; Fortschritte verknüpfter Hosted Worker werden dem Owner-Wallet zugerechnet. Placements kosten Punkte statt Zeit-Cooldown und werden live über einen eigenen WebSocket synchronisiert. Wallet, Audit-Historie, Canvas und Placement-History liegen dauerhaft in SQLite. Details: `V4.4_NEURAL_PLACE.md`.
|
||
|
||
> **V4.2.9 Worker Recovery & Image Update:** Hosted Worker erkennen halb-offene Game-WebSockets per 90s Client-Deadline, erneuern abgelaufene Game-JWTs automatisch mit derselben persistenten P-256-Identity und wiederholen den Wechsel auf einen Folge-Task, bis er verfügbar ist. Hosted Worker fallen bei einem späteren Container-Neustart außerdem von einer inzwischen inaktiven gespeicherten Task-ID auf die serverseitig ausgewählte aktive Task zurück. Neue Worker-Container verwenden `RestartPolicy=unless-stopped`; eine explizite Lease-Sperre kann diese Recovery nicht umgehen. Im privaten Customer Admin kann `CS_WORKER_IMAGE` explizit neu gepullt und ein einzelner oder alle bestehenden Worker unter Beibehaltung ihrer Identity-Volumes neu erzeugt werden. Details: `V4.2.9_WORKER_RECOVERY_IMAGE_UPDATE.md`.
|
||
|
||
> **V4.2.8 Admin Control & Ownership:** Customer-Admins können das Worker-Limit pro Benutzer aufheben und Worker direkt zuordnen. Server-Admins können Task-Serien als AUSLAUFEND markieren, Collectible-Eigentum ohne Umschreiben der historischen Gewinner-Provenienz übertragen und zufällige ADMIN-DROP-NFTs für bestehende Neural-Hunt-Identitäten erzeugen. Details: `V4.2.8_ADMIN_CONTROL_OWNERSHIP.md`.
|
||
|
||
> **V4.2.7 Admin Live Guess Flash:** In der privaten Server-Admin-3D-Map kann `TIPPS %` optional den Score jedes tatsächlich ausgewerteten Tipps kurz am Client-Knoten einblenden – auch wenn er unterhalb des bisherigen Highscores liegt. Die flüchtigen Werte laufen ausschließlich über einen admin-authentifizierten WebSocket und werden nicht an Spieler oder das öffentliche Leaderboard gesendet. Details: `V4.2.7_ADMIN_GUESS_FLASH.md`.
|
||
|
||
> **V4.2.6.1 Build Fix:** Das erste V4.2.6-Release-Archiv enthielt versehentlich nicht `internal/data/`. Dadurch konnte neuer Server-Code mit einem alten Data-Package kombiniert werden und der Server-Build scheiterte mit fehlenden `HostedCreditEvent`/Outbox-Methoden. Dieses Archiv enthält den vollständigen Data-Layer. Details: `V4.2.6.1_BUILD_FIX.md`.
|
||
|
||
> **V4.2.6 Customer Engagement + Admin Controls:** Der Hosted Customer Service kann neuen Konten ein konfigurierbares Startguthaben geben und Hosted Workern für echte persönliche Best-Score-Verbesserungen konfigurierbare Bonus-Credits gutschreiben. Das Customer Portal blendet PayPal vollständig aus, wenn PayPal deaktiviert ist, und fasst den Credit-Verlauf kompakt nach Tag/Buchungsart zusammen. Im privaten Customer-Admin gibt es Benutzer sperren/freigeben, Worker-Stop, Login-/Registrierungs-Kill-Switches, Registrierungs-Proof-of-Work und optionale einmalige Invite-Codes. Positive-Tip-Rewards werden über eine persistente Game-Outbox idempotent an den Customer Service zugestellt. Details: `V4.2.6_CUSTOMER_ENGAGEMENT_ADMIN.md`.
|
||
|
||
> **V4.2 Pipeline Dockerfiles:** Server, Customer Service und Worker besitzen jetzt jeweils ein eigenes Dockerfile (`Dockerfile.server`, `Dockerfile.customer-service`, `Dockerfile.worker`). Das bestehende `Dockerfile` baut weiterhin den Server, damit vorhandene Single-Image-Pipelines kompatibel bleiben. `CS_WORKER_IMAGE` zeigt weiterhin direkt auf das veröffentlichte Worker-Image.
|
||
|
||
> **V4.0 Beacon Hunt + Hosted PrePaid Service:** Optional kann die Task-Lotterie jetzt PULSE/FLUX/ORBIT als vorab signierte Spielerentscheidung verwenden. Der Draw nutzt einen erst nach Fensterschluss verfügbaren drand-Round und speichert Round, Signatur, abgeleitete Randomness und Boost für Audit/Collectible-Traits. Zusätzlich gibt es einen separat aktivierbaren Customer-Service mit PrePaid-Zeitabrechnung, PayPal-Sandbox/Orders-v2-Flow, mehreren Docker-Workern pro Kunde, portablen Worker-Identitäten und delegiertem Reward-Owner. Details: `HOSTED_SERVICE.md`.
|
||
|
||
> **V3.9 Portable Identity + Winner Originals:** Der Shell-Client verwendet standardmäßig dauerhaft `~/.neuralhunt/identity.json` (`0600`) und kann dieselbe P-256-Identität als passwortgeschützten, browser-kompatiblen JSON-Export sichern. Browser und CLI validieren beim Import Public/Private-Key-Paar und Client-ID. Nach dem Import derselben Identität sieht der Browser wieder dieselben serverseitig gebundenen Wins. Authentifizierte Gewinner erhalten unter **MEINE NFTS** Zugriff auf ihr unverändertes Original-Artefakt; andere Identitäten erhalten dafür nur `404`. Der CLI besitzt dafür `my-nfts` und `nft original <task-id> <datei>`. Öffentliche Leaderboards bleiben weiterhin ausschließlich bei Wasserzeichen-Previews.
|
||
|
||
> **V3.5 Random Guess Lottery:** Optional kann die Zahl der tatsächlich ausgewerteten Tipps pro Zeitfenster begrenzt werden. Im Admin-Tab **RUNTIME** steuern `Lotterie-Zeitfenster (s)` und `Max. gezogene Tipps je Task/Fenster` die Funktion; `0` deaktiviert sie vollständig. Die Lotterie läuft getrennt pro aktivem Task. Alle gültigen, signierten Tipps werden bis zum Ende des Zeitfensters gesammelt. Im normalen Lotterie-Modus werden sie anschließend mit `crypto/rand` gleichberechtigt zufällig gezogen; bei aktiviertem Beacon Hunt übernimmt stattdessen der erst nach Fensterschluss verfügbare drand-Reveal die deterministische gewichtete Ziehung. Nur gezogene Tipps werden gegen das geheime Ziel ausgewertet und können Score/Winner/NFT auslösen. Nicht gezogene Tipps verbrauchen ihre Sequenz, damit beim nächsten Fenster ein neuer deterministischer Tipp entsteht, zählen aber nicht als akzeptierter `guess_count`. Abgebrochene HTTP-Requests verbrauchen keinen Lotterie-Slot. Dadurch wird die mögliche Task-Abschluss-/NFT-Rate gedrosselt, ohne Gewinner oder Styles direkt zu manipulieren.
|
||
|
||
# Neural Hunt — V4.5 Place Reward Economy + Service Controller
|
||
|
||
|
||
> **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, niemals Gewinner eines Tasks waren und keine dauerhafte Neural-Place-Ökonomie besitzen. Gewinner, Place-Teilnehmer, verknüpfte Worker/Owner und aktuell verbundene Clients werden geschützt. 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.
|
||
|
||
## HTTPS / Browser-WebCrypto
|
||
|
||
Die Weboberfläche erzeugt ihre ECDSA-P-256-Identität direkt im Browser über die Web Crypto API. Browser stellen `crypto.subtle` für normale Remote-Hosts nur in einem sicheren Kontext bereit. Verwende die Weboberfläche deshalb produktiv über **HTTPS**. `http://localhost` bzw. Loopback-Adressen werden von Browsern für lokale Entwicklung üblicherweise als vertrauenswürdiger Kontext behandelt. Bei einem Aufruf über `http://<IP-or-domain>` zeigt Neural Hunt eine verständliche HTTPS-Fehlermeldung statt eines JavaScript-Fehlers.
|
||
|
||
|
||
## 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`
|
||
- Neural Place: `http://localhost:8080/place`
|
||
- Admin (private listener): `http://localhost:8081/admin`
|
||
|
||
### V4.1: drei Docker-Images
|
||
|
||
Das Runtime-Image ist jetzt nach Rollen getrennt:
|
||
|
||
```text
|
||
neuralhunt-server:local -> Game/API + Game-Admin
|
||
neuralhunt-customer-service:local -> Customer Portal / Billing / Docker-Control
|
||
neuralhunt-worker:local -> CLI-Agent / Hosted Worker
|
||
```
|
||
|
||
Alle drei lokal bauen:
|
||
|
||
```bash
|
||
docker build -f Dockerfile.server -t neuralhunt-server:local .
|
||
docker build -f Dockerfile.customer-service -t neuralhunt-customer-service:local .
|
||
docker build -f Dockerfile.worker -t neuralhunt-worker:local .
|
||
# oder ohne Bake:
|
||
docker compose --profile images build app customer-service worker-image
|
||
```
|
||
|
||
Für Registry-Tags:
|
||
|
||
```bash
|
||
export NEURALHUNT_SERVER_IMAGE=registry.example.com/neuralhunt/server:v4.1
|
||
export NEURALHUNT_CUSTOMER_IMAGE=registry.example.com/neuralhunt/customer-service:v4.1
|
||
export CS_WORKER_IMAGE=registry.example.com/neuralhunt/worker:v4.1
|
||
# In der CI/CD-Pipeline jeweils das passende Dockerfile bauen und pushen.
|
||
```
|
||
|
||
Der Customer Service verwendet **genau** `CS_WORKER_IMAGE` für dynamisch
|
||
erzeugte Worker. Mit `CS_WORKER_AUTO_PULL=true` darf er ein fehlendes Image bei
|
||
Bedarf über Docker Engine nachladen. Für private Registries können dafür
|
||
`CS_WORKER_REGISTRY_SERVER`, `CS_WORKER_REGISTRY_USERNAME` und
|
||
`CS_WORKER_REGISTRY_PASSWORD` mit einem read-only Deploy-Token gesetzt werden.
|
||
|
||
|
||
## 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.
|
||
|
||
Diese Datei **ist die Identität** des Shell-Clients. Solange sie erhalten bleibt, bleibt auch die daraus abgeleitete Client-ID identisch. Für Container/systemd sollte sie deshalb auf einem persistenten Volume bzw. Host-Pfad liegen. Die Raw-Datei enthält den privaten Schlüssel im Klartext und sollte nicht verteilt werden; für Backups und den Wechsel in den Browser immer den verschlüsselten Export verwenden.
|
||
|
||
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] öffentliche Winner-Artefakte mit Wasserzeichen-URLs
|
||
my-nfts [n] eigene fertige Gewinner-Artefakte
|
||
nft get <task-id> <datei> öffentliche Wasserzeichen-Preview speichern
|
||
nft original <task-id> <datei> eigenes unverändertes Original speichern
|
||
identity Client-ID + persistente Identity-Datei
|
||
identity export <datei> browser-kompatiblen verschlüsselten Backup-Export schreiben
|
||
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"
|
||
```
|
||
|
||
### CLI als dediziertes Worker-Image testen
|
||
|
||
Seit V4.1 enthält das Server-Image absichtlich nur noch den Game-Server. Baue das
|
||
Worker-Image separat und mounte für die Identität ein persistentes Verzeichnis:
|
||
|
||
```bash
|
||
docker compose --profile images build worker-image
|
||
mkdir -p ./client-identities
|
||
|
||
docker run --rm -it \
|
||
--network neuralhunt_backend \
|
||
-v "$PWD/client-identities:/identity" \
|
||
neuralhunt-worker:local \
|
||
-url http://app:8080 \
|
||
-identity /identity/test-cli.json
|
||
```
|
||
|
||
Bei jedem weiteren Start mit genau diesem Pfad wird dieselbe Client-ID verwendet.
|
||
Für einen browser-kompatiblen verschlüsselten Export:
|
||
|
||
```bash
|
||
export NH_PASS='correct horse battery staple'
|
||
docker run --rm \
|
||
-e NEURALHUNT_IDENTITY_PASSPHRASE="$NH_PASS" \
|
||
-v "$PWD/client-identities:/identity" \
|
||
neuralhunt-worker:local \
|
||
-identity /identity/test-cli.json \
|
||
-export /identity/test-cli-browser.json
|
||
unset NH_PASS
|
||
```
|
||
|
||
`client-identities/test-cli-browser.json` kann anschließend im Web-Client unter
|
||
**IDENTITÄT → IMPORT** eingelesen werden. Die Raw-Datei `test-cli.json` sollte
|
||
den Host nicht unverschlüsselt verlassen.
|
||
|
||
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
|
||
```
|
||
|
||
Der Export ist selbstbeschreibend (`neuralhunt-identity-export`), enthält die öffentliche Client-ID und verschlüsselt das eigentliche Schlüsselmaterial mit PBKDF2-HMAC-SHA256 (250.000 Iterationen) + AES-256-GCM. Neue Exporte verlangen mindestens 12 Zeichen Passphrase. Im Browser unter **IDENTITÄT → IMPORT** Datei auswählen, dieselbe Passphrase eingeben und den Identitätswechsel bestätigen. Danach meldet sich der Browser mit derselben Client-ID an.
|
||
|
||
Unter **MEINE NFTS** werden dann die fertigen Gewinner-Artefakte dieser Identität angezeigt. Nur der authentifizierte Gewinner darf das Original herunterladen; die öffentliche Galerie bleibt wassergezeichnet.
|
||
|
||
**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. Die Rarity-Verteilung (Common / Uncommon / Rare / Ultra Rare / Special Illustration Rare) ist im Server-Admin unter ARTIFACT prozentual konfigurierbar; die Summe muss 100 % ergeben. Die tatsächlich zugewiesene Rarity wird am Collectible persistiert. 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.
|
||
|
||
|
||
### OpenAI reference-image MIME handling
|
||
|
||
Reference images sent to the OpenAI Images Edit endpoint are uploaded with an explicit per-part MIME type (`image/png`, `image/jpeg` or `image/webp`). This is required because Go's `multipart.CreateFormFile` otherwise labels file parts as `application/octet-stream`, which the Images API rejects. The character anchor is always sent as `image/png`; task style references preserve their detected image MIME type.
|
||
|
||
|
||
|
||
## Public-hosting hardening (V3.8)
|
||
|
||
Neural Hunt now starts two HTTP listeners. `HTTP_ADDR` (default `:8080`) is the public client/leaderboard API and deliberately returns 404 for `/admin` and `/api/admin/*`. `ADMIN_HTTP_ADDR` (default `:8081`) is the private control plane. Keep port 8081 behind a VPN/private reverse-proxy route; the supplied Compose file binds it to host loopback while other containers on the same Docker network can still reach `app:8081`.
|
||
|
||
Admin authentication uses an `HttpOnly`, `SameSite=Strict` session cookie instead of browser `localStorage`. The cookie is marked `Secure` whenever TLS is visible directly or through `X-Forwarded-Proto: https`. The application also emits CSP, anti-clickjacking, HSTS and related browser hardening headers. Public browser WebSockets are same-origin checked; optional extra origins can be listed in `WS_ALLOWED_ORIGINS`. User WebSocket bearer tokens are no longer put in the URL by the bundled web/CLI clients.
|
||
|
||
New identities can be given one-time Hashcash-style proof-of-work plus a short warm-up before guesses are eligible (`sybil_pow_bits`, `sybil_warmup_sec` in Admin → Runtime). This is deliberate Sybil *friction*, not proof of a real-world person; serious adversaries can still buy compute or distribute identities. Reverse-proxy request/IP controls remain recommended.
|
||
|
||
Before every OpenAI image request the worker checks rolling 1-hour/24-hour call caps and a rolling estimated 24-hour USD budget. If the breaker trips, a winner artifact is returned to `pending` with a diagnostic message and is retried after the hold period rather than spending more immediately. Configure `openai_max_calls_1h`, `openai_max_calls_24h`, `openai_max_cost_24h_usd`, and `openai_budget_reserve_usd` in Admin → Runtime. Successful provider usage remains the basis for the local estimate.
|
||
|
||
For public operation, `JWT_SECRET` must be a unique random string of at least 32 characters and `ADMIN_PASSWORD` must be at least 16 characters; known development defaults make startup fail. `ALLOW_INSECURE_DEV_DEFAULTS=1` exists only for local throwaway development.
|
||
|
||
## Distributed Worker Hosts / Service Controller (V4.3)
|
||
|
||
For larger hosted installations, Customer Service no longer has to run every worker on its own Docker host. `cmd/service-controller` is a private worker-host agent that owns Docker access on a remote server, registers at Customer Service, and receives the existing worker lifecycle operations from the Master.
|
||
|
||
The deployment remains backward compatible through `CS_WORKER_ORCHESTRATION_MODE=direct|controller|hybrid`. Use `hybrid` when upgrading an installation with existing local identity volumes; use `controller` once Customer Service should no longer receive `docker.sock` at all.
|
||
|
||
Build the fourth image with `Dockerfile.service-controller`. Configuration, VPN topology, controller emergency UI and failure behavior are documented in `SERVICE_CONTROLLER.md`.
|