26 Commits

Author SHA1 Message Date
Dorian
68b606b489 Persist reward contribution ledger 2026-05-09 17:10:47 +01:00
Dorian
c256726cfa Track miner reward contribution 2026-05-09 16:59:06 +01:00
Dorian
641a086d62 Recognize Bitaxe user agent 2026-05-09 16:56:44 +01:00
Dorian
4064e16ea8 Use host-published Datum admin port 2026-05-09 16:49:01 +01:00
Dorian
4b28f760c5 Use Umbrel DNS for Datum polling 2026-05-09 16:41:33 +01:00
Dorian
99bbd83c34 Harden Datum container discovery 2026-05-09 16:36:15 +01:00
Dorian
87e114a2aa Discover Datum container IP at runtime 2026-05-09 16:33:45 +01:00
Dorian
2bbb89c53f Stabilize Datum container networking 2026-05-09 16:26:18 +01:00
Dorian
7a85e805a7 Identify BigPapa as Bitaxe 2026-05-08 21:51:23 +01:00
Dorian
0a6571a590 Use room key encryption for private chat 2026-05-06 21:32:47 +01:00
Dorian
0ff7bc46fc Move chat to private authenticated API 2026-05-06 21:04:50 +01:00
Dorian
cb21c693d0 Make Nostr chat private 2026-05-06 20:56:29 +01:00
Dorian
b715c3f27d Add Nostr chat drawer 2026-05-06 19:52:20 +01:00
Dorian
fa707e2464 Add network stats and rotating mining jokes 2026-05-06 19:43:28 +01:00
Dorian
0c8e26f597 Add PWA support and miner race 2026-05-06 19:21:06 +01:00
Dorian
360b1ebe66 Rename unknown miner BigPapa 2026-05-06 19:00:04 +01:00
Dorian
c2c376f8b9 Add miner shameboard calculations 2026-05-06 18:47:51 +01:00
Dorian
c77c74612d Add graph telemetry and remote signer login 2026-05-06 18:09:58 +01:00
Dorian
83b3a60497 fix(api): reject datum proxy shell responses 2026-05-06 17:19:19 +01:00
Dorian
2c80340039 fix(api): tame noisy logs and overlapping polls 2026-05-06 17:06:49 +01:00
Dorian
aae760331c fix(api): allow LAN HTTP asset loading 2026-05-06 16:56:54 +01:00
Dorian
00a1258458 fix(docker): make Portainer repository deploy buildable 2026-05-06 16:54:27 +01:00
Dorian
0fef84e3eb fix(datum): surface fetch error cause + url in logs
Node's fetch wraps the underlying network error in `.cause`; the bare
`err.message` is just "fetch failed" which tells us nothing about
DNS vs connection refused vs network unreachable. Add formatErr() that
walks .cause and includes its .code, plus the url being attempted, so
logs distinguish between the actual failure modes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 16:37:08 +01:00
Dorian
8f7aeb88b7 chore: serve everything on port 1337
User-facing port unified at 1337 — vibes-aligned and easier to remember
than 8080/8420. Updates: api default PORT, .env.example, docker-compose
mapping (1337:1337), healthcheck target, Dockerfile EXPOSE, Vite dev
proxy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 16:33:01 +01:00
Dorian
de353878f6 feat(api): nostr nip-98 login, jwt sessions, datum digest poller
Backend in @gashboard/api (Express 5 + TS, ~1.2k LoC):

Auth (NIP-98 over HTTP, lifted from indeehub pattern):
  - Client signs a kind-27235 event with method+URL, base64s as
    Authorization: Nostr <event>. Server verifies sig, freshness
    (±120s), method/URL tags via constant-time string compare.
  - npub allowlist decoded to hex once at boot, fail-closed if any
    entry is malformed or list is empty.
  - HS256 JWT sessions returning {token, npub, expiresAt}.
  - express-rate-limit on POST /api/auth/login (10/min/IP).

Datum integration (the trickier half):
  - HTTP Digest *SHA-256* client (community-fork Datum uses sha-256,
    not md5; node has no first-class support — hand-rolled in
    digest.ts: parse challenge → ha1=sha256(user:realm:pw),
    ha2=sha256(method:URI), response=sha256(...) → retry).
  - HTML parsers for /clients (per-worker) and /threads (auth-less
    fallback) using node-html-parser.
  - Profile matcher: UserAgent contains "NerdQAxe" → NerdQAxe;
    else worker-name suffix on auth username → workerNameMatchers.
    Live UA strings observed: NerdQAxe self-IDs; Bitaxe / Avalon
    Nano 3 / Avalon Mini 3 all report cgminer/4.11.1, must match
    via workername.
  - 5s poll interval, 10s AbortController timeout per upstream call,
    in-memory snapshot, /api/datum/stats + SSE /api/datum/stream.

Hardened-by-default Express setup:
  helmet CSP (frame-ancestors 'none', script-src 'self'),
  pino with redaction (auth header, *.password, *.token, *.jwt,
  *.sig), AppError class + central errorHandler, zod env validation,
  graceful shutdown on SIGTERM/SIGINT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:58:35 +01:00
Dorian
2dc9be4678 chore: scaffold pnpm workspace, container, deploy docs
Two-app pnpm workspace for the gashboard (mining dashboard) project:
@gashboard/api (Express 5 + TS) and @gashboard/web (Vue 3 + Vite + TS).
Shared tsconfig.base.json. Multi-stage Dockerfile (node:22.12-alpine,
non-root, healthchecked) and docker-compose.yml ready to deploy as a
Portainer Stack on Umbrel — joins umbrel_main_network so it can reach
the Datum container directly. .env.example documents every var; README
covers the Portainer deploy flow and the security posture.

Note: Dockerfile has a TODO marker to SHA256-pin the base image before
shipping to production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:57:57 +01:00