Skip to content

layer07/ubit-eye

Repository files navigation

N|Solid

License: GPL-2.0-or-later

μBiT::EYE — WhatsMiner ASIC Monitor

Real-time monitoring for WhatsMiner ASIC fleets. One binary. No Docker. No dependencies. Boots in 500ms.

Built because MicroBT's stock UI looks like it was designed in 1998 and tells you nothing useful when your hashboards are on fire.


What It Does

  • Scans your network for WhatsMiner devices on port 4028
  • Polls telemetry in real-time — hashrate, temperatures, power, fan speeds, chip data, PSU diagnostics
  • Monitors per-slot hashboard health down to individual chip temperatures
  • Detects errors with 150+ WhatsMiner error codes translated to human-readable meanings AND solutions
  • Exports everything to Prometheus at /metrics — plug Grafana and go
  • Handles both WhatsMiner firmware response formats transparently (some wrap in Msg envelope, some don't — we normalize both)

Quick Start

dotnet build
dotnet run

Open http://localhost:1442. That's it.

Prometheus metrics at http://localhost:1442/metrics.

Configuration lives in app.xml — ports, HTTPS, CORS, everything. Change it from the UI via CONFIG_SET or edit the file directly.


Architecture

┌──────────────────────────────────────────────────────┐
│                      μBiT::EYE                       │
│                                                      │
│  Kestrel ──→ SignalR Hub ──→ CommandDispatch         │
│  HTTP/WS     (LiteHub)       Will → Handler          │
│     │             │               │                  │
│     │       WiredStream      MinerService            │
│     │       (OPENFIRE)       (TCP to ASICs)          │
│     │                                                │
│  /metrics ── PrometheusCollector                     │
│              Per-miner, per-slot, per-PSU telemetry  │
└──────────────────────────────────────────────────────┘

Stack:

  • C# / .NET 10 — single binary, single process
  • SignalR over WebSocket with MessagePack binary protocol
  • Command pattern — Will string → handler → WiredAnswer with microsecond benchmarks
  • Vanilla JS frontend, zero frameworks
  • Native Prometheus exporter — no sidecar, no agent

API Commands

Every command goes over WebSocket. Self-documenting — send LIST_ENDPOINTS_OPTIMIZED to get the full schema at runtime.

Mining

Command Description
MINER_SCAN Sweep IP range, discover WhatsMiner devices
MINER_POLL Query all registered miners, return telemetry snapshots
MINER_GET Full detail for single miner — devices, PSU, errors
MINER_ADD Manually register a miner by IP
MINER_REMOVE Remove miner from registry
MINER_LIST List registered miners (no TCP, instant)
MINER_CLEAR Wipe miner registry

Server Administration

Command Description
SERVER_STATUS Uptime, memory, connections, ports, command stats
SERVER_RESTART Restart Kestrel — reloads config, client auto-reconnects
SERVER_CONNECTIONS List active WebSocket connections
CONFIG_GET Dump running configuration
CONFIG_SET Change config values, writes to app.xml, reports if restart needed
CONFIG_RELOAD Re-read app.xml from disk without restart

Diagnostics

Command Description
PING Connectivity test (~8µs round trip)
NTP_TIME Server time for clock sync detection
ECHO Echo payload for serialization testing

Introspection

Command Description
LIST_ENDPOINTS_OPTIMIZED All commands with request/response schemas
GET_DOC_API Schema for a specific command
LIST_MODULES List all modules and their commands
GET_MODULE_SCHEMA Full schema for all commands in a module

Prometheus Metrics

GET http://localhost:1442/metrics

Server Metrics

liteioctl_uptime_seconds 3421.5
liteioctl_memory_bytes 104857600
liteioctl_connections 3
liteioctl_commands_total 1847
liteioctl_command_avg_us{will="PING"} 8.6
liteioctl_command_avg_us{will="MINER_POLL"} 4521.3

Per-Miner Telemetry

miner_online{id="56",ip="192.168.15.56"} 1
miner_hashrate_ths{id="56",ip="192.168.15.56"} 62.10
miner_temp_board{id="56",ip="192.168.15.56"} 74.0
miner_temp_chip_max{id="56",ip="192.168.15.56"} 89.16
miner_temp_env{id="56",ip="192.168.15.56"} 42.5
miner_power_watts{id="56",ip="192.168.15.56"} 1792
miner_efficiency_jpth{id="56",ip="192.168.15.56"} 28.86
miner_fan_in{id="56",ip="192.168.15.56"} 6334
miner_fan_out{id="56",ip="192.168.15.56"} 6510
miner_accepted{id="56",ip="192.168.15.56"} 6301
miner_rejected{id="56",ip="192.168.15.56"} 7
miner_uptime_seconds{id="56",ip="192.168.15.56"} 1700936

Per-Slot Hashboard Metrics

miner_slot_hashrate_ths{id="56",ip="192.168.15.56",slot="0"} 22.53
miner_slot_hashrate_ths{id="56",ip="192.168.15.56",slot="1"} 21.31
miner_slot_hashrate_ths{id="56",ip="192.168.15.56",slot="2"} 18.26
miner_slot_temp{id="56",ip="192.168.15.56",slot="0"} 74.0
miner_slot_chip_temp_max{id="56",ip="192.168.15.56",slot="0"} 89.23
miner_slot_chips{id="56",ip="192.168.15.56",slot="0"} 78
miner_slot_freq{id="56",ip="192.168.15.56",slot="0"} 476

PSU Metrics

miner_psu_temp{id="56",ip="192.168.15.56",model="P222B"} 58.0
miner_psu_voltage_in{id="56",ip="192.168.15.56"} 212.5
miner_psu_fan{id="56",ip="192.168.15.56"} 9824

Error Codes

miner_error_active{id="74",ip="192.168.15.74",code="352",meaning="Hashboard 2 over-temperature protection triggered",category="Temp Sensor",severity="temp"} 1
miner_error_active{id="74",ip="192.168.15.74",code="600",meaning="Ambient temperature too high",category="Environment",severity="temp"} 1

Error Code Database

150+ WhatsMiner error codes with human-readable meanings, solutions, categories, and severity levels. Covers fans, PSU, temperature sensors, EEPROM, hashboards, chips, firmware, pools, and security.

Every error returned by the API includes:

{
  "Code": 352,
  "Meaning": "Hashboard 2 over-temperature protection triggered",
  "Solution": "Check ambient temperature",
  "Category": "Temp Sensor",
  "Severity": "temp",
  "Timestamp": "2026-04-06 02:41:36"
}

Firmware Compatibility

WhatsMiner has two different API response formats depending on firmware version:

Format A (M30S++, M50, etc.) — data comes direct:

{"STATUS":[...],"SUMMARY":[...],"id":1}

Format B (M30S_V10, etc.) — data wrapped in Msg envelope:

{"STATUS":"S","When":...,"Msg":{"STATUS":[...],"SUMMARY":[...]}}

μBiT::EYE detects and normalizes both formats transparently. The frontend never sees the difference.


Configuration

All settings in app.xml. Changeable at runtime via CONFIG_SET (writes to disk) or by editing the file and calling CONFIG_RELOAD.

Setting Default Description
HTTPPorts/Port 1442 HTTP listen port
HTTPSPorts/Port 1443 HTTPS listen port
HTTPSCertificate/Enabled false Enable TLS
HTTPSCertificate/FilePath wired_dev.pfx Certificate path
WebRoot html Static file directory
SignalR/MaxMessageSizeMB 10 Max WebSocket message size
SignalR/KeepAliveSeconds 15 Ping interval
CorsOrigins/Origin (all) Allowed origins
SpaRewrites/Path /app, /blog SPA fallback routes
Domain localhost Server domain

Changes to ports, HTTPS, or SignalR settings require SERVER_RESTART. Logging and domain changes apply immediately.


Development Mock Server

No ASICs at home? Run the Python mock server:

python mock_miners.py

Serves 3 fake miners with real telemetry data on localhost:

Port Miner Notes
4028 M30S++ (ID 56) Clean, no errors
4029 M30S_V10 (ID 44) Different firmware envelope format
4030 M30S++ (ID 74) Active error codes (352, 600)

Hashrate and temperatures jitter ±5% per poll for realistic chart testing.

// Register mock miners from browser console
BrutalNetwork.send('MINER_ADD', { IP: '127.0.0.1', Port: 4028, Label: 'Mock 56' })
BrutalNetwork.send('MINER_ADD', { IP: '127.0.0.1', Port: 4029, Label: 'Mock 44' })
BrutalNetwork.send('MINER_ADD', { IP: '127.0.0.1', Port: 4030, Label: 'Mock 74' })

// Poll
BrutalNetwork.send('MINER_POLL').then(r => console.log(r.Obj))

Requirements

  • .NET 10 SDK
  • That's it

No Docker. No Node. No npm. No webpack. No node_modules black hole. Single binary, ~33MB RAM, runs on a Raspberry Pi.


Security

  • Read-only — only queries miners via standard WhatsMiner API on port 4028. Same commands the stock UI uses. Never writes to firmware.
  • No authentication data stored — miner registry is in-memory only, lost on restart.
  • Config on diskapp.xml is the only persistent file. No database. No state files.

License

GPL-2.0-or-later


Author: D. Leatti (Forbannet) — kernelriot.comgithub.com/layer07

Donations: bc1qjj5vqw9t6pl4lhydsspll075skfuxgqkj7u97mko-fi.com/soloween

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages