Skip to content

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

Draft
flesher wants to merge 47 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 47 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

…#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>
@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 (b7ff715db7cf6416509af44792ef86ce68c17940...6e1b3692d3049217f6aaa28629dc810b83c89abf, exact PR three-dot diff)
  • Model: gpt-5.6-sol

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


Review Summary

Overall Risk: HIGH

Findings

[HIGH] Required release checksum assets are never published

  • Category: Reliability
  • Location: server/internal/updater/manager.go:1013
  • Description: The new updater unconditionally downloads <archive>.sha256, but the checked-in artifact and release workflows only package and publish the .tar.gz bundle. They never create or upload this sidecar; the manifest inside the archive does not satisfy this URL.
  • Impact: Official stable and RC upgrades will fail with a 404 before staging, making one-click updates unusable for every normally published release.
  • Recommendation: Generate an outer checksum for each architecture bundle, include it in the build artifact and release asset lists, and add an integration check that verifies the exact assets expected by the updater before publishing.

[MEDIUM] Release verification does not authenticate the publisher

  • Category: Infrastructure
  • Location: server/internal/updater/manager.go:1022
  • Description: The archive and checksum are fetched from the same release origin. The checksum detects corruption but cannot detect an attacker replacing both assets. The accepted archive supplies Docker images, deployment scripts, and potentially a replacement updater executed with host privileges.
  • Impact: Compromise of the release account or publishing workflow could become privileged host and fleet compromise.
  • Recommendation: Publish a signed release manifest and verify it using a pinned signing key or tightly constrained provenance identity before extracting or executing the bundle.

[MEDIUM] Organization-wide MQTT credentials are distributed to every Proto rig

  • Category: Plugin
  • Location: server/internal/domain/curtailment/mqttingest/settings_service.go:396
  • Description: Reconciliation decrypts the organization's MQTT password and fans the same credential out to every paired Proto rig. The plugin then sends it to each miner over a REST client that permits plaintext HTTP and HTTPS without peer verification.
  • Impact: Compromise of one miner—or interception on its LAN path—exposes a reusable broker credential. Depending on broker ACLs, an attacker could observe or forge curtailment signals affecting many miners.
  • Recommendation: Use per-device, revocable, topic-scoped credentials with subscribe-only permissions and deliver them over authenticated TLS or a device-bound encrypted envelope. Avoid reusing Fleet's central ingestion credential on miners.

Notes

Review was limited to the authoritative PR diff and static inspection; tests could not run in the read-only workspace. No pool-address substitution or other cryptostealing behavior was identified, and the protobuf changes appear additive and wire-compatible.


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

flesher and others added 25 commits July 29, 2026 08:29
…ed parents (#559) (#821)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
edgars-avotins and others added 21 commits August 4, 2026 19:30
)

Co-authored-by: Claude <noreply@anthropic.com>
…lding surfaces (#864)

Co-authored-by: Claude <noreply@anthropic.com>
…#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>
…nto migrate-single-miner-to-fleet

# Conflicts:
#	docs/plans/2026-07-28-single-miner-views-on-fleet-backend-plan.md
@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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation client dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code javascript Pull requests that update javascript code review-policy: needs-review Managed by the Review Policy workflow. server shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants