753 lines
31 KiB
Markdown
753 lines
31 KiB
Markdown
# MinerOps Mine Map v1
|
|
|
|
## Status
|
|
|
|
This document is the design baseline for the implemented Mine Map v1. The
|
|
current application exposes the read-only operational view at `/monitoring/map`
|
|
and retains the legacy Location Map at `/locations` for existing location
|
|
management workflows. Those mutation workflows are not redesigned by v1.
|
|
|
|
## Purpose
|
|
|
|
Mine Map v1 answers one primary question:
|
|
|
|
> What is happening physically in the mine right now, and where do I need to
|
|
> look?
|
|
|
|
It must let a technician or manager scan a physical room, identify explicit
|
|
runtime and work states, locate affected miners, understand empty capacity, and
|
|
inspect one miner without losing spatial context. It is an operational tool,
|
|
not a geographic GIS map and not a decorative dashboard.
|
|
|
|
## Current Data Model
|
|
|
|
### Physical Hierarchy
|
|
|
|
The implemented hierarchy is:
|
|
|
|
```text
|
|
Site (UUID)
|
|
-> Room (UUID, belongs to Site)
|
|
-> Rack (text label on a location position)
|
|
-> Shelf (text label and optional numeric index)
|
|
-> Position (UUID and numeric index)
|
|
-> Miner (UUID, optional occupant)
|
|
```
|
|
|
|
`sites` and `rooms` are normalized entities. A room belongs to one site.
|
|
`location_positions` stores `room_id`, denormalized `room_name`, `rack_name`,
|
|
`shelf_name`, optional `shelf_index`, `position_index`, and optional
|
|
`position_label`. Rack and shelf are not independent entities and do not have
|
|
durable UUIDs. Mine Map must therefore treat rack and shelf values as ordered
|
|
labels within a room, not as globally addressable assets.
|
|
|
|
A position may reference one miner. `miner_location_states` separately stores
|
|
the miner's current location state and optional position reference. The miner's
|
|
durable identity remains `miners.id`; neither its position, IP address, nor MAC
|
|
address is asset identity.
|
|
|
|
### Position Vocabulary
|
|
|
|
The current code uses these position kinds:
|
|
|
|
- `miner_slot`
|
|
- `switch`
|
|
- `blocked`
|
|
|
|
The current code uses these position statuses:
|
|
|
|
- `available`
|
|
- `occupied`
|
|
- `switch`
|
|
- `blocked`
|
|
- `conflict`
|
|
|
|
These values are used by the active implementation but are not constrained by
|
|
the database. V1 must render unknown values as **Unknown position state** and
|
|
must not silently treat them as empty or occupied. A later schema task should
|
|
constrain the vocabulary after legacy-data preflight.
|
|
|
|
The current miner location-state vocabulary is:
|
|
|
|
- `shelved`
|
|
- `waiting_placement`
|
|
- `storage`
|
|
- `repair`
|
|
- `decommissioned`
|
|
|
|
These values are also not database-constrained. They describe location or
|
|
placement state, not miner lifecycle and not authoritative repair status.
|
|
|
|
### Lifecycle And Repair State
|
|
|
|
Miner lifecycle is independently constrained to:
|
|
|
|
- `active`
|
|
- `spare`
|
|
- `retired`
|
|
- `in_repair`
|
|
|
|
The modular Repairs domain uses `repairs` and immutable `repair_events` as the
|
|
authoritative repair workflow. Active repair statuses are `OPEN`,
|
|
`IN_PROGRESS`, `WAITING_FOR_PARTS`, and `READY_FOR_TEST`. `COMPLETED` and
|
|
`CANCELLED` are not active repairs.
|
|
|
|
The legacy location API currently exposes `miner_location_states.asset_state`
|
|
but does not expose lifecycle or the authoritative active repair. Mine Map v1
|
|
must not equate legacy `asset_state = repair` with a modular repair record.
|
|
|
|
## User Outcomes
|
|
|
|
V1 must support these outcomes without requiring location mutation:
|
|
|
|
- Select a site and room and see its known physical layout.
|
|
- Scan racks, shelves, and positions in their stored order.
|
|
- Distinguish occupied, explicitly empty, blocked, switch, conflict, and
|
|
incomplete positions.
|
|
- Identify miners explicitly reported offline or hash-unstable.
|
|
- Identify miners in the authoritative repair lifecycle or an active repair.
|
|
- Distinguish active, spare, retired, and in-repair assets.
|
|
- See latest available hashrate, power, and temperature values with timestamps.
|
|
- Inspect a miner and open its existing detail page.
|
|
- Return to the same room, rack, layer, filters, selection, and scroll context.
|
|
|
|
## Page Composition
|
|
|
|
Mine Map uses the current authenticated application shell at
|
|
`/monitoring/map`. The page contains four functional regions.
|
|
|
|
### Context Bar
|
|
|
|
The context bar contains:
|
|
|
|
- site selector;
|
|
- room selector;
|
|
- layer selector, defaulting to **Operational Status**;
|
|
- search;
|
|
- filters;
|
|
- data freshness summary; and
|
|
- a link to the existing location-management page when the user has
|
|
`locations.manage`.
|
|
|
|
The selected site limits the room selector. The selected room is required for
|
|
the primary rack layout. An optional all-room summary may show room counts, but
|
|
v1 must not render every tile from every room by default at large sites.
|
|
|
|
### Spatial Layout
|
|
|
|
The main region renders the selected room as ordered rack groups. Each rack
|
|
contains shelf rows, and each row contains positions ordered by
|
|
`position_index`. The layout uses ordinary semantic DOM elements and CSS grid.
|
|
Canvas, WebGL, pan physics, and a geographic basemap are not required.
|
|
|
|
The visual order follows stored room `sort_order`, shelf `shelf_index` where
|
|
present, and position index. Rack ordering is label-based because no rack sort
|
|
field exists. The UI must not infer missing racks, shelves, or positions from
|
|
gaps in names or numbers.
|
|
|
|
### Legend And Summary
|
|
|
|
The legend is always visible for the active layer and pairs color with text and
|
|
an icon or pattern. A compact summary reports only counts derivable from the
|
|
loaded response, such as occupied positions, empty positions, conflicts,
|
|
explicit offline miners, in-repair miners, and unknown-status miners.
|
|
|
|
Counts must identify their scope and freshness. They must not claim site-wide
|
|
coverage when only one room is loaded or when data is partial.
|
|
|
|
### Miner Inspector
|
|
|
|
Selecting an occupied miner opens a side panel on desktop. On narrow screens it
|
|
opens a full-height sheet with an obvious close control. The map remains the
|
|
underlying context and selection is visible on the selected tile.
|
|
|
|
Closing the inspector restores focus to the tile that opened it. Opening the
|
|
full miner detail view uses the existing durable route:
|
|
|
|
```text
|
|
/miners/{miner_id}/detail
|
|
```
|
|
|
|
## Position And Miner Rendering
|
|
|
|
### Occupied Positions
|
|
|
|
An occupied miner tile shows, in priority order:
|
|
|
|
- position label or `Position {position_index}`;
|
|
- Asset ID when available, otherwise current MAC, otherwise **Unidentified
|
|
miner**;
|
|
- operational condition text and icon;
|
|
- the active layer's value; and
|
|
- observation age or **Never observed**.
|
|
|
|
The position remains the visible frame; filtering and status styling must not
|
|
make its physical address ambiguous.
|
|
|
|
### Empty And Non-Miner Positions
|
|
|
|
Empty positions are operationally meaningful and remain visible.
|
|
|
|
| Condition | V1 presentation |
|
|
| --- | --- |
|
|
| `miner_slot`, no miner, no expected MAC, `available` | **Empty position** |
|
|
| `miner_slot`, expected MAC, no miner | **Expected miner not resolved**; not empty |
|
|
| `blocked` kind or status | **Blocked position** with reason when available |
|
|
| `switch` kind or status | **Switch/equipment position** with equipment label |
|
|
| `conflict` or `conflict_reason` | **Location conflict**; never shown as empty |
|
|
| Unknown kind or status | **Unknown position state** |
|
|
| Filtered occupied miner | Tile remains in place but is visually deemphasized |
|
|
| Missing response data | **Unavailable**, distinct from an empty position |
|
|
|
|
An absent position record is not an empty position. V1 must not synthesize a
|
|
tile merely because a numeric position is missing from a shelf sequence.
|
|
|
|
### Unracked And Operationally Removed Miners
|
|
|
|
Miners returned in `virtual_miners` appear outside the rack canvas in an
|
|
**Unplaced and operational areas** section grouped by the stored location
|
|
state. V1 maps the stored values to plain labels without inventing workflow:
|
|
|
|
- `waiting_placement`: Waiting for placement
|
|
- `storage`: Storage
|
|
- `repair`: Legacy repair location state
|
|
- `decommissioned`: Decommissioned location state
|
|
|
|
Lifecycle is shown separately. A spare miner is not automatically in storage,
|
|
and a retired miner is not automatically decommissioned. Where lifecycle and
|
|
location state disagree, both are displayed and the discrepancy is not hidden.
|
|
|
|
### Repair Miners
|
|
|
|
A miner with `lifecycle_status = in_repair` or an authoritative active repair is
|
|
visually unmistakable through text, icon, and a repair treatment independent of
|
|
the selected data layer.
|
|
|
|
V1 distinguishes:
|
|
|
|
- in repair while still assigned to a physical position;
|
|
- in repair with no rack position;
|
|
- repair complete with lifecycle `active` or `spare`, awaiting placement; and
|
|
- legacy `asset_state = repair` without an authoritative active repair.
|
|
|
|
The last case is labelled **Legacy repair location state** rather than being
|
|
presented as a confirmed active repair.
|
|
|
|
## Operational Status Model
|
|
|
|
Operational Status is the default layer. It composes current facts with this
|
|
display precedence:
|
|
|
|
1. Location conflict, blocked position, switch, or empty position.
|
|
2. Active repair or lifecycle `in_repair`.
|
|
3. Lifecycle `retired` or decommissioned location state.
|
|
4. Lifecycle `spare`.
|
|
5. Runtime status explicitly equal to `offline`.
|
|
6. `hash_stable = false`, labelled **Hash unstable**.
|
|
7. Runtime status explicitly equal to `online`.
|
|
8. Unknown or unrecognized runtime status.
|
|
|
|
Lifecycle and repair treatments do not erase runtime facts. For example, an
|
|
in-repair miner may also show that its last runtime observation was online.
|
|
|
|
`miners.status` is free text and not database-constrained. V1 must not infer
|
|
that arbitrary values containing `error`, `warn`, or `fault` mean a particular
|
|
severity. Unknown values may be displayed as secondary raw context after safe
|
|
formatting.
|
|
|
|
### Freshness
|
|
|
|
`last_seen_at` and latest telemetry `observed_at` are available, but MinerOps has
|
|
no single authoritative staleness policy. Existing legacy screens use
|
|
inconsistent thresholds, including 10 and 30 minutes. Therefore:
|
|
|
|
- v1 always displays observation time or age;
|
|
- missing timestamps are **Never observed** or **Freshness unknown**;
|
|
- explicit runtime `offline` may be shown as Offline;
|
|
- v1 does not label a miner Stale based on age until a centralized policy is
|
|
approved and supplied by the backend; and
|
|
- late or failed refreshes keep the last successful values visible with a
|
|
prominent **Update failed** or **Data may be stale** state.
|
|
|
|
### Conditions Requiring Policy
|
|
|
|
The following labels require an approved backend policy and are not inferred by
|
|
v1:
|
|
|
|
- stale based on observation age;
|
|
- down based on missing hashrate or elapsed time;
|
|
- hot or overheating based on temperature;
|
|
- degraded based on hashrate, rejection rate, power, or temperature; and
|
|
- low hashrate relative to model expectation.
|
|
|
|
The legacy Location Map and dashboard endpoints contain hard-coded thresholds,
|
|
but they are not a consistent policy contract. V1 may display raw measurements
|
|
and the explicit `hash_stable` boolean without adopting those thresholds.
|
|
|
|
## Layer Model
|
|
|
|
The layer selector is extensible, but the first implementation is not required
|
|
to enable every layer.
|
|
|
|
| Layer | V1 status | Current data | Required behavior |
|
|
| --- | --- | --- | --- |
|
|
| Operational Status | Required, default | Position kind/status, runtime status, lifecycle after API addition, `hash_stable`, timestamps | Uses the factual precedence above; no invented thresholds |
|
|
| Repair State | Required presentation, optional standalone layer | Lifecycle and modular repair after API addition; legacy location state exists now | Repair treatment remains visible on every layer |
|
|
| Temperature | Optional v1 value layer | Latest chip maximum and average are already in the map response; environment temperature exists in snapshots but is omitted | Show numeric value and timestamp; no hot classification without policy |
|
|
| Hashrate | Optional v1 value layer | Latest average hashrate in TH/s is already in the map response | Show value and missing-data state; no low-hashrate threshold |
|
|
| Power | Optional v1 value layer | Latest power in watts is already in the map response | Show value and missing-data state; no density or efficiency inference |
|
|
| Thermal heat map | Future | Point temperatures exist for some miners | Requires approved thresholds, aggregation semantics, coverage disclosure, and legend |
|
|
|
|
The frontend implements layers through a shared tile contract rather than
|
|
separate maps. Adding a future layer changes tile content and legend, not the
|
|
physical hierarchy or selection model.
|
|
|
|
## Filtering And Search
|
|
|
|
The implemented filters are allowlisted and use returned miner facts: derived
|
|
operational status, lifecycle, model, and authoritative active-repair presence.
|
|
Search covers Asset ID, miner serial, current MAC, current IP, and model. Location
|
|
context remains visible while nonmatching occupied tiles are deemphasized. Users
|
|
without `miners.view` receive none of these miner fields; miner search, miner
|
|
filters, and non-status layers are disabled rather than operating on redacted
|
|
values.
|
|
|
|
Filtering preserves physical context. Positions remain in the layout;
|
|
nonmatching occupied tiles are deemphasized and marked as filtered. A separate
|
|
**Show only matches** density option may collapse nonmatching rack groups, but
|
|
it is off by default and clearly indicates that context is hidden. Empty,
|
|
blocked, switch, and conflict positions remain visible unless explicitly
|
|
filtered by position type.
|
|
|
|
No-results differs from load failure and from an empty room:
|
|
|
|
- **No positions configured**: the selected room has no position records.
|
|
- **No miners match these filters**: layout remains visible, with miners
|
|
deemphasized.
|
|
- **Map unavailable**: the request failed or returned an invalid response.
|
|
|
|
## Miner Inspector Fields
|
|
|
|
Where authorized and available, the inspector shows:
|
|
|
|
- Asset ID;
|
|
- model or miner type;
|
|
- miner serial number;
|
|
- current MAC and IP address;
|
|
- full site, room, rack, shelf, and position path;
|
|
- runtime status and telemetry observation age;
|
|
- lifecycle;
|
|
- active repair status and reason;
|
|
- latest hashrate in TH/s;
|
|
- latest power in watts;
|
|
- latest chip maximum temperature;
|
|
- telemetry observation time; and
|
|
- a link to full miner detail.
|
|
|
|
Null values render as **Unavailable**. Numeric zero is displayed as zero, not as
|
|
missing. The panel must not fetch one API resource per visible tile. A single
|
|
detail request for the selected miner is acceptable when it supplies fields not
|
|
needed by the map response.
|
|
|
|
## Context Preservation
|
|
|
|
Shareable state is URL-backed. The initial query contract is:
|
|
|
|
```text
|
|
/monitoring/map?site_id={uuid}&room_id={uuid}&layer=status&miner_id={uuid}
|
|
```
|
|
|
|
Optional encoded parameters hold allowlisted filters and search. Rack and shelf
|
|
do not have URL identifiers because they are labels rather than durable entities.
|
|
Unknown, unauthorized, or malformed parameters are discarded and the canonical
|
|
URL is replaced; they never broaden data access.
|
|
|
|
The URL preserves:
|
|
|
|
- selected site and room;
|
|
- layer;
|
|
- filters and search; and
|
|
- selected miner.
|
|
|
|
Selecting or closing a miner updates history so browser Back and Forward restore
|
|
predictable URL-backed selection. Refresh and bookmarked links reconstruct the
|
|
same valid shareable state. Scroll position is not persisted by v1.
|
|
|
|
When full miner detail opens in the same tab, the return URL contains the map
|
|
query state. A new tab remains available through normal browser behavior.
|
|
|
|
## Responsive Behavior
|
|
|
|
### Desktop
|
|
|
|
- Context controls remain visible above the map.
|
|
- The room layout occupies the main content width.
|
|
- The inspector uses a fixed-width side panel without resizing rack tiles.
|
|
- Horizontal scrolling is contained within an individual wide rack or map
|
|
viewport, not the whole page.
|
|
|
|
### Tablet And Phone
|
|
|
|
- Site, room, layer, and filter controls stack without hiding the active scope.
|
|
- One rack is shown at a time when the full room cannot remain legible.
|
|
- Shelf rows retain physical position order and allow contained horizontal
|
|
scrolling.
|
|
- The inspector becomes a full-screen sheet.
|
|
- Closing the sheet restores the selected tile and scroll location.
|
|
- Touch targets meet the shared UX guide sizing rules.
|
|
|
|
The mobile view does not turn positions into an unrelated flat miner list. Rack
|
|
and shelf context remains visible.
|
|
|
|
## Accessibility
|
|
|
|
Mine Map v1 must:
|
|
|
|
- communicate every state with text and an icon or pattern, not color alone;
|
|
- provide one page heading and semantic site, room, rack, shelf, and position
|
|
labels;
|
|
- expose each rack as a labelled region and each shelf as a labelled group;
|
|
- expose miner tiles as buttons when they open the inspector and noninteractive
|
|
elements when they do not;
|
|
- include an accessible name containing position, miner identity, condition,
|
|
and freshness;
|
|
- support logical keyboard movement through positions in stored spatial order;
|
|
- preserve visible focus and return focus after closing the inspector;
|
|
- provide keyboard-operable filters, layer selection, scrolling, and zoom or
|
|
density controls;
|
|
- avoid hover-only values;
|
|
- support browser zoom without clipping labels or controls; and
|
|
- avoid required motion and respect reduced-motion preferences if motion is
|
|
introduced later.
|
|
|
|
A screen-reader summary provides counts and a structured rack/shelf/position
|
|
list. It describes empty and blocked positions rather than omitting them.
|
|
|
|
## Loading, Failure, And Partial Data
|
|
|
|
The map has explicit states:
|
|
|
|
- **Loading**: layout skeletons preserve the eventual page structure.
|
|
- **Ready**: layout and response scope are known.
|
|
- **Empty room**: valid response with no configured positions.
|
|
- **No configured sites or rooms**: directs authorized users to existing
|
|
location management without implying a load failure.
|
|
- **Denied**: permission failure without leaking counts or miner previews.
|
|
- **Error**: request failed or response shape is invalid; remains visible until
|
|
retry or navigation.
|
|
- **Partial data**: physical layout is usable, but one or more miner fields or
|
|
telemetry observations are unavailable.
|
|
- **Refresh failed**: last successful map remains visible, clearly marked with
|
|
the failure time.
|
|
|
|
The frontend validates the response envelope and required position identifiers.
|
|
A malformed response is an error, not an empty room.
|
|
|
|
## Performance And Refresh
|
|
|
|
V1 is designed for hundreds or thousands of positions without prematurely
|
|
adopting canvas or WebGL.
|
|
|
|
- Fetch a map-shaped bulk response; never issue one request per position.
|
|
- Load one selected site and room where backend filtering is available.
|
|
- Render the visible room first and defer secondary summaries.
|
|
- Build rack and shelf fragments off-DOM, then attach them in batches.
|
|
- Update changed tile content without replacing the full page or shell.
|
|
- Keep layout refresh separate from telemetry refresh because positions change
|
|
less frequently than observations.
|
|
- Do not overlap refresh requests; cancel or ignore superseded responses.
|
|
- Pause automatic telemetry refresh while the page is hidden.
|
|
- Base any polling interval on collector cadence or backend guidance; this
|
|
document does not invent an interval.
|
|
- Preserve last successful values during transient failure and expose their
|
|
observation timestamps.
|
|
- Introduce list virtualization only after measurement shows the selected-room
|
|
DOM is too large; accessibility and keyboard order must be preserved.
|
|
|
|
The current bulk location query selects the latest snapshot per miner and is a
|
|
useful foundation. Before production use at large scale, its query plan and
|
|
indexes must be measured with representative site data.
|
|
|
|
## Current API Assessment
|
|
|
|
### `GET /api/location/map`
|
|
|
|
Permission: `locations.view`.
|
|
|
|
This is the primary existing map-shaped response and avoids N+1 tile requests.
|
|
It returns:
|
|
|
|
- `data.positions`: position UUID, room/rack/shelf labels, shelf and position
|
|
indexes, position label/kind/status, expected MAC, equipment metadata,
|
|
conflict information, miner UUID, current MAC/IP, runtime status, hostname,
|
|
serials, model/type, firmware, last seen, legacy location state, and latest
|
|
hashrate, power, chip temperatures, hash stability, rejection percentage, and
|
|
observation time;
|
|
- `data.virtual_miners`: miners in waiting-placement, storage, legacy repair,
|
|
or decommissioned location states; and
|
|
- counts in `meta`.
|
|
|
|
The implemented additive response also supplies site and room UUIDs, Asset ID,
|
|
lifecycle, authoritative active repair context, generated-at metadata, typed
|
|
contracts, and optional server-side site and room filters. Its read path follows
|
|
Router -> Service -> Repository -> PostgreSQL. Users with `locations.view` but
|
|
without `miners.view` receive physical layout and occupancy with miner identity,
|
|
telemetry, repair, and virtual-miner details removed.
|
|
|
|
Tags and environment temperature are not included because v1 does not require
|
|
them. Rack and shelf remain labels. The frontend renders legacy positions without
|
|
`room_id` in a separate **Unscoped legacy positions** section; it does not attach
|
|
their denormalized room labels to a selected site or room.
|
|
|
|
### `GET /api/sites`
|
|
|
|
Permission: `locations.view`.
|
|
|
|
Returns site UUID, name, description, and timestamps. It supplies the site
|
|
selector. It does not include room or map counts.
|
|
|
|
### `GET /api/rooms`
|
|
|
|
Permission: `locations.view`.
|
|
|
|
Returns room UUID, site UUID/name, room name/description, sort order, and
|
|
timestamps. It supplies site-scoped room selection.
|
|
|
|
### `GET /api/location/rooms-order`
|
|
|
|
Permission: `locations.view`.
|
|
|
|
Returns room UUID/name/order and position, miner-slot, switch, blocked, and
|
|
assigned-miner counts. Its location join is name-based and not site-scoped, so
|
|
it cannot be the sole source for a correct multi-site selector.
|
|
|
|
### `GET /api/inventory/miners`
|
|
|
|
Permission: `miners.view`.
|
|
|
|
Returns durable miner UUID, Asset ID, current MAC/IP, model, lifecycle, runtime
|
|
status, site/room UUIDs, tags, and timestamps. Search covers Asset ID, MAC, IP,
|
|
model, miner serial, control-board serial, PSU serial, and notes. The collection
|
|
is bounded to 500 records per request and has no physical position or telemetry.
|
|
|
|
It can validate the missing identity fields during development, but Mine Map
|
|
must not join paginated inventory calls in the browser for every room. The map
|
|
read model should include the small identity and lifecycle subset it needs.
|
|
|
|
### `GET /api/inventory/miners/{miner_id}`
|
|
|
|
Permission: `miners.view`.
|
|
|
|
Adds hostname, miner serial, control-board serial, PSU serial, firmware, notes,
|
|
and first-seen time. One call after selection is acceptable when the inspector
|
|
needs these fields, but it is not used to populate tiles.
|
|
|
|
### `GET /api/miners/{miner_id}/status-snapshots`
|
|
|
|
Authentication and `miners.view` are required. Admin is allowed by the existing
|
|
admin override; Manager, Technician, Read Only, and custom roles are authorized
|
|
according to their effective `miners.view` permission. Mine Map does not call
|
|
this endpoint per tile.
|
|
|
|
Returns up to 100 recent snapshots with hashrate, power, efficiency,
|
|
environment and chip temperatures, fans, uptime, stability, rejection rate,
|
|
performance mode, timestamps, and raw JSON. It is too heavy for every tile and
|
|
must not be called per visible miner. It may support a future selected-miner
|
|
trend, which is outside v1.
|
|
|
|
### Repair APIs
|
|
|
|
`GET /api/repairs/{repair_id}` and its event route expose authoritative modular
|
|
repair detail when a repair UUID is already known. There is no current modular
|
|
read endpoint that returns the active repair for each map miner.
|
|
|
|
`GET /api/repairs/queue` is a bulk legacy queue based on
|
|
`miner_location_states.asset_state = repair` and legacy maintenance events. It
|
|
does not represent the modular `repairs` table and must not be used as the
|
|
authoritative Mine Map repair source.
|
|
|
|
The additive map read model therefore needs a set-based join to at most one
|
|
active modular repair per miner. It must not perform per-miner repair lookups.
|
|
|
|
### Dashboard APIs
|
|
|
|
`GET /api/dashboard/miner-table` exposes latest telemetry in a fleet table.
|
|
The hot, unstable, and stale endpoints expose useful operational lists, but
|
|
they use endpoint-specific or caller-supplied thresholds. Mine Map does not
|
|
need these endpoints because the location map already includes the relevant raw
|
|
latest measurements. V1 must not combine their classifications into a new
|
|
status policy by accident.
|
|
|
|
## Implemented Map Read Model
|
|
|
|
The additive `GET /api/location/map` contract provides:
|
|
|
|
- site UUID and name;
|
|
- room UUID as well as room name;
|
|
- Asset ID;
|
|
- lifecycle status;
|
|
- a nullable active repair object containing repair UUID, status, and reason;
|
|
- response generation time;
|
|
- explicit scope metadata for selected site and room;
|
|
- per-miner latest telemetry observation time; and
|
|
- truthful nulls for unavailable fields.
|
|
|
|
The query is set-based, parameterized, permission-controlled, and uses four
|
|
fixed database executions independent of fleet size. Optional site and room UUID
|
|
filters are supported by the API; the current frontend performs one bulk request
|
|
and renders the selected room locally. The repository owns SQL, the service owns
|
|
response composition, and the router owns HTTP validation, authorization, and
|
|
the typed response envelope.
|
|
|
|
V1 does not require a write endpoint. Reading the map, changing layer, applying
|
|
filters, selecting a miner, or opening the inspector must never mutate location,
|
|
lifecycle, repair, or telemetry state.
|
|
|
|
## Permissions
|
|
|
|
- The page and location layout require `locations.view`.
|
|
- Supplemental Inventory detail requires `miners.view`.
|
|
- The existing miner detail link is shown only when the user can view miners.
|
|
- The location-management link is shown only with `locations.manage`.
|
|
- No placement or repair command is exposed by Mine Map v1.
|
|
- Frontend permission checks control presentation only; backend dependencies
|
|
remain authoritative.
|
|
|
|
All default roles currently receive both `locations.view` and `miners.view`,
|
|
but custom role combinations are possible. The backend implements the boundary:
|
|
`locations.view` alone returns layout and occupancy while redacting Asset ID,
|
|
miner UUID, expected/current MAC, IP, serials, model, runtime/lifecycle/location
|
|
state, telemetry, active repair, and virtual-miner data. This is authoritative
|
|
server-side redaction rather than frontend hiding.
|
|
|
|
## Architecture Boundary
|
|
|
|
Mine Map v1 follows:
|
|
|
|
```text
|
|
Frontend page
|
|
-> FastAPI router
|
|
-> Location map service/read model
|
|
-> Location repository
|
|
-> PostgreSQL
|
|
```
|
|
|
|
The frontend contains no SQL, status policy, repair reconstruction, or location
|
|
mutation. The read repository may join location, Inventory-owned projections,
|
|
latest telemetry, and active repair state for this explicit read model. It does
|
|
not write those domains. The service maps persistence results into the typed
|
|
map contract and preserves null and unknown values.
|
|
|
|
The existing integrated route may be migrated incrementally behind the same
|
|
path. The legacy `/locations` page remains available during rollout. Mine Map
|
|
v1 must not copy its inline HTML/JavaScript or hard-coded classification rules
|
|
into the new frontend shell.
|
|
|
|
## V1 Implementation Boundary
|
|
|
|
The smallest useful v1 includes:
|
|
|
|
- real site and room selection;
|
|
- real racks, shelves, positions, and miners;
|
|
- explicit empty, blocked, switch, conflict, and unknown positions;
|
|
- unplaced and operational-area miners;
|
|
- Operational Status as the default layer;
|
|
- repair and lifecycle treatments based on authoritative data;
|
|
- raw latest telemetry values and freshness;
|
|
- status legend and scoped counts;
|
|
- basic allowlisted filters and search;
|
|
- selected-miner inspector;
|
|
- existing miner-detail navigation;
|
|
- URL-backed context and predictable browser history;
|
|
- loading, empty, denied, error, and partial-data states;
|
|
- desktop, tablet, and phone behavior;
|
|
- keyboard, screen-reader, touch, zoom, and reduced-motion support;
|
|
- permission-aware presentation; and
|
|
- focused route, contract, rendering, permission, accessibility, and context
|
|
preservation tests.
|
|
|
|
Location creation, movement, import, rack/shelf editing, and position-type
|
|
changes remain in existing management workflows. V1 may link to those workflows
|
|
but does not reproduce them.
|
|
|
|
## Deferred Capabilities
|
|
|
|
The following are explicitly outside v1:
|
|
|
|
- advanced thermal heat maps;
|
|
- historical map playback;
|
|
- drag-and-drop or click-to-move relocation;
|
|
- map, rack, shelf, or position editing;
|
|
- predictive failure visualization;
|
|
- animated telemetry;
|
|
- power-density and electrical-capacity modelling;
|
|
- saved map views;
|
|
- multi-site geographic maps;
|
|
- multi-selection and bulk commands;
|
|
- repair, lifecycle, or component mutations;
|
|
- alerts and notifications;
|
|
- selected-miner telemetry charts; and
|
|
- generalized infrastructure modelling beyond current position records.
|
|
|
|
## Remaining Backend And Data Gaps
|
|
|
|
1. **Freshness policy:** No single approved stale/down threshold exists. V1
|
|
shows explicit offline state and observation age but does not classify stale.
|
|
2. **Health and thermal policy:** Hashrate, rejection, power, and temperature
|
|
measurements exist, but model-aware degraded and hot thresholds do not.
|
|
3. **Location vocabularies:** Position and location-state values are not
|
|
database-constrained. Unknown legacy values require truthful fallback.
|
|
4. **Rack and shelf identity/order:** Rack and shelf are labels, and racks lack
|
|
an explicit sort field. V1 cannot provide durable rack or shelf links or
|
|
operator-defined rack ordering.
|
|
|
|
## Acceptance Criteria
|
|
|
|
Mine Map v1 is accepted when all of the following are true:
|
|
|
|
- An authorized user can select a site and room and see every configured
|
|
position in stored spatial order.
|
|
- Occupied, empty, expected-but-unresolved, blocked, switch, conflict, unknown,
|
|
and unavailable states are visually and semantically distinct.
|
|
- Filtering does not silently erase physical context.
|
|
- Explicit offline, hash-unstable, spare, retired, and in-repair miners are
|
|
identifiable without inventing thresholds.
|
|
- Every telemetry value is tied to an observation time or marked unavailable.
|
|
- A user can identify whether apparent problems cluster by room, rack, or shelf
|
|
from factual tile states and measurements.
|
|
- A selected position identifies its miner or states why no miner is present.
|
|
- The inspector shows the key current identity, location, lifecycle, repair,
|
|
runtime, and telemetry details available to the user.
|
|
- The full miner detail page can be opened using the durable miner UUID.
|
|
- Closing the inspector or returning from detail restores the exact shareable
|
|
map context and practical scroll/focus context.
|
|
- Browser Back, Forward, refresh, bookmarks, and shared URLs behave
|
|
predictably.
|
|
- Permission failures expose no unauthorized counts, previews, search results,
|
|
or inspector fields.
|
|
- The page remains usable with keyboard, screen reader, browser zoom, touch,
|
|
narrow viewports, missing telemetry, partial records, and transient refresh
|
|
failures.
|
|
- The implementation performs no per-tile or per-miner request fan-out.
|
|
- The map is read-only; existing location management remains available.
|
|
|
|
Until the freshness and health-policy gaps are resolved, the map can answer
|
|
which miners are explicitly offline, hash-unstable, in repair, or associated
|
|
with location conflicts. It cannot truthfully claim that every down, stale,
|
|
hot, or degraded miner has been identified. The interface must state that
|
|
boundary rather than implying complete health classification.
|
|
|
|
## Unresolved Decisions
|
|
|
|
The following decisions remain for later map and data work:
|
|
|
|
1. Which backend-owned policy defines stale, down, hot, and degraded states,
|
|
including scope, thresholds, collector cadence, and configuration source.
|
|
2. Whether rack ordering may use a documented natural-label sort or requires an
|
|
additive persisted sort field.
|
|
3. Whether `location_positions.room_id` can be made required after legacy-data
|
|
preflight, eliminating name-only room association for production map data.
|
|
|
|
These decisions do not require changing the durable miner identity, existing
|
|
repair workflow, or v1 read-only boundary.
|