This repository contains two separate applications in one repo:
Training Hubfor player-contributed training data and admin-side pipeline controlMarketGuard APIfor Hypixel SkyBlock market data, including Lowest BIN aggregation
- Clear package split between
app/training_hubandapp/marketguard_api - External web sign-in via approved GitHub OAuth and/or Authelia OIDC identities
- Local app session management after external provider sign-in
- Branded HTML emails with plain-text fallback for operational mail where enabled
- Admin backup create/restore for DB + uploads + bundles
- Player dashboard with own contribution stats
- Upload form for
training-cases-v2.jsonlfiles - Per-account upload history with download links
- Self-service upload deletion, full contribution purge, and account deletion
- Self-service account data export workflow delivered by email
- Admin view over users, basic case list, training runs, and audit log
- Admin-managed content scrubbing rules for quarantining matching cases from future uploads before storage and training
- Monitoring metrics endpoint (
/api/v1/metrics) and auth-spike alerting - Public Lowest BIN v2 endpoint at
/api/v2/lowestbin - Public Bazaar endpoint at
/api/v1/bazaar - Public player/profile QUERY endpoint at
/api/v1/players - Admin button to:
- build one merged training bundle from all accepted uploads
- Audit log also records upload and bundle downloads
Data/state:
- the default deployment stores app state under
/app/data - Training Hub stores users, sessions, uploads, cases, and audit metadata in MariaDB for staging/production deployments
- accepted upload payloads, quarantined scrub hits, and generated bundles are kept in the persistent app data volume
Frontend files:
- HTML templates:
sites/ - CSS:
css/
Application packages:
app/training_hub/contains the Training Hub app, routes, storage, auth, and admin flowsapp/marketguard_api/contains the Hypixel auction client, Lowest BIN cache, and API routesapp/main.pyremains available as the combined in-process entrypoint used by tests and local integration scenarios
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
Copy-Item .env.example .envThe sample .env.example is a local-development baseline. Before a real deployment, switch the production-only flags called out in section 4.
It is intentionally minimal: anything omitted falls back to the app defaults in app/training_hub/config/settings.py and app/marketguard_api/config.py.
Set at least:
TRAINING_HUB_SECRET_KEYto a long random value (at least 32 characters recommended)
Optional:
TRAINING_HUB_ADMIN_USERNAMES(comma-separated admin allowlist for externally provisioned users)TRAINING_HUB_DB_DRIVER=sqliteif you intentionally want a local development fallback instead of MariaDB
Bootstrap note: web access now comes from external identity providers. Configure either GitHub OAuth or Authelia OIDC plus an explicit allowlist before first sign-in.
.\.venv\Scripts\Activate.ps1
uvicorn app.training_hub.main:create_app --factory --host 0.0.0.0 --port 8080In a second shell for the MarketGuard API:
.\.venv\Scripts\Activate.ps1
uvicorn app.marketguard_api.main:create_marketguard_app --factory --host 0.0.0.0 --port 8081Open:
http://localhost:8080(Training Hub landing page)http://localhost:8080/hub(redirects to login/dashboard)http://localhost:8081/api/v2/lowestbin(MarketGuard Lowest BIN JSON withlastUpdated,products, seller UUID, and auctionitem_name)http://localhost:8081/api/v1/bazaar(MarketGuard Bazaar summary JSON)http://localhost:8081/api/v1/players(MarketGuard player/profile JSON via the QUERY method)http://localhost:8081/docs(interactive OpenAPI docs for local validation)
The repository now ships a single Compose stack behind bundled Caddy. It runs five base services plus an optional Redis cache service when MARKETGUARD_REDIS_ENABLED=true with SCAMSCREENER_REDIS_MANAGED=true:
scamscreener-hubfor the Training Hubscamscreener-apifor the public Lowest BIN and Bazaar APImarketguard-hubfor the public market website under/market/scamscreener-dbfor the internal MariaDB databasecaddyfor public HTTPS termination and reverse proxy
The stack keeps persistent state under /app/data, auto-generates a strong app secret on first boot when you do not provide one, auto-generates persistent MariaDB credentials for the managed internal database, and preserves the same public URLs as before.
One-time setup:
Copy-Item .env.production.example .env.production
# edit .env.productionThen start production:
python scripts/update.pyWhat this path expects:
- a real public domain in
CADDY_SITE_ADDRESSsuch asscamscreener.creepans.net TRAINING_HUB_PUBLIC_BASE_URLis set to the real publichttps://...URL- GitHub OAuth and/or Authelia OIDC are configured with explicit allowlists
TRAINING_HUB_ADMIN_USERNAMESand/orTRAINING_HUB_ADMIN_EMAILSare set for deterministic admin bootstrap- SMTP is configured only if you explicitly enable password reset or admin MFA mail flows
TRAINING_HUB_SITE_*values are reviewed for/impressumand/datenschutz- persistent storage is kept on the Docker volumes
What this path provides automatically:
- one internal hub container, one internal API container, one internal market website container, one internal MariaDB container, one public Caddy container, and an optional internal Redis container
- automatic HTTPS via Caddy
/api/v1/healthhealthchecks for the hub and a dedicated internal health route for the API- public blocking of
/api/v1/health,/api/v1/metrics, and internal-only health paths - generated persistent secret key when
TRAINING_HUB_SECRET_KEYis omitted - generated persistent MariaDB app/root passwords when
SCAMSCREENER_DB_MANAGED=true
Operational helpers for this path:
python scripts/update.pyruns preflight, rebuilds the image, restarts the stack, waits for app health, and marks the deployment as OAuth-readypython scripts/update.py --skip-pullskips upstream base-image pulls during rebuildpython scripts/migrate.pycreates repo/volume backups, stops the legacy local-login split stack without deleting volumes, and then starts the OAuth/OIDC release in placepython scripts/reset.pyasks for confirmation and then deletes the full compose deployment state for a clean restartpython scripts/reset.py --yes --prune-imagesalso removes the locally built app image
The production topology is Compose-first. Running a single docker run container no longer reproduces the full production stack because the hub, public API, and MariaDB are isolated into separate services.
CADDY_SITE_ADDRESSdefaulthttp://localhost(set a real domain for public Caddy TLS)CADDY_HTTP_PORTdefault80CADDY_HTTPS_PORTdefault443PORToptional runtime port override used by the app imageWEB_CONCURRENCYoptional worker count for the app image (default1; keep this value for the player QUERY route until its per-IP rate limiter is centralized)TRAINING_HUB_HOSTdefault0.0.0.0TRAINING_HUB_PORTdefault8080TRAINING_HUB_ENVdefaultdevelopment(productionenforces strict startup checks)TRAINING_HUB_PUBLIC_BASE_URLoptional absolute public base URL; recommended for production and used for reset links plus allowed-host fallbackTRAINING_HUB_ALLOWED_HOSTSoptional allowlist forHostheader validationSCAMSCREENER_DB_MANAGEDdefaultfalse; settruefor the bundled internal MariaDB serviceSCAMSCREENER_DB_NAMEdefaultscamscreener_hubSCAMSCREENER_DB_USERdefaultscamscreenerTRAINING_HUB_DB_DRIVERdefaultsqlitein development andmariadbin staging/productionTRAINING_HUB_DATABASE_URLoptional full DSN override (mariadb://user:pass@host:3306/db)TRAINING_HUB_DB_HOSTdefault127.0.0.1TRAINING_HUB_DB_PORTdefault3306TRAINING_HUB_DB_NAMEdefaultscamscreener_hubTRAINING_HUB_DB_USERdefaultscamscreenerTRAINING_HUB_DB_PASSWORDrequired when driver ismariadbunless the managed compose stack injects it from its generated runtime secretTRAINING_HUB_DB_REQUIRE_TLSdefaultfalsein development andtruefor external MariaDB in productionTRAINING_HUB_DB_SSL_CAoptional CA path for external MariaDB TLS verificationTRAINING_HUB_DB_SSL_CERToptional client certificate for MariaDB TLSTRAINING_HUB_DB_SSL_KEYoptional client key for MariaDB TLSTRAINING_HUB_DB_SSL_VERIFY_HOSTNAMEdefaulttrueTRAINING_HUB_SECRET_KEYrequiredTRAINING_HUB_SESSION_TTL_MINUTESdefault720TRAINING_HUB_SESSION_BIND_IPdefaultfalseTRAINING_HUB_SESSION_BIND_USER_AGENTdefaultfalseTRAINING_HUB_GITHUB_OAUTH_CLIENT_ID/TRAINING_HUB_GITHUB_OAUTH_CLIENT_SECRETenable GitHub sign-inTRAINING_HUB_GITHUB_OAUTH_ALLOWED_LOGINS/TRAINING_HUB_GITHUB_OAUTH_ALLOWED_EMAILS/TRAINING_HUB_GITHUB_OAUTH_ALLOWED_SUBJECTSrestrict GitHub accessTRAINING_HUB_AUTHELIA_OIDC_ISSUER_URL/TRAINING_HUB_AUTHELIA_OIDC_CLIENT_ID/TRAINING_HUB_AUTHELIA_OIDC_CLIENT_SECRETenable Authelia OIDCTRAINING_HUB_AUTHELIA_OIDC_SCOPESdefaultopenid,profile,emailTRAINING_HUB_AUTHELIA_OIDC_ALLOWED_EMAILS/TRAINING_HUB_AUTHELIA_OIDC_ALLOWED_USERNAMES/TRAINING_HUB_AUTHELIA_OIDC_ALLOWED_SUBJECTSrestrict Authelia accessTRAINING_HUB_SMTP_HOSTSMTP server hostTRAINING_HUB_SMTP_PORTSMTP server port (default587)TRAINING_HUB_SMTP_USERNAMEoptional SMTP usernameTRAINING_HUB_SMTP_PASSWORDoptional SMTP passwordTRAINING_HUB_SMTP_FROM_EMAILsender address for outbound account exports and operational mailTRAINING_HUB_SMTP_USE_TLSdefaultfalse(implicit TLS/SMTPS)TRAINING_HUB_SMTP_USE_STARTTLSdefaulttrue(explicit STARTTLS)TRAINING_HUB_SITE_PROJECT_CLASSIFICATIONdefaultPrivate non-commercial community projectTRAINING_HUB_SITE_OPERATOR_NAMEoptional operator/provider name rendered on/impressumTRAINING_HUB_SITE_POSTAL_ADDRESSoptional postal address rendered on/impressumTRAINING_HUB_SITE_CONTACT_CHANNELoptional public contact channel rendered on/impressumTRAINING_HUB_SITE_PRIVACY_CONTACToptional privacy contact rendered on/datenschutzTRAINING_HUB_SITE_HOSTING_LOCATIONdefaultAshburn, Virginia, USATRAINING_HUB_WEBAUTHN_RP_IDoptional WebAuthn relying-party ID (defaults fromTRAINING_HUB_PUBLIC_BASE_URLor allowed hosts)TRAINING_HUB_WEBAUTHN_RP_NAMEdefaultScamScreenerTRAINING_HUB_WEBAUTHN_ORIGINSoptional comma-separated WebAuthn origins (defaults fromTRAINING_HUB_PUBLIC_BASE_URL, or from allowed hosts in production)TRAINING_HUB_ENFORCE_HTTPSdefaultfalse(truein production)TRAINING_HUB_ENABLE_RATE_LIMITdefaulttrueTRAINING_HUB_ENFORCE_ORIGIN_CHECKdefaulttrueTRAINING_HUB_MAX_UPLOAD_BYTESdefault5242880TRAINING_HUB_MAX_UPLOAD_DOWNLOADS_PER_MINUTE_PER_USERdefault60TRAINING_HUB_MAX_BUNDLE_DOWNLOADS_PER_MINUTE_PER_USERdefault30TRAINING_HUB_MAX_UPLOADS_PER_DAY_PER_USERdefault40TRAINING_HUB_MAX_UPLOAD_BYTES_PER_DAY_PER_USERdefault209715200TRAINING_HUB_MAX_UPLOAD_CASES_PER_DAY_PER_USERdefault20000TRAINING_HUB_MAX_UPLOADS_PER_DAY_PER_IPdefault120TRAINING_HUB_GLOBAL_UPLOAD_STORAGE_CAP_BYTESdefault5368709120TRAINING_HUB_RETENTION_SESSIONS_DAYSdefault30TRAINING_HUB_RETENTION_PASSWORD_RESET_DAYSdefault7TRAINING_HUB_RETENTION_AUDIT_LOGS_DAYSdefault180TRAINING_HUB_RETENTION_UPLOADS_DAYSdefault365TRAINING_HUB_RETENTION_BUNDLES_DAYSdefault365TRAINING_HUB_RETENTION_BACKUPS_DAYSdefault30TRAINING_HUB_RETENTION_RATE_LIMIT_DAYSdefault7TRAINING_HUB_RETENTION_AUTO_ENABLEDdefaultfalseTRAINING_HUB_RETENTION_AUTO_INTERVAL_MINUTESdefault1440TRAINING_HUB_BACKUP_RESTORE_MAX_BYTESdefault536870912TRAINING_HUB_SECURITY_ALERT_WINDOW_MINUTESdefault15TRAINING_HUB_SECURITY_ALERT_COOLDOWN_MINUTESdefault15TRAINING_HUB_SECURITY_ALERT_FAILED_LOGIN_THRESHOLDdefault10TRAINING_HUB_SECURITY_ALERT_MFA_FAILED_THRESHOLDdefault6TRAINING_HUB_SECURITY_ALERT_PASSWORD_RESET_THRESHOLDdefault10TRAINING_HUB_STORAGE_DIRdefault./dataTRAINING_HUB_ADMIN_EMAILSoptional in development, but required in production unlessTRAINING_HUB_ADMIN_USERNAMESis setTRAINING_HUB_ADMIN_USERNAMESoptional in development, but required in production unlessTRAINING_HUB_ADMIN_EMAILSis setTRAINING_HUB_TRUSTED_PROXIESoptional, comma-separated exact IPs or CIDR ranges (docker-compose.ymlkeeps127.0.0.1for the internal healthcheck and appends the internal Caddy IP automatically)TRAINING_HUB_PROJECT_ROOToptionalSCAMSCREENER_INTERNAL_API_METRICS_URLoptional absolute internal URL for aggregating live API metrics from a separate API process or containerMARKETGUARD_HYPIXEL_API_BASE_URLdefaulthttps://api.hypixel.net/v2MARKETGUARD_REQUEST_TIMEOUT_SECONDSdefault10MARKETGUARD_MAX_PARALLEL_PAGESdefault8MARKETGUARD_SNAPSHOT_RETRIESdefault3MARKETGUARD_DB_DRIVERmust bemariadbMARKETGUARD_DATABASE_URLoptional direct MariaDB DSN overrideMARKETGUARD_DB_HOST/MARKETGUARD_DB_PORT/MARKETGUARD_DB_NAME/MARKETGUARD_DB_USER/MARKETGUARD_DB_PASSWORDconfigure the API database whenMARKETGUARD_DATABASE_URLis unsetMARKETGUARD_DB_REQUIRE_TLSandMARKETGUARD_DB_SSL_CAenable verified external MariaDB TLS; the managed internal compose database defaults to plain internal transportMARKETGUARD_CACHE_TTL_SECONDSdefault60MARKETGUARD_STALE_IF_ERROR_SECONDSdefault300MARKETGUARD_HISTORY_RETENTION_DAYSdefault45MARKETGUARD_LOWESTBIN_RATE_LIMIT_PER_MINUTEdefault30MARKETGUARD_HYPIXEL_API_KEYrequired for/api/v1/players; keep it only in the deployment secret environmentMARKETGUARD_PLAYERS_RATE_LIMIT_PER_MINUTEdefault3per source IP; set0only for controlled internal testingMARKETGUARD_PLAYERS_MAX_UPSTREAM_CONCURRENCYdefault4per API worker; raise it only after checking the Player QUERY efficiency metrics and Hypixel quotaMARKETGUARD_LOCAL_CACHE_ENABLEDtoggles the small per-process response cacheMARKETGUARD_LOCAL_CACHE_TTL_SECONDSandMARKETGUARD_LOCAL_CACHE_MAX_ENTRIESbound local API RAM usageMARKETGUARD_REDIS_ENABLEDtoggles the shared Redis response cacheMARKETGUARD_REDIS_URLoptional direct Redis URL overrideMARKETGUARD_REDIS_HOST/MARKETGUARD_REDIS_PORT/MARKETGUARD_REDIS_DB/MARKETGUARD_REDIS_PASSWORDconfigure Redis whenMARKETGUARD_REDIS_URLis unsetMARKETGUARD_REDIS_REQUIRE_TLSenablesrediss://for external RedisMARKETGUARD_REDIS_CACHE_TTL_SECONDSandMARKETGUARD_REDIS_KEY_PREFIXcontrol Redis response cachingMARKETGUARD_REDIS_MAXMEMORYandMARKETGUARD_REDIS_MAXMEMORY_POLICYtune the internal Redis container whenSCAMSCREENER_REDIS_MANAGED=trueMARKETGUARD_HTTP_USER_AGENTdefaultScamScreener-MarketGuard/1.0MARKETGUARD_TRUSTED_PROXIESoptional, comma-separated exact IPs or CIDR ranges (falls back toTRAINING_HUB_TRUSTED_PROXIESwhen unset)TRAINING_HUB_API_DOCS_ENABLEDdefaulttrueoutside production,falsein productionMARKETGUARD_API_DOCS_ENABLEDdefaulttruefor the standalone MarketGuard app, setfalsein production
Production-mode startup checks (TRAINING_HUB_ENV=production) enforce:
TRAINING_HUB_ENFORCE_HTTPS=true- strong
TRAINING_HUB_SECRET_KEY(>= 32 chars) TRAINING_HUB_ENABLE_RATE_LIMIT=trueTRAINING_HUB_ENFORCE_ORIGIN_CHECK=true- explicit
TRAINING_HUB_ALLOWED_HOSTS(no wildcard) - MariaDB selected by default unless
TRAINING_HUB_DB_DRIVERis explicitly overridden - MariaDB TLS enabled for external MariaDB connections unless the managed internal compose database is used
- at least one external sign-in provider must be configured
Admin trigger creates a merged bundle and records the run as prepared.
Security headers include CSP, COOP/CORP, X-Frame-Options, and Permissions-Policy.
Failed/locked login attempts for known accounts are written to the audit log.
Admin can run retention cleanup from /admin to prune stale sessions, reset tokens, legacy MFA challenges, generic auth flows, logs, uploads, bundles, backups, and rate-limit rows.
Automatic retention cleanup runs in the background when TRAINING_HUB_RETENTION_AUTO_ENABLED=true.
Admin can create and restore backups from /admin (archive includes DB export + uploads + bundles; restore requires valid signed manifest).
Prometheus-compatible monitoring is available at the internal-only /api/v1/metrics endpoint.
Container hardening defaults:
- runs as non-root user
- read-only root filesystem in
docker-compose.yml - dropped Linux capabilities (
cap_drop: ALL) no-new-privilegesenabled- internal MariaDB transport stays on the private Compose network; use external MariaDB plus TLS settings if you need DB-layer encryption
Supply-chain checks:
- GitHub Actions workflow
.github/workflows/server-security.ymlrunspip-auditandtrivy - Dependabot config
.github/dependabot.ymlenables weekly dependency updates
GET /api/v1/health(internal only)GET /api/v2/lowestbinQUERY /api/v2/lowestbinGET /api/v1/bazaarQUERY /api/v1/playersQUERY /api/v1/player-financeGET /market/GET /market/bazaarPOST /api/v1/client/uploadsPOST /api/v1/client/uploads/anonymousPOST /api/v1/client/auth/logout
/api/v1/health is an internal-only observability endpoint that returns status, UTC time, user/upload counts, and storage metadata.
/api/v2/lowestbin returns an object with top-level lastUpdated plus a products object whose keys are item identifiers and whose values contain the current Lowest BIN price, seller auctioneerUuid, Hypixel auction item_name, and snapshot-based avg7d / avg30d averages over deduplicated Hypixel snapshots.
QUERY /api/v2/lowestbin accepts a JSON body with a non-empty products array and returns the same response shape containing only the requested identifiers. Unknown identifiers are omitted. The QUERY method is additive and does not replace the GET endpoint; because the HTTP QUERY method is currently an IETF Internet-Draft, clients should retain GET as a compatibility fallback.
QUERY /api/v1/players accepts up to ten Minecraft usernames or UUIDs paired with a required SkyBlock profile UUID. It returns each player in request order with the canonical UUID, first join timestamp, requested profile, bank/purse coins, decoded armor and equipment item lists, and SkyBlock skill level plus XP. The endpoint is public but rate-limited and uses MARKETGUARD_HYPIXEL_API_KEY only on the server. Profile privacy settings or upstream failures can make individual fields unavailable; the response reports that through status and unavailableFields without exposing raw upstream data. If that key is missing or rejected by Hypixel, the route returns HTTP 419 with a generic error detail. The top-level response status is ok or stale when served from the shared cache; clients can also inspect X-Data-Stale. Cache misses for the same normalized request share one in-flight upstream lookup, the upstream work is bounded per worker, and skill definitions are cached for one hour.
QUERY /api/v1/player-finance accepts exactly {playerUuid, profileId} with either compact or dashed UUIDs and returns compact lowercase UUIDs. The public route uses the existing player rate limit and shared response cache. It first verifies through Hypixel /v2/skyblock/profile that the player belongs to the requested profile, then reads /v2/skyblock/museum with the server-side MARKETGUARD_HYPIXEL_API_KEY. Only the requested member is normalized. The response contains bank, purse, Hypixel's authoritative museum value and appraisal, donated position IDs, decoded special exhibit IDs, counts, and knownTotal only when bank, purse, and museum value are all available. Raw Coop members and Museum NBT are never returned. Privacy-disabled or temporarily unavailable Museum data produces status: "partial" with unavailableFields while preserving available bank and purse values. A missing or rejected server key returns HTTP 419. Costs, revenue, profit, and ROI are not available from these upstream fields and are intentionally not estimated or included.
The protected Admin Analytics Metrics page includes the player route's cache-hit rate, average response time, active upstream loads, coalesced requests, and upstream failures. The Compose deployment already supplies its internal API metrics URL. Keep WEB_CONCURRENCY=1 while this route uses the built-in process-local per-IP limiter; Redis shares cached responses but does not make that limiter distributed.
Example QUERY /api/v1/players request:
QUERY /api/v1/players HTTP/1.1
Content-Type: application/json
{"players":[{"player":"Pankraz01","profileId":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]}Example QUERY /api/v1/player-finance request:
QUERY /api/v1/player-finance HTTP/1.1
Content-Type: application/json
{"playerUuid":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","profileId":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}Example complete player-finance result:
{
"status": "ok",
"stale": false,
"fetchedAt": 1715478978620,
"playerUuid": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"profile": {
"id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"name": "Apple",
"selected": true,
"finance": {
"bank": 125000000.0,
"purse": 4250000.5,
"museumValue": 85000000.0,
"knownTotal": 214250000.5
},
"museum": {
"value": 85000000.0,
"appraisal": true,
"donatedIds": ["ASPECT_OF_THE_END", "NECRON_HELMET"],
"donatedCount": 2,
"specialIds": ["DCTR_SPACE_HELM"],
"specialCount": 1
}
},
"unavailableFields": []
}Example QUERY /api/v2/lowestbin request:
QUERY /api/v2/lowestbin HTTP/1.1
Content-Type: application/json
{"products":["HYPERION","TRUE_ESSENCE"]}Example successful player result:
{
"status": "ok",
"players": [
{
"status": "ok",
"uuid": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "Pankraz01",
"firstJoin": 1587483921000,
"profile": {
"id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"name": "Apple",
"selected": true,
"wealth": {
"bank": 125000000.0,
"purse": 4250000.5,
"equipment": [],
"armor": []
},
"skills": {
"farming": {"level": 60, "xp": 111234567.0}
}
},
"unavailableFields": []
}
]
}Example GET /api/v2/lowestbin response:
{
"lastUpdated": 1700000000000,
"products": {
"HYPERION": {
"price": 98000000.0,
"auctioneerUuid": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"item_name": "Hyperion",
"avg7d": 97500000,
"avg30d": 96000000
},
"TRUE_ESSENCE": {
"price": 23437.5,
"auctioneerUuid": "cccccccccccccccccccccccccccccccc",
"item_name": "True Essence",
"avg7d": 22850,
"avg30d": 22120
}
}
}API documentation:
/docs,/redoc, and/openapi.jsonare intended for local development and controlled internal use- the combined production app disables them by default when
TRAINING_HUB_ENV=production - the standalone MarketGuard app can disable them explicitly with
MARKETGUARD_API_DOCS_ENABLED=false
The client upload API is meant for non-browser clients such as a Minecraft mod. The preferred mod path is the anonymous endpoint authenticated by a normalized local clientId plus server-verified SHA-256 headers over the raw NDJSON payload. Browser-backed API use can still rely on the normal external web sign-in session, but password-based API login is no longer part of the supported surface. Do not add custom application-layer crypto on top of TLS without a concrete threat model.
Example anonymous upload:
curl -sS https://scamscreener.creepans.net/api/v1/client/uploads/anonymous \
-X POST \
-H "Content-Type: application/x-ndjson" \
-H "X-ScamScreener-Filename: training-cases-v2.jsonl" \
-H "X-ScamScreener-Client-Id: your-normalized-client-id" \
-H "X-ScamScreener-Payload-Sha256: YOUR_PAYLOAD_SHA256" \
-H "X-ScamScreener-Handshake-Sha256: YOUR_HANDSHAKE_SHA256" \
--data-binary @training-cases-v2.jsonlNotes:
- The anonymous mod endpoint is
POST /api/v1/client/uploads/anonymous. - Users can manually link already-known mod
clientIdvalues fromAccount -> Clients; once linked, historical uploads for that client ID appear in the dashboard. - The server recalculates
X-ScamScreener-Payload-Sha256andX-ScamScreener-Handshake-Sha256; mismatches are rejected with400. /api/v1/client/uploadsaccepts the raw JSONL body and applies the same validation, quotas, deduplication, and audit logging as the dashboard upload form./api/v1/client/uploads/anonymousaccepts the raw JSONL body and applies the same validation, quotas, deduplication, and audit logging without requiring a web login.- Full mod-side integration guidance:
MINECRAFT_MOD_INTEGRATION.md
This repository is licensed under the GNU Affero General Public License v3.0 only.
SPDX identifier: AGPL-3.0-only