Files
minerops/docs/tasks/004-control-board-replacement.md
T

10 KiB

Task 004: Control Board Replacement

Status

Design specification. Not yet implemented.

Objective

Add an explicit, auditable workflow for replacing a miner control board while preserving durable asset identity, network-identity history, repair context, and immutable operational history.

Product Intent

A miner is a durable physical asset. Its control board is replaceable. A replacement may change the MAC address and control-board serial, but it must not create a new miner or erase prior history.

Current implementation context: miners.id is the durable asset UUID. miners.current_mac and miners.control_board_sn are current-state fields. miner_network_identities already stores MAC, IP, hostname, source, seen timestamps, and an is_current marker for a miner. The legacy miner_maintenance_events table can contain MAC and component details, but those records are editable, deletable, and not part of the immutable, repair-scoped timeline. No dedicated control-board component-history table exists.

The integrated application also exposes the legacy POST /api/miners/{miner_id}/change-mac route, and Inventory, discovery, and location-import code can write current network fields. These are existing compatibility paths, not implementations of Task 004.

Scope

  • proposed route POST /api/miners/{miner_id}/replace-control-board
  • proposed permission miners.replace_control_board
  • require an active repair and miner lifecycle in_repair
  • normalize and validate the new MAC address
  • reuse and strengthen miner_network_identities for MAC history rather than creating a competing MAC-history table
  • update miners.current_mac and miners.control_board_sn as current-state projections
  • preserve the old and new control-board serials in immutable repair history
  • append one immutable CONTROL_BOARD_REPLACED event
  • execute atomically and safely under concurrency
  • add migration, constraints, tests, and documentation

Out of Scope

Automatic discovery reconciliation, generic component inventory, stock quantities, purchasing, warranties, firmware flashing, pool changes, remote commands, bulk replacement, automatic miner merge, UI redesign, and replacement of hashboards, fans, or PSUs.

Permission

The proposed permission is:

miners.replace_control_board

Proposed role assignment, following the implemented operational-action policy:

  • Admin: allowed
  • Manager: allowed
  • Technician: allowed
  • Read Only: denied

The permission and assignments do not exist yet. They must be introduced by the Task 004 migration. The route must check the capability rather than hard-code role names.

Authentication and Response

Authentication is required. On success, return 200 OK using a typed response envelope. The response must identify the durable miner UUID, resolved active repair UUID, current MAC, current control-board serial, and appended event ID and sequence without exposing unrelated miner data.

Request

Suggested shape:

{
  "new_mac_address": "AA:BB:CC:DD:EE:FF",
  "new_control_board_serial": "CB-2026-001234",
  "notes": "Replacement board installed and tested."
}

The new MAC is required. new_control_board_serial is a required, nullable field so the operator explicitly records a serial or confirms that it is unknown. A null or normalized blank value clears the old current-board serial; it must never leave the removed board serial as current. Normalize nonblank serials and limit them to the Inventory field maximum of 255 characters.

Notes are optional, trim like existing repair notes, treat whitespace-only as absent, and use the established 4,000-character limit. The client must not supply the old MAC, old serial, repair UUID, actor, timestamp, event sequence, or event payload; the service derives those values from authoritative state.

MAC Rules

Define and test one canonical display format, such as AA:BB:CC:DD:EE:FF, while comparing ownership by a normalized 12-hex-digit key. Existing data must be checked for normalized conflicts before a uniqueness constraint is added.

Reject malformed, broadcast, multicast, all-zero, and currently duplicated MAC addresses. A MAC historically associated with another asset must produce a conflict requiring a later deliberate transfer or correction workflow rather than automatic reassignment. Task 004 must not silently rewrite, merge, or delete a conflicting association.

Durable Identity Rules

  • miner UUID and asset record remain unchanged
  • current MAC changes
  • prior MAC remains associated with the same miner in miner_network_identities
  • repair, location, ownership, and asset-ID history remain unchanged
  • no second miner is auto-created
  • no automatic asset merge occurs

Preconditions

  1. Miner exists.
  2. Miner is in_repair.
  3. Miner has exactly one active repair.
  4. New MAC is valid and canonical.
  5. New MAC is not current or historical on another miner.
  6. When a current MAC exists, old and new MAC differ.
  7. Actor and timestamp are authoritative.

Data Model

