26 KiB
Validation — V2.5.1 Reconnect / Presence / Heartbeat
Regression: regelmäßiger HTTP 409 nach ~90 Sekunden
- Browser-Client mit einem aktiven Task verbinden und mindestens 3 Minuten ohne F5 laufen lassen.
- Prüfen, dass Auto-Guesses über die frühere 90-Sekunden-Grenze hinweg weiterlaufen.
- DevTools → Network → WS beobachten: der Server sendet echte WebSocket-Ping-Control-Frames; Browser-Pongs verlängern die Read-Deadline.
- Netzwerk für einige Sekunden trennen und wieder aktivieren. Der Browser muss automatisch reconnecten und ohne F5 weitertippen.
- Während eines Task-Wechsels bzw. schnellen Reconnects darf ein verspätetes
ReleasePresenceder alten Verbindung die neue Presence nicht entfernen. Das wird zusätzlich durchTestPresenceReconnectOldReleaseCannotDeleteNewLeasegeprüft. - Shell-Client ebenfalls mindestens 3 Minuten laufen lassen, Verbindung kurz unterbrechen und prüfen, dass
[ws] wieder verbundenerscheint und Guessing fortgesetzt wird.
Maschinenlesbare 409-Codes:
presence_required
sequence_mismatch
task_config_changed
selection_conflict
task_inactive
V2.5 — Task-Auswahl, Vererbung und Shell-Client
Browser-Landing-Page
- Server mit mindestens zwei aktiven Tasks starten (
ACTIVE_TASK_COUNT=2). /in einem neuen Browserprofil öffnen.- Prüfen, dass vor der 3D-Ansicht die Task-Landing-Page erscheint.
- Einen Task wählen und prüfen, dass WebSocket/Auto-Guess starten.
- TASKS wählen, einen anderen Task anklicken und prüfen, dass keine
identity already connected-Meldung entsteht. - Browser neu laden: die serverseitig zuletzt gewählte Task-Karte muss als ausgewählt markiert sein.
Task-spezifisches NFT-Prompt + Admin-Draft
/admin→ TASK → Task auswählen.- Anzeigename, Beschreibung, NFT-Prompt-Anweisungen und Negative Prompt eintippen, noch nicht speichern.
- Mindestens einen Auto-Refresh-Zyklus (>3 s) abwarten und danach F5 drücken.
- Prüfen, dass die noch nicht gespeicherten Eingaben weiter im Formular stehen.
- TASK-KONFIG SPEICHERN verwenden und anschließend neu laden; nun müssen die Werte vom Server kommen.
- Task gewinnen oder über Admin
closebeenden. - Folge-Task öffnen und prüfen, dass
range_bits, task-spezifische Intervalle, Anzeigename, Beschreibung und beide Prompt-Felder identisch geerbt wurden.
Shell-Client interaktiv
go run ./cmd/client -url http://127.0.0.1:8080
Prüfen:
tasks
status
map 25
leaderboard 20
nfts 10
use 2
status
Der Shell-Client muss weiter automatisch Tipps senden. Nach use darf der Server die neue WebSocket-Verbindung nicht wegen der alten Presence ablehnen.
Shell-Client unbeaufsichtigt
go run ./cmd/client \
-url http://127.0.0.1:8080 \
-non-interactive \
-task 1 \
-max-nodes 250
Mindestens zwei Tippintervalle laufen lassen und im Admin prüfen, dass Guess-/Score-Aktivität vorhanden ist. Dann Ctrl+C; die Presence muss freigegeben werden.
Browser-/Shell-Identity-Portabilität
Im Browser eine Identity exportieren. Danach Browser-Tab schließen, damit die Single-Connection-Lease frei ist:
export NEURALHUNT_IDENTITY_PASSPHRASE='test-passphrase'
go run ./cmd/client \
-import ./neuralhunt-identity-....json \
-identity ./tmp-shell-identity.json
Die Client-ID muss mit der Browser-ID übereinstimmen.
Umgekehrt:
export NEURALHUNT_IDENTITY_PASSPHRASE='test-passphrase'
go run ./cmd/client \
-identity ./tmp-shell-identity.json \
-export ./tmp-browser-import.json
Diese Datei anschließend über Import in einem Browserprofil laden; die Client-ID muss identisch bleiben.
Automatische Tests
Zusätzlich zu den bisherigen Tests:
go test ./cmd/client
enthält PBKDF2-Testvektor sowie Raw-/verschlüsselten Identity-Roundtrip. internal/data/successor_test.go prüft mit einem echten SQLite-Treiber die Task-Vererbung, Idempotenz des Nachfolgers und das atomare Migrieren der Client-Auswahl.
In dieser Erstellungsumgebung erfolgreich ausgeführt:
node --check internal/webui/dist/app.js
gofmt -w cmd/client/*.go internal/data/store.go internal/server/server.go internal/artifact/worker.go
# kompletter Source-Compile mit lokalen API-Stubs für die drei nicht ladbaren Module
go test -run '^$' ./...
# echte Shell-Client-Krypto-/Identity-Tests; nur der WebSocket-Import ist dabei gestubbt
go test ./cmd/client
Zusätzlich wurde eine V2.4-artige SQLite-Datenbank mit Python/SQLite angelegt und die Upgrade-Reihenfolge aus OpenSQLite() reproduziert. Dabei wurden die fünf neuen Task-Spalten, client_task_selection und der erst nach parent_task_id angelegte tasks_parent_unique_idx erfolgreich erzeugt. Das ist relevant, weil der Index bei Bestandsdatenbanken nicht vor dem ALTER TABLE angelegt werden darf.
Ergebnis dieser ausführbaren Prüfungen: erfolgreich.
Statisch geprüft / implementiert
- UI ist weiterhin vollständig unter
internal/webui/dist/eingebettet; kein npm/Vite-Build nötig. /leaderboardist eine SPA-Route und besitzt einen öffentlichen Live-WebSocket.- Mobile Mode ist für Client, Leaderboard und Admin implementiert; Admin bietet mobile MAP/TASKS/CONTROL-Navigation.
- Das Leaderboard lädt Gewinner-Artefakte ausschließlich über
/api/public/artifacts/{task}/preview. - Die öffentlichen Leaderboard-Daten enthalten NFT-Anzahl und letzte Wasserzeichen-Preview, aber keine Original-Artifact-URI.
- Der frühere öffentliche
/artifacts/*-File-Server ist entfernt; Original und Manifest benötigen ein Admin-JWT. - Raster-Previews werden serverseitig mit einem eingebrannten Bitmap-Wasserzeichen neu als PNG codiert; SVG-Previews erhalten eine wiederholte Wasserzeichenebene.
- TARGET FIELD bildet Score monoton und direkt auf den Orbitalradius ab; Kameratiefe kann die wahrgenommene Nähe nicht mehr umkehren.
- Score-Ringe: 0 / 25 / 50 / 75 / 90 / 95 / 99+; Vorder- und Rückhälfte werden zur Tiefenwahrnehmung unterschiedlich gerendert.
- Pseudozufällige Client-zu-Client-Synapsen sind vollständig entfernt.
- SIGNALWEGE verbinden ausschließlich Top-Kandidaten/eigenen Client mit dem Task-Core; Score-Verbesserungen erzeugen nur entlang dieses realen Pfads Signalimpulse.
- Client lädt bei
task_changedRange Bits, Seed, Revision, Intervalle und Punkte neu. - Client-Autosubmit verwendet keinen statischen
setIntervalmehr, sondern die jeweils aktuelle Task-Konfiguration. - Task-Aktionen werden in
task_actionspersistiert, jede Sekunde geprüft und mit Status/Audit gespeichert. - Alte SQLite-Datenbanken erhalten die neuen
tasks-Spalten perALTER TABLE-Migration. set_range_bitsunterstütztpreserveundreroll.- Task-spezifische Tippintervalle überschreiben globale Runtime-Defaults.
- OpenAI-, ComfyUI- und A1111-Provider speichern nur lokal erzeugte Ergebnisbytes + Manifest; kein S3 notwendig.
- OpenAI-Provider validiert für
gpt-image-2die dokumentierten Dimensionsgrenzen, bevor ein Request gesendet wird. - Provider-Secrets/URLs werden nicht über die Runtime-Settings-API exponiert.
- ComfyUI unterstützt Workflow-Platzhalter für Prompt/Negative Prompt/Seed/Größe/Steps/Modell.
- A1111 unterstützt optional Basic Auth und Checkpoint-Override pro Request.
In dieser Umgebung nicht vollständig ausführbar
go test ./... und go mod tidy können hier nicht abgeschlossen werden, weil ausgehender DNS/Netzwerkzugriff auf proxy.golang.org gesperrt ist. Der Versuch scheitert beim Download dieser bereits im go.mod referenzierten Module:
modernc.org/sqlite
github.com/go-chi/chi/v5
github.com/gorilla/websocket
Dadurch konnten internal/data, internal/server, internal/ws und das komplette Binary hier nicht gegen frisch heruntergeladene Dependencies gebaut werden. Die direkt von ihnen unabhängigen geänderten Go-Packages wurden kompiliert/getestet.
Docker ist in der Erstellungsumgebung ebenfalls nicht verfügbar.
Empfohlene Tests auf deinem Rechner
go mod tidy
go test ./...
go run ./cmd/server
Dann:
/öffnen und prüfen, dass TARGET FIELD standardmäßig aktiv ist. Einen Score-90-, Score-95- und Score-99-Punkt vergleichen: 99 muss unabhängig von Orbit/Neigung sichtbar auf dem kleinsten Ring liegen.ORBITlaufen lassen und die Ansicht ziehen: Die Reihenfolge der Ringradien darf sich durch Kamerabewegung nicht umkehren.SIGNALWEGEeinschalten: Es dürfen keine Client→Client-Kanten erscheinen; nur ausgewählte Client→Task-Pfade sind erlaubt.- Zwei unabhängige Browserprofile auf
127.0.0.1verbinden. /leaderboardin einem dritten Tab öffnen; Punktänderungen sollen ohne manuelles Reload erscheinen.- Im Admin-Bereich einen aktiven Task auswählen und
pausesofort ausführen. Clients müssenPAUSEanzeigen und keine Tipps senden. resumeausführen; Autosubmit muss mit dem aktuellen Client-Intervall weiterlaufen.set_range_bitsim Moduspreserveerhöhen; Client muss neue Bitzahl ohne Reload verwenden.set_range_bitsim Modusrerollausführen; Punkte/Scores müssen auf 0 zurückfallen und neuer Seed/Revision aktiv werden.- Eine Range-Bit-Änderung fünf Minuten in die Zukunft planen und anschließend abbrechen; Status
cancelledprüfen. - Zwei geplante Aktionen hintereinander setzen, z. B. 24 Bit und später 32 Bit, und Ausführung/Audit prüfen.
- Einen kleinen Zahlenraum verwenden, einen Task lösen und lokales SVG + Manifest prüfen.
/leaderboardöffnen: das Gewinner-Artefakt muss in Galerie und Ranking nur als Wasserzeichen-Vorschau erscheinen.- Direkter Aufruf der früheren
/artifacts/...-URI muss 404 liefern; Original/Manifest aus dem Admin-Dashboard müssen weiterhin nach Authentifizierung öffnen. - Browser auf <850 px setzen oder MOBILE aktivieren: Client-Details müssen einklappbar sein; im Admin müssen MAP/TASKS/CONTROL einzeln erreichbar sein.
OpenAI Provider
ARTIFACT_PROVIDER=openai
ARTIFACT_MODEL=gpt-image-2
OPENAI_API_KEY=...
ARTIFACT_WIDTH=1024
ARTIFACT_HEIGHT=1024
ARTIFACT_QUALITY=medium
Task lösen bzw. bei einem abgeschlossenen Task NFT-Bild neu erzeugen ausführen. Erwartet:
/data/artifacts/artifact_<id>/image.png
/data/artifacts/artifact_<id>/manifest.json
ComfyUI Provider
ComfyUI starten, API-Workflow exportieren und im Workflow mindestens {{PROMPT}} sowie optional die anderen Platzhalter verwenden. Danach:
ARTIFACT_PROVIDER=comfyui
COMFYUI_URL=http://127.0.0.1:8188
COMFYUI_WORKFLOW_PATH=/path/workflow-api.json
ARTIFACT_MODEL=checkpoint.safetensors
AUTOMATIC1111 Provider
A1111 mit --api starten:
ARTIFACT_PROVIDER=a1111
A1111_URL=http://127.0.0.1:7860
ARTIFACT_MODEL=checkpoint.safetensors
Bei --api-auth user:pass zusätzlich A1111_USER und A1111_PASSWORD setzen.
Regressionen aus früheren Fixes
- Auth challenge/login toleriert optionale JWK-Metadaten (
alg,use,kidetc.). - Mehrere Challenges derselben Identität überschreiben einander nicht.
- Eine Browser-ID darf weiterhin nur eine aktive Client-WebSocket-Verbindung besitzen.
- Leere Listen werden als
[], nicht alsnull, an die UI geliefert. - Client-Punkt wird direkt beim WebSocket-Connect erzeugt.
Additional v2 checks
- SQLite schema parsed successfully with Python
sqlite3, includingtask_actionsand the live task override columns. - The legacy
migrations/001_init.sqlmirror was synchronized withinternal/data/schema.sqlso manual schema inspection does not show an obsolete layout. - Responsive CSS keeps the Admin Task Actions / Artifact control plane visible on common laptop widths (851–1280px).
V2.1 package-integrity regression
The V2 archive was missing internal/data/, which caused an old store.go to survive when users extracted over a previous checkout. That produced compile errors such as missing Task.GuessMinIntervalSec, Task.Paused, LiveLeaderboard, and TaskAction. V2.1 explicitly includes:
internal/data/store.gointernal/data/schema.sql- the matching V2
internal/server/server.go
Always test from a clean extraction directory.
V2.2 zusätzliche Validierung
node --check internal/webui/dist/app.jserfolgreich.go test ./...wurde zusätzlich gegen lokale Compile-Stubs fürchi,gorilla/websocketundmodernc.org/sqliteausgeführt, um alle Neural-Hunt-Packages inklusiveinternal/server/watermark_test.goohne Netzwerkzugriff zu kompilieren.watermark_test.goprüft Traversal-Abwehr, SVG-Wasserzeichen und Raster-Wasserzeichen.- Die erweiterten Leaderboard-/Artifact-SQL-Abfragen wurden gegen das reale
schema.sqlmit Pythonsqlite3ausgeführt.
V2.4 Scale smoke test
node --check internal/webui/dist/app.js
go test ./internal/runtime ./internal/ws ./internal/server ./cmd/loadtest
# Server in Terminal 1
go run ./cmd/server
# Start small, then increase.
go run ./cmd/loadtest -url http://127.0.0.1:8080 -clients 250 -ramp 10s -duration 1m -max-nodes 100
go run ./cmd/loadtest -url http://127.0.0.1:8080 -clients 1000 -ramp 30s -duration 2m -max-nodes 100
Im Admin unter /admin sollten dabei Guess/s, Improve/s, SQLite W/s, WS Frames/s, WS MB/s, Drops/s, Goroutines und Heap MB live aktualisiert werden. Bei einem stabilen Test sollten Drops/s nahe 0 bleiben. Ein niedriger SQLite W/s relativ zu Guess/s bestätigt, dass verlierende Tipps den DB-Hotpath nicht mehr belasten.
V2.5.2 Admin form stability regression
The periodic 3-second admin telemetry refresh must never rebuild #settingfields.
Only explicit task/tab changes or completed admin actions may refresh the control plane.
Manual regression:
- Open
/adminand selectARTIFACT. - Open the Provider or Quality native select and leave it open for more than 6 seconds.
- The dropdown must remain open while Overview/Performance counters continue updating.
- Type into Prompt-Zusatz for more than 6 seconds; caret position and textarea scroll position must not jump.
- Switch tabs and back; the draft value must still be restored.
Static/syntax checks used for this release:
node --check internal/webui/dist/app.js
go test ./internal/webui ./internal/core ./internal/auth ./internal/artifact ./internal/settings
RIFT Medium + OpenAI usage/cost telemetry
- Start with
ARTIFACT_PRESET=raccoon_full_art_v1and no explicitARTIFACT_QUALITY: the effective quality must bemedium. - Generate one RIFT winner artifact and verify that the OpenAI request sends
quality=medium. - Verify that
artifact_api_usagereceives oneartifactrow per successful winner-image API call and acharacter_anchorrow when the canonical anchor is created. A successful call is logged even if the provider omits its optionalusageblock; in that case token counts remain zero andestimated_cost_usdis NULL. - When OpenAI returns usage data, verify that
input_tokens, text/image input split,output_tokens,total_tokens,estimated_cost_usd, andpricing_basisare persisted and mirrored in the artifact manifest/provider metadata. - In Admin → Artifact, verify KOSTEN HEUTE, Ø KOSTEN PRO KARTE, KOSTEN PRO 1.000 KARTEN, and the recent-call table. Anchor cost belongs to today's total but must not affect the per-card average.
- Verify
GET /api/admin/artifact/usage?day_start_ms=<local-midnight-ms>returns the same aggregates.
RIFT V2.8 — per-task style references + manual anchor
- In Admin → ARTIFACT, with
OPENAI_API_KEYconfigured and no existing_collection/character_anchor.png, click RIFT-ANCHOR JETZT ERZEUGEN. Verify exactly one OpenAI image-generation request is made, the file is stored atdata/artifacts/_collection/character_anchor.png, the usage row haskind=character_anchor, and the UI changes toLOCKEDwithout an overwrite button. - Verify a second direct
POST /api/admin/artifact/character-anchorreturns HTTP 409 while the anchor exists. - In Admin → TASK ACTIONS upload a JPEG/PNG style reference. Verify it is stored content-addressed under
data/artifacts/_styles/,tasks.nft_style_referencereceives only the basename, and the admin preview shows the uploaded image. - Verify the public Task landing card displays
/api/public/tasks/<id>/style-referenceand marks a custom task asTASK STYLE; tasks without an upload display the bundled default reference. - Complete a task and verify its successor inherits
nft_style_reference. - Generate a RIFT winner card and inspect the multipart OpenAI edit request:
image[]must contain two files in this order:character_anchor.png, then the task style reference. Provider metadata should containreference_mode=character-plus-task-style, the character-anchor hash and the style-reference hash. - Use AUF DEFAULT ZURÜCK and verify the task DB reference is empty and generation falls back to
internal/artifact/assets/style_reference.jpgwithout deleting shared content-addressed style files.
V3.1 — Admin cleanup for stale non-winner profiles
- In Admin → RUNTIME set e.g.
30 Tageand click PRÜFEN. Verify the preview reports only clients whoseclients.last_seenis older than the cutoff, that are not currently connected, and that have never appeared astasks.winner_client_id. - Keep an old client connected via WebSocket: it must be reported as aktuell verbunden geschützt and never be deleted.
- Create an old winner identity: it must be reported as Gewinner geschützt and never be deleted, regardless of age.
- Confirm deletion and verify the client row is removed together with cascading
task_points,client_unlocks, andclient_task_selectionrows. - Verify a recent non-winner remains untouched.
- Verify WebSocket connect and disconnect update
clients.last_seen, so a long-running session starts its inactivity window at disconnect rather than at its original login. - The API rejects cleanup windows shorter than one hour.
Relevant automated tests: internal/data/profile_cleanup_test.go and internal/server/profile_cleanup_test.go.
V3.5 Random Guess Lottery
- Admin → RUNTIME:
Lotterie-Zeitfenster (s)=20,Max. gezogene Tipps je Task/Fenster=2setzen und speichern. - Mindestens drei Clients mit demselben Task verbinden und innerhalb desselben Fensters je einen Tipp absenden lassen.
- Bis zum Fensterende müssen die Requests auf die Losziehung warten. Danach dürfen höchstens zwei Clients
Tipp gezogen & geprüftsehen; übrige Clients sehenTipp diesmal nicht gezogen. - Admin-Telemetrie:
Reject/ssteigt für nicht gezogene Tipps;Guess/szählt nur tatsächlich gezogene/ausgewertete Tipps. - Mit
Max. gezogene Tipps je Task/Fenster=0speichern: Tipps müssen wieder ohne Lotterie-Verzögerung normal verarbeitet werden. - Bei mehreren aktiven Tasks prüfen, dass jeder Task sein eigenes Kontingent erhält.
V3.8 security/hardening checks
The production server exposes the public app on HTTP_ADDR (default :8080) and the private control plane on ADMIN_HTTP_ADDR (default :8081). Verify that the public listener returns 404 for /admin and /api/admin/settings, while the private listener serves /admin and requires the HttpOnly admin session cookie.
For large synthetic load tests, temporarily set sybil_pow_bits=0 and sybil_warmup_sec=0 from the private Admin Runtime page (or use the matching DEFAULT_... values on a fresh database). Otherwise the load generator intentionally pays the same new-identity proof-of-work as a real Sybil client.
Useful browser checks after HTTPS proxying:
/public: Content-Security-Policy and X-Frame-Options: DENY are present
/admin: document.cookie does not expose neuralhunt_admin_session (HttpOnly)
WS: normal browser websocket connects; a foreign browser Origin is rejected
The OpenAI circuit breaker uses rolling windows and successful usage rows. Set very small limits in Admin Runtime to verify that a winning artifact stays pending with an OpenAI cost circuit breaker active diagnostic instead of making another provider call. Restore the desired limits afterward.
V3.9 portable identity + owned artifact recovery
- Start the CLI with a persistent identity path and note the printed Client-ID and file path:
NEURALHUNT_IDENTITY=/tmp/nh-client/identity.json go run ./cmd/client -url http://127.0.0.1:8080
- Run
identityand verify the same Client-ID/path is shown. Restart with the same file and verify the Client-ID is unchanged. - Set a passphrase of at least 12 characters and create an encrypted browser-compatible backup:
export NEURALHUNT_IDENTITY_PASSPHRASE='correct horse battery staple'
go run ./cmd/client -identity /tmp/nh-client/identity.json -export /tmp/neuralhunt-browser-import.json
- In the browser choose IDENTITÄT → IMPORT, select that JSON, enter the same passphrase and confirm the switch. The browser Client-ID must exactly equal the CLI Client-ID. The import validates that public/private P-256 keys can sign/verify and that the optional export Client-ID matches.
- After a win is fully rendered, MEINE NFTS must list the artifact and ORIGINAL must download the unwatermarked artifact. A different identity requesting
/api/me/artifacts/<task-id>/downloadmust receive 404. - In the CLI,
my-nftslists only the authenticated identity's ready winner artifacts andnft original <task-id> <datei>downloads the original.nfts/nft getremain public watermarked previews. - Verify legacy encrypted exports without
format/clientId/iterationsmetadata still import using 250,000 PBKDF2 iterations.
Focused identity tests can run without the optional network/database dependencies:
go test ./cmd/client/identity.go ./cmd/client/identity_test.go
node --check internal/webui/dist/app.js
V4.0 Beacon Hunt + Hosted Service smoke test
Beacon Hunt
- Start the game and enable
Guess Lottery Max Accepted > 0plusBeacon Huntin Admin Runtime. - Open two browser/CLI identities and choose different paths (
PULSE,FLUX,ORBIT). - Confirm requests wait until the window closes and that
/api/public/beacon/<task-id>/latestreturns the recorded drand round, signature, derived randomness and boosted path. - Disable Beacon Hunt again and confirm the legacy
crypto/randlottery still works.
Hosted reward pairing
- Build with
make images-compose, then start withdocker compose --profile hosted up -dand route 8090 publicly over HTTPS; keep 8081/8091/8092 private. - Create/log in to a Customer Service account.
- Log into the normal Neural Hunt browser with the desired reward identity and click HOSTED CODE (or run
hosted-codein the CLI). - Paste the one-shot code into the Customer Service portal. Confirm the portal shows the proven Client-ID. Reusing the same code must fail.
Protected manual credits
- Set
CS_ALLOW_MANUAL_CREDITS=1only in a test/private environment. - Log into the Customer Service admin on private port 8091 and grant a small amount of credits.
- Verify the customer ledger contains a positive
manual_test_grantentry. - Set
CS_ALLOW_MANUAL_CREDITS=0again and verify grants are rejected.
Managed workers / prepaid stop
- Create a worker and assign an active task and Beacon path.
- Start it. The worker should create/register its own P-256 identity and the game should install
worker -> reward ownerdelegation. - Let the worker win a test task (use a deliberately tiny range only in a private test environment). Confirm
winner_client_idis the main reward owner whilewinner_worker_client_idis the worker identity. - Reduce the prepaid balance to less than one billable minute and verify Customer Service stops the worker before another paid minute is allowed.
- Stop/start the worker and confirm its Docker identity volume preserves the same Client-ID.
- Download the worker identity, replace it with another valid raw identity, start again and confirm a new worker Client-ID registers while reward ownership remains delegated to the same main identity.
PayPal Sandbox
- Keep
PAYPAL_ENVIRONMENT=sandboxand configure sandbox client ID/secret/webhook ID. - Buy the smallest test package from the Customer Service portal.
- Confirm credits are added only after a server-confirmed
COMPLETEDcapture whose amount and currency match the stored package. - Repeat the return/capture callback or webhook and verify the
paypal:<order-id>ledger reference prevents duplicate credits. - Leave live mode disabled until the provider/legal review for the actual product is complete.
V4.1 split Docker images
- Build the three roles locally:
docker buildx bake --load
- Verify the image contents/entrypoints:
docker image inspect neuralhunt-server:local --format '{{json .Config.Entrypoint}}'
docker image inspect neuralhunt-customer-service:local --format '{{json .Config.Entrypoint}}'
docker image inspect neuralhunt-worker:local --format '{{json .Config.Entrypoint}}'
Expected entrypoints are /app/neuralhunt, /app/neuralhunt-customer-service
and /app/neuralhunt-client respectively. The server image should not contain
the client or Customer Service binaries.
- For local Hosted Service testing, build/tag the worker image before starting the hosted profile:
make images-compose
docker compose --profile hosted up -d
-
Set
CS_WORKER_IMAGEto a deliberately missing public image withCS_WORKER_AUTO_PULL=true; starting a worker should cause Docker Engine to pull the configured image. Repeat withCS_WORKER_AUTO_PULL=false; the request must fail before prepaid credits are charged. -
For a private registry, configure a read-only token with
CS_WORKER_REGISTRY_SERVER/USERNAME/PASSWORD, remove the local worker image and confirm an on-demand pull succeeds. Verify the credentials do not appear in the managed worker container environment (docker inspect).
Focused stdlib-only tests for the Docker image orchestration can be run even without the application's external Go dependencies:
go test ./internal/customer/docker.go ./internal/customer/docker_test.go