diff --git a/docs/proposals/device-identity.md b/docs/proposals/device-identity.md new file mode 100644 index 0000000..c94b718 --- /dev/null +++ b/docs/proposals/device-identity.md @@ -0,0 +1,176 @@ +# Proposal: one identity for the device a PC drives, ending in the registry + +Status: PHASE 1 DONE (shipped after 0.12.0). Phases 2 and 3 not started. +Author: planning session 2026-08-20. + +## 1. What this is + +A shop-floor PC often drives something: a CMM, a Keyence, a Genspect, a +wax-trace unit, a part marker. ShopDB needs to know WHICH one, and the answer +has to survive the PC being replaced. + +Today that answer is assembled from whatever each bay family happens to have, +and two of the three sources are derived from the PC itself, so they do not +survive a swap. This proposal converges them on one identity, moves it from +files to the registry, and does it in three phases so no bay is stranded. + +## 2. Why PC-derived identity fails + +Two mechanisms stand in for a real identity today: + +- **Reuse**: look for a prior collector link from THIS PC asset. +- **Mint**: build `-`, and a PC's asset number is its + hostname. + +Both survive a re-image. Neither survives a PC SWAP. A new hostname is a new PC +asset with no prior link and a predicted number that has never existed, so the +same physical device gets a SECOND record while the first keeps its config and +its backup history under a dead PC's name. + +That is not hypothetical: minting from the hostname left 43 legacy `MT-####` +tools shadowed by minted twins, three records deep in places. + +## 3. The rail that must not be crossed + +`machinenumber` answers "WHICH BAY is this" and is what GE-Enforce +`TargetMachineNumbers` gates on. The device identity answers "WHICH DEVICE is +this". They are different facts and must stay in different fields. + +Putting a device id into the machine-number channel would silently stop every +bay-gated manifest entry from matching - a failure that looks like nothing +happening, on every cycle, with no error anywhere. + +## 4. Where identity comes from today + +| Bay family | Identity source | Survives a PC swap | +|---|---|---| +| DNC / collections | `MachineNo` in the eDNC/NTLARS registry | yes | +| CMM | `C:\Enrollment\cmm\cmmid.txt` | yes, but see below | +| Keyence, Genspect, wax-trace | nothing, before phase 1 | no | +| Part markers | nothing, before phase 1 | no | + +**`cmmid.txt` is not only an identity file.** It is a PATH KEY, and that is why +it cannot simply be retired: + +| Consumer | Use | +|---|---| +| `resolve-cmm-bay-config.ps1` | writes it | +| `Verify-And-Heal-Staging.ps1` | stages `installers-post\cmm\backups\` | +| `09-Setup-CMM.ps1` | locates the bay's staged backup set | +| `Restore-CMM.ps1` | restores from `\\`, same-bay by design | +| collector | reads it as `machineNo` | + +Only the last row is an identity use. The rest address directories by it. + +It is also doing the job badly: for a CMM bay it reports the INSTRUMENT's id in +the `machinenumber` channel, and the server then resolves it back to a measuring +tool at step 4 of its own fallback chain. That round trip is what phases 2 and 3 +remove. + +## Phase 1 - one field, one file (DONE) + +The collector accepts a single `deviceid`, read from `C:\Enrollment\asset-id.txt` +and resolved BEFORE anything PC-derived, for every device family. + +- One file for every bay with no `MachineNo` registry to identify it. +- It names the asset and NOT its type: the pc-type already says what kind of + device it is, through `SUBORDINATE_DEVICE_MAP`, so a device type declared + later through `subordinatedevice_` needs no new file and no client + change. +- Wire field is `deviceid`, not `assetid`, because `assetid` is already this + contract's RESPONSE field for a PC's integer primary key. +- `measuringtoolid` (0.12.0) is accepted as an alias and the older file is still + read, so a bay staged in that window keeps reporting. +- Guards: an unresolvable value warns rather than minting a phantom, and a value + resolving to the WRONG KIND of asset is refused with the asset named. + +Nothing was removed. Every previous fallback still runs, one place lower. + +## Phase 2 - imaging writes it, and `cmmid.txt` stops being an identity + +Repository: the PXE/imaging repo, not this one. **Needs deploying to the live +server, not only committing** - the live boot image is authoritative. + +1. `resolve-cmm-bay-config.ps1` writes `C:\Enrollment\asset-id.txt` ALONGSIDE + `cmmid.txt`. Two files, two questions: the identity, and the backup-set key. + `cmmid.txt` keeps every path use it has today. +2. Every other device pc-type writes `asset-id.txt` at imaging from its bay + config. Bays with no device write nothing, and the file's absence stays + meaningful. +3. The collector demotes `cmmid.txt` to last place in the machine-number chain + and stops being the intended path for CMM identity. It is still read, so a + grandfathered bay that never gets re-imaged keeps reporting. +4. The value written is the device's `assetnumber` as ShopDB holds it. Where a + CMM's asset number is already its bay id (`CMM3`), phase 2 is a no-op in + content and a change only in which file carries it. + +Exit criterion: a report from a re-imaged bay of each device family carries +`deviceid`, and no bay of those families relies on the `cmmid.txt` fallback. +The collector response warnings are the check: an adopted device is silent, a +minted one is not. + +## Phase 3 - identity moves to the registry + +`HKLM:\SOFTWARE\GE\ShopDB\DeviceId`. + +This is the destination because the registry survives the profile cleanup and +disk hygiene that eats `C:\Enrollment`, is readable as SYSTEM with no file ACL +to get wrong, and is already how a DNC bay is identified. It needs no new key +and no new read path: the collector already opens +`HKLM:\SOFTWARE\GE\ShopDB` (and the `WOW6432Node` variant) for `BaseUrl` and +`ApiToken`, and `Install-GEEnforce.ps1` already writes that key. + +Resolution order after phase 3: + +1. `HKLM:\SOFTWARE\GE\ShopDB\DeviceId` +2. `C:\Enrollment\asset-id.txt` +3. `C:\Enrollment\measuringtool-id.txt` (0.12.0 window) +4. `C:\Enrollment\cmm\cmmid.txt` as `machineNo` (grandfathered CMM bays) + +Then, and only then, the file branches can be deleted one at a time, newest +first, each when no bay has reported through it for a full inventory cycle. + +**`cmmid.txt` survives phase 3 regardless**, as the CMM backup-set path key. It +is not on the deletion list; only its identity use is. + +### Doing phase 3 early is cheaper than doing it late + +If the registry read lands before imaging is changed, phase 2 can write the +REGISTRY for new bays and skip the file era entirely for them. The file then +exists only for bays staged in the window between phase 1 and phase 2, and the +eventual cleanup is deleting a code branch rather than sweeping the fleet. + +The cost of adding the read now is one branch in the client and one row in the +docs. The cost of adding it after every bay has a file is a migration. + +## 5. What this does not change + +- `machinenumber` and its GE-Enforce gating. Untouched in all three phases. +- A part marker still files `partof` the operation from `machinenumber`. The + device identity says WHICH marker the PC controls, not which operation that + marker belongs to. +- Minting. It stays as the last resort for a bay that names nothing, because a + device nobody declared is still better recorded than not recorded. Anything + minted should be treated as a placeholder to be reconciled. + +## 6. Before phase 2: find what is already duplicated + +Phase 1 stops NEW duplicates. It does not merge the ones a PC swap has already +created. Before imaging starts writing files, run a read-only query for device +assets whose controlling PC no longer exists, or whose asset number carries a +hostname that is not a current PC. That list is the reconciliation backlog, and +it is much easier to read before the fleet starts reporting new identities over +the top of it. + +## 7. Rollback + +Each phase is independently reversible because nothing is removed until the +phase after it proves the replacement: + +- Phase 1: stop sending `deviceid` and every previous fallback still runs. +- Phase 2: stop writing `asset-id.txt` and bays fall back to what they used + before. +- Phase 3: stop writing the registry value and bays fall back to the file. + +The only irreversible step is deleting a fallback branch, which is deliberately +outside all three phases.