Task 004 must build on the existing structures:

  • miners.current_mac remains the current MAC projection.
  • miners.control_board_sn remains the current control-board serial projection.
  • miner_network_identities remains the MAC-history structure. The migration may add only the constraints or indexes required to enforce one current association and prevent cross-miner normalized-MAC ownership conflicts.
  • repair_events remains the immutable repair timeline. Extend its constrained event vocabulary and payload validation for CONTROL_BOARD_REPLACED.

Do not create a second MAC-history table. Do not treat legacy miner_maintenance_events as authoritative component history for this workflow. A separate component-installation table is outside this task unless repository inspection during implementation identifies a concrete query or integrity requirement that cannot be met by the current projections, network identity history, and immutable repair event. Report that conflict before adding such a table.

Constraints

  • no more than one current network-identity row per miner
  • one miner owner per normalized MAC across current and historical identities
  • valid normalized MAC for rows written by the new workflow
  • current projections agree with the newly current identity after replacement
  • foreign-key behavior preserves operational history

The migration must preflight existing normalized MAC duplicates and fail with an actionable message rather than changing existing ownership silently.

Legacy Data

Existing current MAC values may be reconciled with miner_network_identities only when the association is unambiguous. Existing identity rows and legacy maintenance events must be preserved. Unknown installation actor, time, control-board serial, or source must remain unknown; never fabricate a replacement event.

Event

Proposed type: CONTROL_BOARD_REPLACED.

Suggested payload:

{
  "old_mac_address": "CC:0C:15:00:0A:49",
  "new_mac_address": "CC:0C:15:00:9D:82",
  "old_control_board_serial": null,
  "new_control_board_serial": "CB-2026-001234",
  "notes": "Replacement board installed and tested."
}

Include only authoritative values.

Atomicity and Locking

Use one transaction and the established lock order:

Miner -> Repair -> network-identity rows

The service resolves and locks the miner first, then its one active repair, then the relevant network-identity rows. This matches Tasks 001 and 003.

The operation must atomically validate MAC ownership, mark prior network identities non-current, create or activate the new association without reassigning another miner's history, update the current miner fields, and append exactly one event to the active repair.

Concurrent requests must not assign one MAC to two miners, create two current boards, duplicate event sequence values, or leave partial state.

Errors

  • 401: unauthenticated
  • 403: authenticated user lacks miners.replace_control_board
  • 404: miner missing
  • 409: miner not in repair, no active repair, same current MAC, MAC conflict, historical ownership conflict, or concurrent replacement
  • 422: malformed UUID, invalid MAC, missing required fields, excessive text, unknown fields, or other invalid request data

Architecture

Preserve Router -> Service -> Repository -> PostgreSQL.

No SQL or transaction management in routers. No FastAPI in services or repositories. Do not add generic component CRUD.

Tests

Cover:

  • role authorization
  • valid replacement
  • durable asset identity unchanged
  • old MAC remains searchable
  • malformed and prohibited MACs
  • current and historical MAC conflicts
  • duplicate existing normalized-MAC migration preflight
  • missing active repair
  • miner not in repair
  • concurrent replacement
  • rollback at each write step
  • direct database constraints
  • event payload and sequence
  • Tasks 001-003 regressions
  • migration upgrade, downgrade/refusal, re-upgrade, and empty database

Decisions Required Before Implementation

Before Task 004 is marked ready for implementation, decide:

  1. Whether the legacy /api/miners/{miner_id}/change-mac route will delegate to the new service or be deprecated through an explicit compatibility plan. It must not remain a competing writer that bypasses the active-repair workflow.
  2. Whether an operator-recorded replacement MAC counts as a network observation for the existing first_seen_at and last_seen_at semantics, or whether the schema needs a distinct association timestamp. Do not label an unobserved MAC as discovered accidentally.
  3. How Inventory creation, discovery, and location-import writers will translate normalized-MAC ownership conflicts without exposing raw database errors or auto-merging assets.

These decisions do not permit automatic asset creation, merging, or MAC transfer. A separate deliberate transfer workflow remains out of scope.

Acceptance Criteria

  1. One explicit route exists.
  2. Durable miner identity remains unchanged.
  3. Old and new MACs remain searchable.
  4. Normalized MAC ownership and current-association invariants are database-backed.
  5. Active repair and in_repair are required.
  6. Exactly one immutable event is written.
  7. Operation is atomic and concurrency-safe.
  8. Legacy data remains truthful.
  9. Migration and regression suites pass.
  10. No commit or push is performed.

Completion Report

Report branch, starting commit, migration, files, route, permission, schema, constraints, normalization, history, transaction strategy, event, tests, validation, limitations, and final git status --short.

Do not commit. Do not push.