feat: add Mine Map v1
This commit is contained in:
1 parent
548051899e
commit
60a831950c
16 files changed
+2027
-163
No files matched your search
+67
-107
@@ -2,16 +2,10 @@
|
||||
|
||||
## Status
|
||||
|
||||
This document is the implementation-ready design baseline for Mine Map v1. It
|
||||
defines the smallest useful spatial operations view that can be built from the
|
||||
current MinerOps location, inventory, telemetry, and repair foundations. It
|
||||
does not claim that the described v1 interface is implemented.
|
||||
|
||||
The current application exposes a legacy Location Map at `/locations`, a Mine
|
||||
Map placeholder at `/monitoring/map`, and a bulk location response at
|
||||
`GET /api/location/map`. Mine Map v1 replaces the placeholder with a read-only,
|
||||
permission-aware operational view. Existing location-management workflows
|
||||
remain available separately and are not redesigned by this specification.
|
||||
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
|
||||
|
||||
@@ -245,8 +239,8 @@ Operational Status is the default layer. It composes current facts with this
|
||||
display precedence:
|
||||
|
||||
1. Location conflict, blocked position, switch, or empty position.
|
||||
2. Lifecycle `retired`.
|
||||
3. Active repair or lifecycle `in_repair`.
|
||||
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**.
|
||||
@@ -310,19 +304,13 @@ physical hierarchy or selection model.
|
||||
|
||||
## Filtering And Search
|
||||
|
||||
V1 filters are allowlisted and based on returned facts:
|
||||
|
||||
- explicit runtime status;
|
||||
- lifecycle;
|
||||
- model or miner type;
|
||||
- authoritative active repair presence;
|
||||
- location state;
|
||||
- position kind/status; and
|
||||
- presence or absence of current telemetry.
|
||||
|
||||
Search supports Asset ID, miner serial, current MAC, current IP, model, room,
|
||||
rack, shelf, and position label where those fields are available. Asset ID and
|
||||
lifecycle require the map response addition described below.
|
||||
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
|
||||
@@ -347,12 +335,12 @@ Where authorized and available, the inspector shows:
|
||||
- miner serial number;
|
||||
- current MAC and IP address;
|
||||
- full site, room, rack, shelf, and position path;
|
||||
- runtime status and last seen time;
|
||||
- runtime status and telemetry observation age;
|
||||
- lifecycle;
|
||||
- active repair status, reason, and repair UUID;
|
||||
- active repair status and reason;
|
||||
- latest hashrate in TH/s;
|
||||
- latest power in watts;
|
||||
- latest chip maximum and average temperature;
|
||||
- latest chip maximum temperature;
|
||||
- telemetry observation time; and
|
||||
- a link to full miner detail.
|
||||
|
||||
@@ -366,30 +354,27 @@ needed by the map response.
|
||||
Shareable state is URL-backed. The initial query contract is:
|
||||
|
||||
```text
|
||||
/monitoring/map?site_id={uuid}&room_id={uuid}&rack={label}&layer=status&miner_id={uuid}
|
||||
/monitoring/map?site_id={uuid}&room_id={uuid}&layer=status&miner_id={uuid}
|
||||
```
|
||||
|
||||
Optional repeated or encoded parameters hold allowlisted filters and search.
|
||||
Rack is a label because racks do not have UUIDs. Unknown, unauthorized, or
|
||||
malformed parameters are ignored with a nonblocking notice; they never broaden
|
||||
data access.
|
||||
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;
|
||||
- optional rack focus;
|
||||
- layer;
|
||||
- filters and search; and
|
||||
- selected miner.
|
||||
|
||||
History state or session state preserves room scroll position, focused tile,
|
||||
and any future map viewport that is too transient for a URL. Selecting or
|
||||
closing a miner updates history so browser Back and Forward restore predictable
|
||||
selection. Refresh and bookmarked links reconstruct the same shareable view.
|
||||
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. Returning restores the prior scroll position when it remains
|
||||
valid. A new tab may be offered as a normal browser link, not forced.
|
||||
query state. A new tab remains available through normal browser behavior.
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
@@ -502,27 +487,17 @@ It returns:
|
||||
or decommissioned location states; and
|
||||
- counts in `meta`.
|
||||
|
||||
Limitations:
|
||||
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.
|
||||
|
||||
- no site ID or site name;
|
||||
- no room UUID in each position;
|
||||
- room ordering joins positions to rooms by case-insensitive room name, which is
|
||||
ambiguous when different sites use the same room name;
|
||||
- no Asset ID;
|
||||
- no lifecycle status;
|
||||
- no authoritative active repair UUID/status/reason;
|
||||
- no tags;
|
||||
- no environment temperature;
|
||||
- no generated-at or response freshness metadata;
|
||||
- no server-side site/room filter; and
|
||||
- integrated `main.py` implementation with no typed response model.
|
||||
|
||||
The endpoint is already efficient in shape. V1 should preserve its path for
|
||||
compatibility and implement an additive, typed map read model through the
|
||||
required Router -> Service -> Repository -> PostgreSQL boundaries. It should
|
||||
accept allowlisted `site_id` and `room_id` filters and add the missing identity,
|
||||
lifecycle, site, and authoritative repair fields. A second competing map
|
||||
endpoint is not required solely for v1.
|
||||
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`
|
||||
|
||||
@@ -569,10 +544,10 @@ needs these fields, but it is not used to populate tiles.
|
||||
|
||||
### `GET /api/miners/{miner_id}/status-snapshots`
|
||||
|
||||
Authentication is required by the route, but the current legacy endpoint does
|
||||
not enforce `miners.view`. Mine Map must not treat authentication alone as
|
||||
sufficient authorization. Any v1 use of this supplemental endpoint requires a
|
||||
backend permission correction to `miners.view` first.
|
||||
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,
|
||||
@@ -603,10 +578,9 @@ 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.
|
||||
|
||||
## Required Map Read Model Additions
|
||||
## Implemented Map Read Model
|
||||
|
||||
Before the full v1 behavior is enabled, the additive `GET /api/location/map`
|
||||
contract must provide:
|
||||
The additive `GET /api/location/map` contract provides:
|
||||
|
||||
- site UUID and name;
|
||||
- room UUID as well as room name;
|
||||
@@ -618,10 +592,12 @@ contract must provide:
|
||||
- per-miner latest telemetry observation time; and
|
||||
- truthful nulls for unavailable fields.
|
||||
|
||||
The query must remain set-based, parameterized, permission-controlled, and
|
||||
bounded by selected scope. The repository owns SQL, the service owns response
|
||||
composition and any future status policy, and the router owns HTTP validation,
|
||||
authorization, and the typed response envelope.
|
||||
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,
|
||||
@@ -638,11 +614,11 @@ lifecycle, repair, or telemetry state.
|
||||
remain authoritative.
|
||||
|
||||
All default roles currently receive both `locations.view` and `miners.view`,
|
||||
but custom role combinations are possible. Before implementing the additive map
|
||||
response, decide whether users with only `locations.view` receive a reduced
|
||||
miner shape or whether the enriched response requires both permissions. The API
|
||||
must not expose fields merely because the standard role matrix usually grants
|
||||
both.
|
||||
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
|
||||
|
||||
@@ -683,7 +659,7 @@ The smallest useful v1 includes:
|
||||
- selected-miner inspector;
|
||||
- existing miner-detail navigation;
|
||||
- URL-backed context and predictable browser history;
|
||||
- loading, empty, denied, error, partial, and refresh-failure states;
|
||||
- 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
|
||||
@@ -713,31 +689,17 @@ The following are explicitly outside v1:
|
||||
- selected-miner telemetry charts; and
|
||||
- generalized infrastructure modelling beyond current position records.
|
||||
|
||||
## Backend And Data Gaps
|
||||
## Remaining Backend And Data Gaps
|
||||
|
||||
These gaps block parts of the acceptance criteria:
|
||||
|
||||
1. **Multi-site location identity:** The current map response omits site and
|
||||
room UUIDs and joins room ordering by name. Same-named rooms across sites
|
||||
cannot be represented reliably until the query is site-scoped.
|
||||
2. **Asset and lifecycle context:** Asset ID and lifecycle are not in the map
|
||||
response. Without them, the map cannot truthfully distinguish spare,
|
||||
retired, and in-repair lifecycle states.
|
||||
3. **Authoritative repair context:** No bulk modular repair projection is joined
|
||||
to map miners. The legacy repair queue is not an equivalent source.
|
||||
4. **Freshness policy:** No single approved stale/down threshold exists. V1 can
|
||||
show explicit offline state and observation age, but cannot classify stale
|
||||
consistently until policy is centralized.
|
||||
5. **Health and thermal policy:** Hashrate, rejection, power, and temperature
|
||||
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.
|
||||
6. **Location vocabularies:** Position and location-state values are not
|
||||
database-constrained. Unknown legacy values require truthful fallback and a
|
||||
later preflight before constraints are added.
|
||||
7. **Rack and shelf identity/order:** Rack and shelf are labels, and racks lack
|
||||
an explicit sort field. V1 can render them but cannot provide durable rack or
|
||||
shelf links or guaranteed operator-defined rack ordering.
|
||||
8. **Map contract:** The current response has no typed model or generated-at
|
||||
metadata and cannot be server-filtered by site or room.
|
||||
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
|
||||
|
||||
@@ -777,15 +739,13 @@ boundary rather than implying complete health classification.
|
||||
|
||||
## Unresolved Decisions
|
||||
|
||||
The following decisions must be made before implementation begins:
|
||||
The following decisions remain for later map and data work:
|
||||
|
||||
1. Whether the enriched map response requires both `locations.view` and
|
||||
`miners.view`, or returns a reduced miner shape to location-only users.
|
||||
2. Which backend-owned policy defines stale, down, hot, and degraded states,
|
||||
1. Which backend-owned policy defines stale, down, hot, and degraded states,
|
||||
including scope, thresholds, collector cadence, and configuration source.
|
||||
3. Whether rack ordering may use a documented natural-label sort for v1 or
|
||||
requires an additive persisted sort field before rollout.
|
||||
4. Whether `location_positions.room_id` can be made required after legacy-data
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user