RC-7
release-tag / release-image (push) Failing after 2m44s

This commit is contained in:
2026-08-11 16:58:07 +02:00
parent 185ccf1101
commit bcfbef390f
44 changed files with 4778 additions and 172 deletions
+232
View File
@@ -0,0 +1,232 @@
# Neural Hunt Hosted Service (optional)
V4.1 splits the deployment into three dedicated Docker images. V4.0 added an optional commercial control plane without changing the normal game
server. The default `docker compose up` still starts only `app`. For a local source build,
build all three images first and then start the hosted layer:
```bash
docker compose --profile images build app customer-service worker-image
docker compose --profile hosted up -d
```
With Docker Buildx the three images can also be built/tagged in one operation:
```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 .
```
## Three Docker images
V4.1 no longer ships one monolithic runtime image. `Dockerfile` has three final
targets:
- `server` -> `neuralhunt-server:local` (game/API + private game admin)
- `customer-service` -> `neuralhunt-customer-service:local` (portal/billing/control plane)
- `worker` -> `neuralhunt-worker:local` (CLI hunting agent only)
The tags are configurable through `.env` / the build environment:
```env
NEURALHUNT_SERVER_IMAGE=registry.example.com/neuralhunt/server:v4.1
NEURALHUNT_CUSTOMER_IMAGE=registry.example.com/neuralhunt/customer-service:v4.1
CS_WORKER_IMAGE=registry.example.com/neuralhunt/worker:v4.1
```
`CS_WORKER_IMAGE` is both the worker build tag and the exact image reference the
Customer Service passes to Docker Engine. With `CS_WORKER_AUTO_PULL=true` (the
default), a missing worker image is pulled lazily when a customer first needs a
worker. For a private registry, configure a read-only deploy/robot credential:
```env
CS_WORKER_REGISTRY_SERVER=registry.example.com
CS_WORKER_REGISTRY_USERNAME=neuralhunt-worker-pull
CS_WORKER_REGISTRY_PASSWORD=<read-only-token>
```
Those credentials are converted to Docker's `X-Registry-Auth` header only for
the image-pull request and are never passed to managed worker containers. Set
`CS_WORKER_AUTO_PULL=false` if production policy requires images to be
pre-pulled instead.
The dedicated worker image already declares `/app/neuralhunt-client` as its
ENTRYPOINT. `CS_WORKER_ENTRYPOINT` should therefore stay empty. It exists only
as a compatibility override for older monolithic images.
For registry publishing with Buildx:
```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
# CI/CD: Dockerfile.server, Dockerfile.customer-service und Dockerfile.worker getrennt bauen/pushen.
```
## Ports and trust zones
- `app:8080` — public Neural Hunt game
- `app:8081` — private game admin + private Customer-Service delegation API
- `customer-service:8090` — customer portal (put behind HTTPS)
- `customer-service:8091` — private/VPN Customer-Service admin
- `customer-service:8092` — Docker-network-only worker registration
Do not route 8081, 8091 or 8092 to the public Internet.
## PrePaid billing model
The reference implementation bills **running worker time**, not individual
submitted guesses. This makes the commercial unit explicit and predictable, but
it is only a billing design; it does not by itself determine the legal/payment-
provider classification of a paid prize/chance product. `CS_WORKER_CREDITS_PER_MINUTE=1.0` means one paid minute of
one worker consumes one credit. The next minute is debited atomically before it
is allowed to continue; when there is insufficient balance the service stops
the worker. A 2-second reconciliation loop also catches containers that stop or
exit outside the portal. If Docker fails before a newly purchased start minute
actually begins, that start debit is automatically refunded to the ledger.
Credit packages are configured server-side:
```env
CS_CREDIT_PACKAGES=starter:499:EUR:60;plus:1999:EUR:300;power:4999:EUR:900
```
The four fields are `id:price_cents:CURRENCY:credits`. These are examples, not a
pricing recommendation.
## PayPal
PayPal is disabled by default and should first be configured in sandbox:
```env
PAYPAL_ENABLED=true
PAYPAL_ENVIRONMENT=sandbox
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
PAYPAL_WEBHOOK_ID=...
```
The browser never receives the PayPal secret. Customer Service creates Orders
server-side, redirects the user to the returned approval URL, captures the
Order server-side after return and reconciles the captured amount/currency
against the package stored before crediting the ledger. Webhooks are verified
through PayPal's webhook-signature verification API. Ledger references make a
completed Order idempotent.
Live PayPal is additionally locked until:
```env
PAYPAL_LIVE_APPROVAL_ACK=I_HAVE_PAYPAL_APPROVAL
```
is set. This is intentional: products involving payment plus prizes/chance can
require provider approval and legal review. Do not use that flag as a substitute
for the approval/review itself.
## Manual test/support credits
Manual top-ups are a separate protected bypass and are **off by default**:
```env
CS_ALLOW_MANUAL_CREDITS=1
```
They are available only from the private Customer-Service admin listener on
8091 after its own HttpOnly admin session. Each grant is written to the same
immutable-style credit ledger with a unique `manual:*` reference. Keep the flag
off on a normal public production deployment unless support operations require
it.
## Multiple workers and reward ownership
Each managed worker gets:
- its own P-256 Neural Hunt identity in a dedicated Docker named volume;
- its own normal one-identity/one-presence lease;
- its own task assignment and Beacon path (`auto`, `PULSE`, `FLUX`, `ORBIT`);
- an authenticated private registration token known only to Customer Service.
The customer configures one **main reward identity** in the portal, but the
portal does not trust a pasted public Client-ID. While logged into the game with
the intended owner identity, the customer requests a **HOSTED CODE** (or runs
`hosted-code` in the CLI). The game stores only the SHA-256 hash of this random
one-time code; it expires after 10 minutes. Customer Service redeems the code
over the private game control plane and receives the proven Client-ID. The
P-256 private key never leaves the browser/CLI.
When a worker starts, it registers its own Client-ID with Customer Service,
which installs a delegation on the private game control plane:
```text
worker identity -> main reward identity
```
A winning task records both values:
- `winner_client_id` = durable reward owner
- `winner_worker_client_id` = cryptographic identity that actually submitted the winning guess
Thus several workers can run concurrently without disabling the game's normal
single-presence rule. The original NFT is retrieved later by logging into the
browser/CLI with the main reward identity.
Worker raw identity files can be downloaded or replaced from the portal. They
contain a private key and must be treated as secrets. Replacing an identity
stops the worker and forces private delegation registration again on next start.
Deleting a worker also deletes its identity volume, so back it up first if it
must remain portable.
## Worker containment
Managed worker containers receive no Docker socket and no published ports. The
reference Docker Engine request additionally uses:
- read-only root filesystem
- all Linux capabilities dropped
- `no-new-privileges`
- 256 MiB memory limit
- 1 CPU limit
- PID limit 128
- only the private identity volume writable
The Customer Service itself needs Docker Engine control. Directly mounting
`/var/run/docker.sock` effectively gives this service host-level Docker control.
The provided Compose setup supports it for a simple self-hosted deployment, but
a production setup should preferably put a narrowly permissioned Docker Socket
Proxy in front and set `DOCKER_HOST` to that proxy instead.
Capacity is also bounded independently of reverse-proxy rate limiting. Stopped worker records do not allocate a Docker volume until the worker is actually started (or an identity is explicitly uploaded):
```env
CS_MAX_WORKERS_PER_CUSTOMER=20
CS_MAX_WORKERS_GLOBAL=1000
CS_MAX_RUNNING_WORKERS_PER_CUSTOMER=10
CS_MAX_RUNNING_WORKERS_GLOBAL=100
```
## Optional Beacon Hunt
Enable from the game Admin Runtime settings or defaults:
```env
DEFAULT_BEACON_HUNT_ENABLED=1
DEFAULT_BEACON_BONUS_WEIGHT=2
```
Players/agents choose PULSE, FLUX or ORBIT before the current lottery window
ends. Neural Hunt plans the **first drand round strictly after that boundary**.
When it becomes available, `SHA-256(signature)` is used to derive both the
boosted path and the weighted selection. A matching path receives the configured
weight; a non-matching path still has weight 1.
Successful draws are stored in `beacon_draws` with beacon ID, round, signature,
derived randomness, boosted path, ticket count and selected count, and the latest
record is available from `/api/public/beacon/<task-id>/latest` for independent
audit.
The current implementation relies on HTTPS to the configured drand endpoint and
stores the returned signature for external verification. It does **not** yet
perform local BLS signature verification against the drand chain public key.
If cryptographic self-verification is a product requirement, add a vetted drand
client/verifier before making that claim in customer-facing material.