Skip to content

proto(lab): fleet-native single-miner view — discovery + strategy prototypes [PROTOTYPE] - #827

Draft
flesher wants to merge 9 commits into
mainfrom
migrate-single-miner-to-fleet
Draft

proto(lab): fleet-native single-miner view — discovery + strategy prototypes [PROTOTYPE]#827
flesher wants to merge 9 commits into
mainfrom
migrate-single-miner-to-fleet

Conversation

@flesher

@flesher flesher commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Draft / prototype branch — not for merge. Dev-only Lab at /lab exploring how to migrate ProtoFleet's single-miner view toward fleet-server rendering.

What's here

Discovery (docs/): the fleet-native single-miner-view research + API parity map, plus a strategies RFC (docs/rfcs/0003-single-miner-view-strategies.md).

Three working prototypes in the Lab, each rendering the same shared <SingleMinerView> (identity + 3 KPIs + ASIC mini-grid + one control) so they can be compared apples-to-apples:

  • S1 · Fleet-native — the view sourced entirely from the fleet server (ListMinerStateSnapshots). Framed as reading the fleet proto directly; the ASIC grid is hand-waved (fleet stores device-level scalars today).
  • S2 · Proxy (versioned) — probes /api/version, picks the right MDK client via a version seam, and proxies requests to the miner.
  • S3 · Adapter — one generic seam, swappable backend adapters (fleet / MDK v1 / MDK v2). List filtered to first-party rigs; direct MDK contexts auto-connect.

Data-flow pane

A collapsible "Data flow" drawer narrates each strategy's plumbing like a color-coded network tab:

  • blue client → fleet RPC · violet fleet ⇽ miner (collection) · green client → miner REST · teal version seam (S2) · amber adapter layer (S3)
  • POST calls get a solid method badge (GET a muted one); proxied calls show a "via fleet proxy" chip
  • events append sequentially, clearing only on a new connection or prototype switch

Testing

Runs against the fake rigs (just lab-fakes, v1 :18081 / v2 :18082) and a live fleet-api session. tsc / eslint / prettier clean.

🤖 Generated with Claude Code

@github-actions github-actions Bot added documentation Improvements or additions to documentation review-policy: needs-review Managed by the Review Policy workflow. labels Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (e022aad84cc92f071eb603de015cb8dfba4cb0c7...4cf72eb6ee7973e2866eb8170f6f2bf0d739bb70, exact PR three-dot diff)
  • Model: gpt-5.6-sol

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: MEDIUM

Findings

[MEDIUM] Wildcard CORS exposes the entire simulator API to arbitrary origins

  • Category: Auth
  • Location: server/fake-proto-rig/main.go:117
  • Description: The prototype CORS middleware is applied unconditionally to every fake-rig endpoint and permits arbitrary origins plus authorization headers.
  • Impact: A malicious website visited while the simulator is reachable can authenticate using repository-known development credentials and invoke protected mutations such as mining, reboot, or pool changes. It also weakens browser-isolation testing outside the Lab.
  • Recommendation: Enable CORS only behind an explicit Lab flag, restrict allowed origins, scope it to required endpoints, and bind Lab instances to loopback.

[MEDIUM] MDK v2 telemetry bypasses authentication

  • Category: Auth
  • Location: server/fake-proto-rig/mdk_v2.go:71
  • Description: /api/v2/miner is registered without requireBearerAuth, although it returns device identity, network information, performance, and per-chip telemetry corresponding to protected v1 data.
  • Impact: Anyone able to reach an MDK v2 simulator can read its detailed state. More importantly, the simulator and client validate an authentication-free v2 integration that would be unsafe if carried into the real protocol.
  • Recommendation: Keep /api/version public, but require bearer authentication for /api/v2/miner and have the v2 adapter authenticate before requesting telemetry.

[MEDIUM] Every version-probe failure silently selects the legacy adapter

  • Category: Reliability
  • Location: client/src/protoFleet/prototypes/adapter/probe.ts:38
  • Description: Network failures, aborts, server errors, authorization errors, and malformed responses are all interpreted as evidence of pre-probe v1 firmware.
  • Impact: A transient v2 probe failure can successfully fall through to the fake rig's still-available v1 API, display the wrong client, and expose controls using the wrong protocol assumptions.
  • Recommendation: Fall back only for an explicit unsupported-endpoint response such as 404/405. Propagate aborts, network failures, authentication failures, server errors, and invalid response schemas.

