Skip to content

Asset Identity Gaps (8.0)

Inventory of what is done vs still missing after the Asset / Device / Assignment cutover. Grounded in code on the asset-uuid branch and the TST000 (safe.ssh) upgrade dry-run.

Design freeze: Asset Identity. Operator guide: Asset identity & calibration. Customer DB dry-run: scripts/upgrade/.

Status matrix

AreaDoneGapSeverityEvidence
Asset / device / assignment schemaMigrations 075088; backfill + open assignmentsserver/migrations/07*08*
Ingest keyed by asset_uidMQTT dmac → assignment → history under uidserver/src/events.ts, cache.ts
Replace-device (future swaps)Single + batch API/UI; history stays on uiddevice_assignment.ts, Device Pool UI
History rewrite on upgradesensor_data, events, tracking, .pb, remint mapdry-run VALIDATION_REPORT.md
Leftover-MAC auditCLI --fail-on-leftoverscripts/audit/leftover_mac_history.ts
Zabbix host identityLLD emits {#ASSET_UID}Host still {#NAME} with MAC suffix; prototypes use {#NAME}; replace-device creates a new Zabbix hostHighSee Zabbix
LLD schema / typesHTTP payload includes uid + SIDValibot LLD schemas omit {#ASSET_UID} / {#SID} (bridge strips extras); lld.d.ts staleMediumlib/src/schemas.ts, lib/src/types/lld.d.ts, bridge.ts
Asset history mergeOrdered admin preview/apply UI + APIs; exclusive sensor windows; assignment lineage; source retirementSee Merge
Location codes → real UIDsMint path + remint 084 + admin remint API/UIGateway/e-ink still mint-on-edit onlyLow (ops)Assets settings → Remint LEGACY UIDs
E-ink wire MACPoll ?mac= kept; admin has uid + replaceOptional eink_mac_normalize thin-wrap deferredLowroadmap deferred
E-ink layout cellsCells store sensor_uid; migrate 103; designer picker is UIDInterface-template charts still bind dmacs[] to device MACMediumlib/src/eink.ts, 103_eink_layout_sensor_uid
Grafana legendsPrefer beacon name (6i)LowCHANGELOG 6i
External MAC path/WS clientsPhase 6c hard cutCommunicate breaking change to integratorsMedium (comms)docs/reference/api/http.md
Migration dry-run toolingcustomer_db_verify / customer_db_soakPoint operators at it from install docs more prominentlyLowdocs/guide/asset-identity-calibration.md

Zabbix / bridge

Design intent vs reality

Design (asset-identity.md) states: {#ASSET_UID} primary; {#MAC} = current device; bridge resolves wire dmac → asset host.

LayerCurrent behavior
GET /lld/beacons / /lld/gatewaysEmits {#ASSET_UID} (fallback to mac if missing)
{#NAME} hostnameWT_SENSOR-{name}-{MAC} via beacon_get_zabbix_hostnameMAC baked into host identity
Zabbix host prototypehost: '{#NAME}' in scripts/cli/zabbix.ts
Bridge LLD cacheKeyed by {#MAC}correct for MQTT wire lookup
Bridge → trapperUses cached LLD name as Zabbix host string
Valibot LLDBeaconSchema / LLDGatewaySchemaMissing {#ASSET_UID}, {#SID}, gateway SID — unknown keys stripped on parse
mermaid
flowchart LR
  MQTT["MQTT dmac"] --> Bridge["bridge cache by MAC"]
  Bridge -->|"trapper host = LLD NAME"| ZabbixHost["Zabbix host WT_SENSOR-name-MAC"]
  LLD["GET /lld/beacons"] -->|"NAME embeds MAC"| ZabbixHost
  LLD -.->|"ASSET_UID emitted but unused for host id"| Unused["unused for prototypes"]

Replace-device breakage

After Replace device, SafeCall keeps asset_uid and rewrites denormalized beacons.mac. LLD then emits a new {#NAME} (new MAC suffix). Zabbix LLD discovers a new host; trapper metrics and historical items remain on the old host. That defeats stable asset identity for monitoring.

TST000 (safe.ssh) environment

Observed on the test install (2026-08):

  • Live 7.7.6 LLD has no {#ASSET_UID}; sample: WT_SENSOR-Senimed_1-BC57290E3A95.
  • zabbix-server inactive; only zabbix-agent2 packaged. Bridge server_bridge_zabbix_host=localhost / port 10051 — not a full Zabbix reference stack.
  • Use a customer or lab Zabbix for any host-identity cutover verification; do not treat TST000 alone as Zabbix UAT.
  1. Make Zabbix host identity stable: host prototype host: '{#ASSET_UID}' or change {#NAME} to a uid-stable string (no MAC suffix); keep {#MAC} / {#NAME} as visible macros/items.
  2. Align LLDBeaconSchema, lld.d.ts, bridge doctor/verify, and docs/zabbix/.
  3. Document one-time Zabbix cleanup: old MAC-suffixed hosts → disable/delete after LLD re-discovery on uid hosts; refresh LLD after remint (084) as already noted in the operator guide.
  4. Bridge may keep MAC-keyed cache for MQTT; optionally also index by uid for debugging.

Asset history merge (shipped)

Old replace vs new replace

WorkflowWhat happensHistory
New: Replace deviceSame asset row / asset_uid; MAC swaps; assignment closed/openedStays on one asset_uid key in sensor_data / events / .pb
Old: edit MAC / rediscoverNew beacon row and/or new history keyMultiple LEGACY-{mac} (or raw MAC pre-upgrade) series for one logical fridge/slot
Repair: Merge assets into thisOrdered source rows retire into one survivor; final source device becomes currentExclusive reading windows rewrite onto survivor UID; main history and .pb files merge

Upgrade alone does not reunite old lineages: each beacon becomes its own LEGACY-* asset with its own history.

Operators can now repair those old lineages from Admin → Assets → Merge assets into this. The preview reports reading windows and kept/dropped row counts before the irreversible apply.

TST000 note

Typed sensors on the dry-run snapshot have distinct names (Senimed_1–4, etc.)—no obvious same-name duplicates. Merge is still required for other customer DBs and any site that swapped hardware the old way (including fridge virtual-MAC lineages split across rows).

Shipped behavior

Operator: pick a survivor sensor + 1..20 source sensors, order the sources, and confirm the preview.

Server:

  1. Keep all survivor sensor rows; each ordered source contributes only rows newer than the previous segment's last reading. Delete overlap and rewrite kept rows to the survivor asset_uid.
  2. Apply the same inferred windows to beacon_events, tracking_logs, and button_press_audit.
  3. Merge interval .pb files.
  4. Move each source's open device assignment onto the survivor with reason = merge; leave the final device open/current.
  5. Deactivate source assets and replace their denormalized MACs with unique local retirement placeholders.

Runtime on apply:

  1. Flush pending tracking logs and button-press audit queues.
  2. Build the exclusive-window plan; merge interval .pb files.
  3. Apply main DB + sensor DB in one outer transaction.
  4. Rekey any mid-flight pending tracking/audit rows and sensor_last_store onto the survivor UID.
  5. Refresh beacon cache for the survivor and retired sources (inactive inventory + retirement MAC index).
  6. Idempotent DELETE of remaining source history keys (covers late MQTT inserts under retired keys).

Prefer a quiet period for the involved sensors. Wire-MAC panic/KISS maps (trigger_cache, panic_event_ids, gateway_cache) are intentionally untouched for sensor-only merge — the survivor inherits the live MAC.

APIs: POST /api/v1/beacon/:id/merge/plan, POST /api/v1/beacon/:id/merge, and GET /api/v1/beacon/:id/assignments.

Zabbix: after merge, expect source hosts to disappear from LLD; clean up stale discovered hosts manually until host identity is uid-stable.

Still out of scope: auto-detecting merges without operator confirmation; orphan history keys without source assets; rewriting Zabbix item history inside Zabbix DB.

Discovery helper

Read-only candidate scan: scripts/audit/merge_candidates.ts (duplicate sanitized names, orphan sensor_data keys).

bash
bun scripts/audit/merge_candidates.ts --db db/safecall.db --sensor-db db/sensor_data.db

Location codes and LEGACY UIDs

  • Real UIDs require coded location paths; otherwise assets stay LEGACY-{mac}.
  • TST000 dry-run: 9 locations, 0 codes → 253/8/2 beacon/gateway/e-ink all LEGACY; remint map empty.
  • Ops path (shipped): assign location codes, then Admin → Assets → settings → Remint LEGACY UIDs (GET/POST /api/v1/assets/remint/*) to batch-remint beacons with full history rewrite. Per-asset mint-on-edit remains available for single rows.

Smaller / deferred items

  • E-ink: optional normalize thin-wrap onto shared mac_normalize (wire poll stays colon MAC).
  • Docs: Zabbix concepts still describe MAC-centric discovery; update when host identity lands.
  • Comms: Phase 6c removed MAC-as-asset HTTP/WS refs—call out in release notes for external clients.
  • Grafana: no known gap for uid cutover beyond verifying dashboards after merge/remint.

Prioritized backlog

  1. Zabbix host identity on asset_uid — LLD {#NAME} / host prototype + schema/docs/CLI; verify on a real Zabbix, not TST000 alone.
  2. LLD schema/type drift — can ship with (1) or as a small fix ahead of it.
  3. Deferred polish — e-ink normalize wrap, install-doc pointers to dry-run scripts.
  • TST000 dry-run report: tmp/upgrade-dry-run-TST000/<stamp>/VALIDATION_REPORT.md (gitignored)
  • Upgrade helpers: scripts/upgrade/customer_db_verify.ts, customer_db_soak.ts
  • Leftover MAC audit: scripts/audit/leftover_mac_history.ts
  • Merge candidates: scripts/audit/merge_candidates.ts