[MEDIUM] Lab cleanup can terminate unrelated processes

  • Category: Reliability
  • Location: justfile:582
  • Description: trap 'kill 0' EXIT signals every process in the recipe's process group rather than only the two spawned fake rigs.
  • Impact: In non-interactive shells and CI, exiting or interrupting the recipe can terminate the invoking shell, wrapper processes, or unrelated concurrent work in the same process group.
  • Recommendation: Capture both child PIDs and terminate/wait for those explicit PIDs in the exit trap.

[MEDIUM] Production routing exposes a view containing fabricated hardware health

  • Category: Frontend
  • Location: client/src/protoFleet/router.tsx:392
  • Description: The throwaway Lab is registered unconditionally, so production builds include /lab. Its fleet-native view presents synthesized ASIC temperatures, hashrates, and offline cells as a device grid.
  • Impact: An operator reaching the hidden route can mistake invented component failures or health for fleet telemetry and make incorrect maintenance decisions.
  • Recommendation: Exclude the route and chunks from production builds. Where component telemetry is unavailable, render an explicit unavailable state instead of synthetic health data.

Notes

The changed pool seed is confined to an explicitly enabled fake-rig mode; no cryptostealing or production pool-redirection path was identified in this diff.


Generated by Codex Security Review |
Triggered by: @flesher |
Review workflow run

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code automation javascript Pull requests that update javascript code client server shared labels Aug 7, 2026
@flesher flesher changed the title docs: fleet-native single-miner view — discovery (#826) proto(lab): fleet-native single-miner view — discovery + strategy prototypes [PROTOTYPE] Aug 7, 2026
Comment on lines +3 to +6
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
if err != nil {
return nil, err
}
ephemeral, err := ecdh.X25519().GenerateKey(rand.Reader)
if len(payload.GetCiphertext()) > maxCiphertext {
return sdk.CurtailmentConfig{}, fmt.Errorf("encrypted curtailment config is %d bytes; maximum is %d", len(payload.GetCiphertext()), maxCiphertext)
}
recipient, err := ecdh.X25519().NewPrivateKey(privateKey)
if err != nil {
return sdk.CurtailmentConfig{}, fmt.Errorf("parse recipient private key: %w", err)
}
ephemeral, err := ecdh.X25519().NewPublicKey(payload.GetEphemeralPubkey())
if len(publicKey) != x25519KeySize {
return nil, fmt.Errorf("%w: must be %d bytes, got %d", ErrInvalidRecipientPublicKey, x25519KeySize, len(publicKey))
}
recipient, err := ecdh.X25519().NewPublicKey(publicKey)
nodeDir := filepath.Join(outputDir, host.name)
if err := writeFile(filepath.Join(nodeDir, "service-ca.crt"), ca.certPEM, 0o644); err != nil {
return err
}
serial, err := randomSerial()
if err != nil {
return err
}
}
keyDER, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return fmt.Errorf("encode %s key: %w", name, err)
hex.Encode(encoded, raw)
return encoded, nil
}

return fmt.Errorf("write %s: %w", filepath.Base(path), err)
}
return nil
}
flesher and others added 9 commits August 10, 2026 11:18
…#826)

Discovery brief for migrating the single-miner view off the miner-API
proxy onto fleet-collected data: as-is architecture, data-gap analysis,
full ProtoOS -> ProtoFleet API parity map, and a phased plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two sub-discoveries linked from the main single-miner-view plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Throwaway prototype scaffolding (never merges): /lab preview surface, shared
SingleMinerView + snapshot contract, MDK_VERSION knob + divergent /api/v2 on
fake-proto-rig, and a just lab-fakes recipe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…TYPE]

Add SingleMinerAdapter seam and two direct browser→miner adapters that fold
divergent wire formats into one SingleMinerSnapshot rendered by the shared view:

- MdkV1Adapter: today's multi-endpoint /api/v1 REST (login + system + mining +
  hashboards); synthesizes the ASIC grid around avg ASIC temp (v1 has no bulk
  per-chip data — the honest limitation).
- MdkV2Adapter: faked consolidated GET /api/v2/miner with real per-chip data.
- probe.ts: GET /api/version picks the adapter (falls back to v1 pre-probe).
- AdapterPage: base-URL/IP input + version probe + refresh; presets target the
  two fake rigs, any reachable miner IP works.

Fake rig: top-level permissive CORS (browser→rig needs it — a real S3 finding),
FAKE_RIG_MINING seed so the Lab shows a mining device, and fix v2State to match
the real MiningState constants ("Mining"/"DegradedMining").

Throwaway; never merges to main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FleetAdapter sources identity + KPIs entirely from the fleet server via the
existing FleetManagementService.ListMinerStateSnapshots RPC (by /32 ipCidr) over
/api-proxy — never touches the device. FleetNativePage IS the single-miner mode:
type a miner IP (or "pick a discovered miner") → resolve fleet-native → render
the shared SingleMinerView.

The ASIC grid is synthesized from device-level temperature, with a code-cited
callout on why: the collector builds HashBoards→ASICs in memory
(plugins/mappers/sdk_mapper.go) but persistence drops the arrays
(timescaledb/telemetry_store.go:366) and no client RPC exposes them. Making the
grid real = a new prototype/v1 Connect RPC calling miner.GetDeviceMetrics(ctx)
on demand (interfaces/miner.go:59) — mechanism exists, deferred (each server
change needs a full image rebuild; ASIC source for these rigs is unconfirmed).

Throwaway; never merges to main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Probe the miner's version and mount a bespoke, version-specific client instead
of the shared view — the strategy's whole point (and cost): you ship and
maintain a separate client per firmware generation.

- ProtoOSv1Mini: renders the v1 REST surface in its own idiom (login + system +
  mining + hashboards; board hardware list, no per-chip grid — v1 has none).
- ProtoOSv2Mini: renders the v2 consolidated envelope with a native per-chip
  grid. Structurally and visually distinct from v1 on purpose.
- ProxyVersionedPage: GET /api/version → mount the matching client; presets for
  both fake rigs so you can compare the two UIs side by side.

Lab fetches hit the fake rigs directly; the same code targets the minerproxy
path (/api-proxy/miners/:id) in production.

Throwaway; never merges to main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reflect what actually shipped: S1 uses existing ListMinerStateSnapshots +
synthesized grid (prototype/v1 RPC deferred, not built), fake-rig additions,
and the lab-fakes recipe. Add a "Running it" section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…PROTOTYPE]

S3 now offers all three backends behind the one generic view: a "Fleet server"
option (reusing FleetAdapter — identity + KPIs from ListMinerStateSnapshots over
/api-proxy, ASIC grid synthesized) alongside the MDK v1/v2 direct path. Shows
what a single-miner UI rendered inside ProtoFleet off the fleet backend looks
like, folded into the same SingleMinerView as the MDK adapters. Includes a
"pick a discovered miner" helper for a valid fleet IP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Refactor the single-miner Lab so all three strategies render one shared
<SingleMinerView>, and add a collapsible "Data flow" pane that narrates
each strategy's plumbing.

- Split the view into shared pieces: MinerViewFrame (chrome + Details
  modal), MinersList (tabular Name / IP / firmware), SingleMinerDetails
  (data-path ribbon), plus status/asicData helpers.
- Data-flow trace bus (flowTrace.ts + FlowPane.tsx): adapters emit
  semantic events (requests, notes, version seam, adapter layer); the
  page-configured tracer colors them by hop and flags proxying.
  - blue: client -> fleet RPC; violet: fleet <- miner collection;
    green: client -> miner REST; teal: version seam (S2);
    amber: adapter layer (S3).
  - POST calls get a solid method badge; GET a muted one.
  - Trace appends sequentially, clearing only on new connection or
    prototype switch; control actions (pause/resume) append.
- S1 fleet-native: hand-waves the synthetic ASIC grid; runs under a
  fleet-native transport so the adapter row is suppressed (the view is
  framed as reading the fleet proto directly).
- S3 adapter: fleet is one adapter among three (adapter row shown);
  list filtered to first-party rigs; direct MDK contexts auto-connect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@flesher
flesher force-pushed the migrate-single-miner-to-fleet branch from 6e1b369 to 4cf72eb Compare August 10, 2026 18:25
@github-actions github-actions Bot removed dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code shared labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation client documentation Improvements or additions to documentation javascript Pull requests that update javascript code review-policy: needs-review Managed by the Review Policy workflow. server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants